From 03d686a246827f653411dac461a318e01a0e8d63 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 27 Aug 2018 12:27:40 -0400 Subject: [PATCH 001/332] This is unused, remove it. --- indra/newview/app_settings/cmd_line.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 253b0ad6e..fbb2bb10f 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -211,12 +211,6 @@ NoHardwareProbe - noquicktime - - map-to - NoQuickTime - - nosound map-to From 8ecbf79199888de53b286c5f200bb3542dd4611a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 27 Aug 2018 12:27:58 -0400 Subject: [PATCH 002/332] Fix this because it shoulda been this from the start, oops. --- indra/newview/chatbar_as_cmdline.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index c03d8424c..7021ace43 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -586,10 +586,10 @@ LLUUID cmdline_partial_name2key(std::string partial_name) std::string av_name; LLStringUtil::toLower(partial_name); LLWorld::getInstance()->getAvatars(&avatars); - auto instance = (LLFloaterAvatarList::instanceExists() ? &LLFloaterAvatarList::instance() : nullptr); + auto radar = (LLFloaterAvatarList::instanceExists() ? LLFloaterAvatarList::getInstance() : nullptr); for(const auto& id : avatars) { - if (LLAvatarListEntry* entry = instance ? instance->getAvatarEntry(id) : nullptr) + if (LLAvatarListEntry* entry = radar ? radar->getAvatarEntry(id) : nullptr) av_name = entry->getName(); else if (gCacheName->getFullName(id, av_name)); else if (LLVOAvatar* avatarp = gObjectList.findAvatar(id)) From 13e2af899087db42e7b1e2fa69ea8590657d9e78 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 27 Aug 2018 12:33:02 -0400 Subject: [PATCH 003/332] Request: Add setting SinguMotionResetsCameraReset to let the user decide if SinguMotionResetsCamera will Reset without their explicit action. --- indra/newview/app_settings/settings_ascent.xml | 12 ++++++++++++ indra/newview/llviewermenu.cpp | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index dd373885d..712f038d8 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -873,6 +873,18 @@ Value 1 + SinguMotionResetsCameraReset + + Comment + Disable for the behavior Liru intended SinguMotionResetsCamera to have, wherein it doesn't reset every time you press escape, teleport, or whatever else! So you can walk around and your camera won't reset at all like it was meant to be. +RIP Latif Khalifa. + Persist + 1 + Type + Boolean + Value + 1 + SinguLastKnownReleaseBuild Comment diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 742d5f7d6..e23353a47 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -4088,7 +4088,8 @@ void reset_view_final( BOOL proceed ) } gAgentCamera.resetView(TRUE, TRUE); gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); - gSavedSettings.setBOOL("SinguMotionResetsCamera", true); + if (gSavedSettings.getBOOL("SinguMotionResetsCameraReset")) + gSavedSettings.setBOOL("SinguMotionResetsCamera", true); if(gAgentCamera.cameraCustomizeAvatar() && LLFloaterCustomize::instanceExists()) LLFloaterCustomize::getInstance()->close(); From 252fdcdd1eb4a42df4f38cec6de181905d967cba Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 27 Aug 2018 18:42:27 -0400 Subject: [PATCH 004/332] Collab with RouterGray to set Startup project automatically via CMake. Also thanks to Alchemy, we think. --- indra/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 59735a520..06e06f274 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -58,5 +58,8 @@ add_subdirectory(${LIBS_OPEN_PREFIX}plugins) add_subdirectory(${VIEWER_PREFIX}newview/statemachine) add_subdirectory(${VIEWER_PREFIX}newview) -add_dependencies(viewer singularity-bin) +add_dependencies(viewer ${VIEWER_BRANDING_ID}-bin) +if (WINDOWS) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${VIEWER_BRANDING_ID}-bin) +endif (WINDOWS) From 13816b696f6e995f8201b863c75a6ef89c5e566b Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 6 Jan 2019 09:46:14 -0500 Subject: [PATCH 005/332] Fix reporter spawning notifications over and over The user needs only one per session if that. Also no longer have reporter processRegionInfo when it doesn't use that. --- indra/newview/llfloaterreporter.cpp | 14 +++----------- indra/newview/llfloaterreporter.h | 4 ---- indra/newview/llviewerregion.cpp | 2 -- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 0b175dcfa..2ff488629 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -111,17 +111,6 @@ LLFloaterReporter::LLFloaterReporter() LLUICtrlFactory::getInstance()->buildFloater(this, "floater_report_abuse.xml"); } -// static -void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) -{ - U32 region_flags; - msg->getU32("RegionInfo", "RegionFlags", region_flags); - - if (LLFloaterReporter::instanceExists() && LLFloaterReporter::getInstance()->getVisible()) - { - LLNotificationsUtil::add("HelpReportAbuseEmailLL"); - } -} // virtual BOOL LLFloaterReporter::postBuild() { @@ -155,6 +144,7 @@ BOOL LLFloaterReporter::postBuild() mDefaultSummary = getChild("details_edit")->getValue().asString(); + /* Singu Note: We only used this to trigger the notification that's now below, there's no point to this anymore. // send a message and ask for information about this region - // result comes back in processRegionInfo(..) LLMessageSystem* msg = gMessageSystem; @@ -163,6 +153,8 @@ BOOL LLFloaterReporter::postBuild() msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); gAgent.sendReliableMessage(); + */ + LLNotificationsUtil::add("HelpReportAbuseEmailLL"); // abuser name is selected from a list diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index 5e9d9ebf8..f48594473 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -38,7 +38,6 @@ #include "v3math.h" class LLAvatarName; -class LLMessageSystem; class LLViewerTexture; class LLInventoryItem; class LLViewerObject; @@ -104,9 +103,6 @@ public: static void addDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); static void setDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); - // static - static void processRegionInfo(LLMessageSystem* msg); - void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id); private: diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 44dd85350..d1208658a 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -63,7 +63,6 @@ #include "llfloateravatarlist.h" #include "llfloatergodtools.h" #include "llfloaterperms.h" -#include "llfloaterreporter.h" #include "llfloaterregioninfo.h" #include "llhttpnode.h" #include "llregioninfomodel.h" @@ -904,7 +903,6 @@ void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**) LLRegionInfoModel::instance().update(msg); LLFloaterGodTools::processRegionInfo(msg); LLFloaterRegionInfo::processRegionInfo(msg); - LLFloaterReporter::processRegionInfo(msg); } void LLViewerRegion::setCacheID(const LLUUID& id) From 0e21b53c2ff0c569e4986eafec3179c659e22f8c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 6 Jan 2019 14:20:13 -0500 Subject: [PATCH 006/332] Sync installer with Alchemy for NSIS 3 support Add a checkbox for creating desktop shortcut, restoring this functionality. Fix install folder defaulting to 64-bit for 32-bit. NOTICE: We now only support NSIS 3, no we don't care about your set up, see our build-windows page for updated details on this. --- .../installers/windows/installer_template.nsi | 64 +++++++++++-------- indra/newview/viewer_manifest.py | 6 +- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index ed160f8a1..f2730839b 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -24,6 +24,10 @@ ;; ;; Author: James Cook, Don Kjer, Callum Prentice, Drake Arconis ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;-------------------------------- +;Unicode + Unicode true + ;-------------------------------- ;Include Modern UI @@ -72,7 +76,7 @@ InstallDirRegKey HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" "" !endif - ;Request application privileges for Windows Vista + ;Request application privileges for Windows UAC RequestExecutionLevel admin ;Compression @@ -139,7 +143,11 @@ !define MUI_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip +!ifdef WIN64_BIN_BUILD !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${APPNAME} (64 bit) Viewer" +!else + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${APPNAME} Viewer" +!endif !insertmacro MUI_PAGE_STARTMENU Application $STARTMENUFOLDER ;Install Progress Page @@ -150,6 +158,10 @@ !define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip_finish !define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN_FUNCTION launch_viewer + !define MUI_FINISHPAGE_SHOWREADME + !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" + !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED + !define MUI_FINISHPAGE_SHOWREADME_FUNCTION create_desktop_shortcut !define MUI_FINISHPAGE_NOREBOOTSUPPORT !insertmacro MUI_PAGE_FINISH @@ -185,12 +197,12 @@ ;because this will make your installer start faster. !insertmacro MUI_RESERVEFILE_LANGDLL - ReserveFile "${NSISDIR}\Plugins\NSISdl.dll" - ReserveFile "${NSISDIR}\Plugins\nsDialogs.dll" - ReserveFile "${NSISDIR}\Plugins\StartMenu.dll" - ReserveFile "${NSISDIR}\Plugins\StdUtils.dll" - ReserveFile "${NSISDIR}\Plugins\System.dll" - ReserveFile "${NSISDIR}\Plugins\UserInfo.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\NSISdl.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsDialogs.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\StartMenu.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\StdUtils.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\System.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\UserInfo.dll" ;-------------------------------- ; Local Functions @@ -212,6 +224,14 @@ Function launch_viewer ${StdUtils.ExecShellAsUser} $0 "$INSTDIR\$INSTEXE" "open" "$SHORTCUT_LANG_PARAM" FunctionEnd +Function create_desktop_shortcut +!ifdef WIN64_BIN_BUILD + CreateShortCut "$DESKTOP\$INSTSHORTCUT x64.lnk" "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$INSTEXE" +!else + CreateShortCut "$DESKTOP\$INSTSHORTCUT.lnk" "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$INSTEXE" +!endif +FunctionEnd + ;Check version compatibility Function CheckWindowsVersion !ifdef WIN64_BIN_BUILD @@ -221,7 +241,7 @@ Function CheckWindowsVersion ${EndIf} !endif - ${If} ${AtMostWinXP} + ${If} ${AtMostWinVista} MessageBox MB_OK $(CheckWindowsVersionMB) Quit ${EndIf} @@ -229,20 +249,6 @@ FunctionEnd ;Check service pack compatibility and suggest upgrade Function CheckWindowsServPack - ${If} ${IsWinVista} - ${AndIfNot} ${IsServicePack} 2 - MessageBox MB_OK $(CheckWindowsServPackMB) - DetailPrint $(UseLatestServPackDP) - Return - ${EndIf} - - ${If} ${IsWin2008} - ${AndIfNot} ${IsServicePack} 2 - MessageBox MB_OK $(CheckWindowsServPackMB) - DetailPrint $(UseLatestServPackDP) - Return - ${EndIf} - ${If} ${IsWin7} ${AndIfNot} ${IsServicePack} 1 MessageBox MB_OK $(CheckWindowsServPackMB) @@ -320,7 +326,9 @@ Function CloseSecondLife IntCmp $0 0 DONE StrCmp $SKIP_DIALOGS "true" CLOSE - MessageBox MB_YESNOCANCEL $(CloseSecondLifeInstMB) IDYES CLOSE IDNO DONE + MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL + + CANCEL_INSTALL: Quit CLOSE: @@ -346,7 +354,9 @@ Function un.CloseSecondLife Push $0 FindWindow $0 "Second Life" "" IntCmp $0 0 DONE - MessageBox MB_YESNOCANCEL $(CloseSecondLifeUnInstMB) IDYES CLOSE IDNO DONE + MessageBox MB_OKCANCEL $(CloseSecondLifeUnInstMB) IDOK CLOSE IDCANCEL CANCEL_UNINSTALL + + CANCEL_UNINSTALL: Quit CLOSE: @@ -491,14 +501,16 @@ Section "Viewer" ;Write URL registry info - WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "(Default)" "URL:Second Life" + DeleteRegKey HKEY_CLASSES_ROOT "${URLNAME}" + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "" "URL:Second Life" WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" "$INSTDIR\$INSTEXE" ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" - WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "(Default)" "URL:Second Life" + DeleteRegKey HKEY_CLASSES_ROOT "x-grid-location-info" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "" "URL:Hypergrid legacy" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\"" ;; URL param must be last item passed to viewer, it ignores subsequent params diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 576a24613..4fe74702e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -585,14 +585,14 @@ class WindowsManifest(ViewerManifest): # http://www.scratchpaper.com/ try: import _winreg as reg - NSIS_path = reg.QueryValue(reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\NSIS\Unicode") + '\\makensis.exe' + NSIS_path = reg.QueryValue(reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\NSIS") + '\\makensis.exe' self.run_command([proper_windows_path(NSIS_path), self.dst_path_of(tempfile)]) except: try: - NSIS_path = os.environ['ProgramFiles'] + '\\NSIS\\Unicode\\makensis.exe' + NSIS_path = os.environ['ProgramFiles'] + '\\NSIS\\makensis.exe' self.run_command([proper_windows_path(NSIS_path), self.dst_path_of(tempfile)]) except: - NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe' + NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\makensis.exe' self.run_command([proper_windows_path(NSIS_path),self.dst_path_of(tempfile)]) From adba4cf1fa4939481e94350d8c0f7d2cdf97d13b Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 7 Jan 2019 17:39:54 -0500 Subject: [PATCH 007/332] Beware the coral, it adds the reef factor --- indra/newview/llimpanel.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 4388c46d8..c2eb49492 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1016,26 +1016,29 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) return; } - int option = value.asInteger(); - if (option == 1) onClickHistory(); - else if (option == 2) LLAvatarActions::offerTeleport(mOtherParticipantUUID); - else if (option == 3) LLAvatarActions::teleportRequest(mOtherParticipantUUID); - else if (option == 4) LLAvatarActions::pay(mOtherParticipantUUID); - else if (option == 5) LLAvatarActions::inviteToGroup(mOtherParticipantUUID); - else if (option == -1) copy_profile_uri(mOtherParticipantUUID); - else if (option == -2) LLAvatarActions::showOnMap(mOtherParticipantUUID); - else if (option == -3) gAgentCamera.lookAtObject(mOtherParticipantUUID); - else if (option >= 6) // Options that use dynamic items + switch (int option = value.asInteger()) + { + case 1: onClickHistory(); break; + case 2: LLAvatarActions::offerTeleport(mOtherParticipantUUID); break; + case 3: LLAvatarActions::teleportRequest(mOtherParticipantUUID); break; + case 4: LLAvatarActions::pay(mOtherParticipantUUID); break; + case 5: LLAvatarActions::inviteToGroup(mOtherParticipantUUID); break; + case -1: copy_profile_uri(mOtherParticipantUUID); break; + case -2: LLAvatarActions::showOnMap(mOtherParticipantUUID); break; + case -3: gAgentCamera.lookAtObject(mOtherParticipantUUID); break; + default: // Options >= 6 use dynamic items { - // First remove them all removeDynamics(flyout); // Toggle as requested, adjust the strings - if (option == 6) mDing = !mDing; - else if (option == 7) mRPMode = !mRPMode; - else if (option == 8) LLAvatarActions::isFriend(mOtherParticipantUUID) ? LLAvatarActions::removeFriendDialog(mOtherParticipantUUID) : LLAvatarActions::requestFriendshipDialog(mOtherParticipantUUID); - else if (option == 9) LLAvatarActions::toggleBlock(mOtherParticipantUUID); + switch (option) + { + case 6: mDing = !mDing; break; + case 7: mRPMode = !mRPMode; break; + case 8: LLAvatarActions::isFriend(mOtherParticipantUUID) ? LLAvatarActions::removeFriendDialog(mOtherParticipantUUID) : LLAvatarActions::requestFriendshipDialog(mOtherParticipantUUID); break; + case 9: LLAvatarActions::toggleBlock(mOtherParticipantUUID); break; + } // Last add them back addDynamics(flyout); @@ -1044,6 +1047,7 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) if (flyout->remove(focus)) // If present, reorder to bottom. flyout->add(focus, -3); } + } } void show_log_browser(const std::string& name, const std::string& id) From 022fba2ba48d72ec21fcb374488a25f456c04b9b Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 8 Jan 2019 11:27:51 -0500 Subject: [PATCH 008/332] Add option "Add to Conference" to Conference chats and Instant Messages Sync code from upstream for inviting people to conference via Avatar Picker --- indra/newview/llimpanel.cpp | 100 +++++++++++++++++- indra/newview/llimpanel.h | 5 + .../xui/de/floater_instant_message_ad_hoc.xml | 2 +- ..._instant_message_ad_hoc_concisebuttons.xml | 2 +- .../skins/default/xui/de/notifications.xml | 4 + .../xui/en-us/floater_instant_message.xml | 1 + .../en-us/floater_instant_message_ad_hoc.xml | 4 +- ..._instant_message_ad_hoc_concisebuttons.xml | 4 +- ...floater_instant_message_concisebuttons.xml | 1 + .../skins/default/xui/en-us/notifications.xml | 14 +++ .../xui/es/floater_instant_message_ad_hoc.xml | 2 +- ..._instant_message_ad_hoc_concisebuttons.xml | 2 +- .../skins/default/xui/es/notifications.xml | 5 +- .../skins/default/xui/fr/notifications.xml | 5 + .../skins/default/xui/it/notifications.xml | 4 + .../skins/default/xui/pt/notifications.xml | 4 + 16 files changed, 150 insertions(+), 9 deletions(-) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index c2eb49492..9eed719b5 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -42,6 +42,7 @@ #include "llavatarnamecache.h" #include "llbutton.h" #include "llcombobox.h" +#include "llfloateravatarpicker.h" #include "llfloaterchat.h" #include "llfloaterinventory.h" #include "llfloaterwebcontent.h" // For web browser display of logs @@ -980,6 +981,96 @@ bool LLFloaterIMPanel::isInviteAllowed() const return mSessionType == ADHOC_SESSION; } +void LLFloaterIMPanel::onAddButtonClicked() +{ + LLView * button = findChild("instant_message_flyout"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMPanel::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); + if (!picker) + { + return; + } + + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLFloaterIMPanel::canAddSelectedToChat, this, _1)); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +bool LLFloaterIMPanel::canAddSelectedToChat(const uuid_vec_t& uuids) const +{ + switch (mSessionType) + { + case P2P_SESSION: return true; // Don't bother blocking self or peer + case ADHOC_SESSION: + { + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = getSpeakerManager(); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); + } + + for (const auto& id : uuids) + for (const LLPointer& speaker : speaker_list) + if (id == speaker->mID) + return false; + } + return true; + default: return false; + } +} + +void LLFloaterIMPanel::addSessionParticipants(const uuid_vec_t& uuids) +{ + if (mSessionType == P2P_SESSION) + { + LLSD payload; + LLSD args; + + LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, + boost::bind(&LLFloaterIMPanel::addP2PSessionParticipants, this, _1, _2, uuids)); + } + else inviteToSession(uuids); +} + +void LLFloaterIMPanel::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + return; + } + + LLVoiceChannel* voice_channel = LLActiveSpeakerMgr::getInstance()->getVoiceChannel(); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != nullptr && voice_channel->getSessionID() == mSessionUUID && voice_channel->isActive(); + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids); + } + else + { + LLAvatarActions::startConference(temp_ids); + } +} + void LLFloaterIMPanel::removeDynamics(LLComboBox* flyout) { flyout->remove(mDing ? getString("ding on") : getString("ding off")); @@ -1012,8 +1103,12 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) { if (value.isUndefined() || value.asInteger() == 0) { - LLAvatarActions::showProfile(mOtherParticipantUUID); - return; + switch (mSessionType) + { + case GROUP_SESSION: LLGroupActions::show(mOtherParticipantUUID); return; + case P2P_SESSION: LLAvatarActions::showProfile(mOtherParticipantUUID); return; + default: onClickHistory(); return; // If there's no profile for this type, we should be the history button. + } } switch (int option = value.asInteger()) @@ -1026,6 +1121,7 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) case -1: copy_profile_uri(mOtherParticipantUUID); break; case -2: LLAvatarActions::showOnMap(mOtherParticipantUUID); break; case -3: gAgentCamera.lookAtObject(mOtherParticipantUUID); break; + case -4: onAddButtonClicked(); break; default: // Options >= 6 use dynamic items { // First remove them all diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index e3789b4ab..6a2e54dbe 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -165,6 +165,11 @@ private: // test if local agent can add agents. bool isInviteAllowed() const; + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids) const; + // Called whenever the user starts or stops typing. // Sends the typing state to the other user if necessary. void setTyping(bool typing); diff --git a/indra/newview/skins/default/xui/de/floater_instant_message_ad_hoc.xml b/indra/newview/skins/default/xui/de/floater_instant_message_ad_hoc.xml index da45b31e9..26728aafe 100644 --- a/indra/newview/skins/default/xui/de/floater_instant_message_ad_hoc.xml +++ b/indra/newview/skins/default/xui/de/floater_instant_message_ad_hoc.xml @@ -10,7 +10,7 @@ Für Instant Message hier klicken. - diff --git a/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc_concisebuttons.xml b/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc_concisebuttons.xml index 65196f003..2d0a69f5c 100644 --- a/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc_concisebuttons.xml +++ b/indra/newview/skins/default/xui/en-us/floater_instant_message_ad_hoc_concisebuttons.xml @@ -8,7 +8,9 @@ [NAME] is typing... Starting session with [NAME], please wait. Click here to instant message. - diff --git a/indra/newview/skins/default/xui/en-us/floater_instant_message_concisebuttons.xml b/indra/newview/skins/default/xui/en-us/floater_instant_message_concisebuttons.xml index b8c3acbb7..6bfe25215 100644 --- a/indra/newview/skins/default/xui/en-us/floater_instant_message_concisebuttons.xml +++ b/indra/newview/skins/default/xui/en-us/floater_instant_message_concisebuttons.xml @@ -19,6 +19,7 @@ + diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 28d0a6b0b..b13e4c8eb 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -5362,6 +5362,20 @@ Visit the [SECOND_LIFE] Support Web site? type="alertmodal"> This grid has no link for support. + + + +When you add a person to an existing conversation, a new conversation will be created. All participants will receive new conversation notifications. + confirm + + - + Date: Sat, 19 Jan 2019 10:47:11 -0500 Subject: [PATCH 045/332] Fix nearby media panel fading after time on Nearby Media Floater How did no one notice I broke this in syncing over two years ago? --- indra/newview/llpanelnearbymedia.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index 55d07a021..9503de65f 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -93,6 +93,7 @@ LLPanelNearByMedia::LLPanelNearByMedia(bool standalone_panel) , mDebugInfoVisible(false) , mParcelMediaItem(nullptr) , mParcelAudioItem(nullptr) + , mStandalonePanel(standalone_panel) { mHoverTimer.stop(); From f6beee36936db7edcb245262bd76cfde696b87f6 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 04:35:21 -0500 Subject: [PATCH 046/332] Don't look for clicking urls during draw, do that later upon click --- indra/llui/lltexteditor.cpp | 20 +++++++++----------- indra/llui/lltexteditor.h | 1 - 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 3c085e536..ec2633a81 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -345,7 +345,6 @@ LLTextEditor::LLTextEditor( resetDirty(); // Update saved text state - mHTML.clear(); // make the popup menu available //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", parent_view); LLMenuGL* menu = new LLMenuGL("rclickmenu"); @@ -3599,13 +3598,6 @@ void LLTextEditor::drawText() style->mUnderline = TRUE; } - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - - if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) ) - { - mHTML = style->getLinkHREF(); - } - drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x ); if( text_x == text_start && mShowLineNumbers ) @@ -4805,10 +4797,16 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) // This mouse up was part of a click. // Regardless of where the cursor is, see if we recently touched a link // and launch it if we did. - if (mParseHTML && mHTML.length() > 0) + if (mParseHTML) { - LLUrlAction::clickAction(mHTML); - mHTML.clear(); + if (auto segment = getSegmentAtLocalPos(x, y)) + { + if (auto style = segment->getStyle()) + { + if (style->isLink()) + LLUrlAction::clickAction(style->getLinkHREF()); + } + } } } diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 66ed4ab39..a0755f067 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -456,7 +456,6 @@ protected: BOOL mParseHTML; BOOL mParseHighlights; - std::string mHTML; typedef std::vector segment_list_t; segment_list_t mSegments; From 3a159111f3bb945025baedaaa7020ee038820451 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 04:35:51 -0500 Subject: [PATCH 047/332] Don't duplicate code, that's silly. --- indra/llui/lltexteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index ec2633a81..9db58989c 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -2210,7 +2210,7 @@ void LLTextEditor::copy(bool raw) // If our selection starts in the middle of a link, set our left_pos to the beginning of its segment. auto segment = **begin; if (auto style = segment.getStyle()) - if (!style->getLinkHREF().empty()) + if (style->isLink()) left_pos = llmin(segment.getStart(), left_pos); } auto text = mWText.substr(left_pos, length); From 4b4c8c8e37239302d12d72a7aef94ed272d7ef3d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 04:37:53 -0500 Subject: [PATCH 048/332] Woops, each link needs its own style! Thanks for testing, Torric! --- indra/llui/lltexteditor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 9db58989c..7f2327294 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4183,15 +4183,15 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s LL_RECORD_BLOCK_TIME(FTM_PARSE_HTML); S32 start=0,end=0; LLUrlMatch match; - LLStyleSP link_style(new LLStyle); - if (style) *link_style = *style; - link_style->setColor(mLinkColor ? *mLinkColor : LLUI::sConfigGroup->getColor4("HTMLLinkColor")); + const auto& link_color = mLinkColor ? *mLinkColor : LLUI::sConfigGroup->getColor4("HTMLLinkColor"); auto append_substr = [&](const size_t& pos, const size_t& count) { appendAndHighlightText(text.substr(pos, count), part, style); }; auto append_link = [&](const std::string& link) { + LLStyleSP link_style(style ? new LLStyle(*style) : new LLStyle); + link_style->setColor(link_color); link_style->setLinkHREF(match.getUrl()); appendAndHighlightText(link, part, link_style); }; From 7112e163e6e4c7a9ed9bf71f7b54a2486014f361 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 09:13:05 -0500 Subject: [PATCH 049/332] Update our url stuffs! Adds support for JIRA link labels from Alchemy. (Made less of a mess by me) Adds support for x-grid-info, the future of x-grid-info-location from Alchemy. Updates uriparser latest from Alchemy. Updates llstring to be more in line with upstream Alchemy. Fixes our LLURI Implementation Updates LLURLAction to modern C++ stuffies~ Adds Email protocol support from alchemy Sync LLSLURL with Alchemy, adding x-grid-info support. Also keep NoProtocol Support because yaaassss~ (also we won't suffer from MAINT-5019 because we're not dumb.) --- autobuild.xml | 10 +- indra/cmake/CMakeLists.txt | 2 + indra/cmake/FindURIPARSER.cmake | 46 ++ indra/cmake/URIPARSER.cmake | 20 + indra/llcommon/CMakeLists.txt | 4 + indra/llcommon/llstring.cpp | 77 +++- indra/llcommon/llstring.h | 125 ++++-- indra/llcommon/lluri.cpp | 61 ++- indra/llcommon/lluri.h | 95 +++-- indra/llcommon/lluriparser.cpp | 266 ++++++++++++ indra/llcommon/lluriparser.h | 87 ++++ indra/llui/lltexteditor.cpp | 19 +- indra/llui/llurlaction.cpp | 96 ++++- indra/llui/llurlaction.h | 22 +- indra/llui/llurlentry.cpp | 392 ++++++++++++++++-- indra/llui/llurlentry.h | 200 ++++++--- indra/llui/llurlmatch.cpp | 13 +- indra/llui/llurlmatch.h | 16 +- indra/llui/llurlregistry.cpp | 116 +++++- indra/llui/llurlregistry.h | 11 +- indra/llwindow/llwindow.cpp | 4 +- indra/newview/CMakeLists.txt | 2 + indra/newview/chatbar_as_cmdline.cpp | 2 +- .../installers/windows/installer_template.nsi | 26 ++ indra/newview/llfloaterobjectiminfo.cpp | 2 +- indra/newview/llslurl.cpp | 31 +- indra/newview/llslurl.h | 1 + indra/newview/llurldispatcher.cpp | 30 +- indra/newview/llurldispatcher.h | 41 +- .../newview/skins/default/xui/de/strings.xml | 3 + .../skins/default/xui/en-us/strings.xml | 1 + .../newview/skins/default/xui/es/strings.xml | 3 + .../newview/skins/default/xui/pt/strings.xml | 6 + 33 files changed, 1529 insertions(+), 301 deletions(-) create mode 100644 indra/cmake/FindURIPARSER.cmake create mode 100644 indra/cmake/URIPARSER.cmake create mode 100644 indra/llcommon/lluriparser.cpp create mode 100644 indra/llcommon/lluriparser.h diff --git a/autobuild.xml b/autobuild.xml index 274dde845..3108aac0d 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2476,7 +2476,7 @@ hash_algorithm md5 url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/uriparser-0.8.4-darwin-201511221948.tar.bz2 + https://depot.alchemyviewer.org/pub/darwin/lib/uriparser-0.8.4-darwin-201511221948.tar.bz2 name darwin @@ -2498,11 +2498,11 @@ archive hash - 69224d9285e5a26fc7a9aca5d3da7543 + cf21d9f7ed7949d2230413e8a391fb73 hash_algorithm md5 url - http://depot.alchemyviewer.org/pub/linux64/lib-trusty/uriparser-0.8.4-linux64-201603240044.tar.bz2 + https://depot.alchemyviewer.org/pub/linux64/lib-xenial/uriparser-0.8.4-linux64-201609241428.tar.bz2 name linux64 @@ -2512,11 +2512,11 @@ archive hash - f9f82fbd29751a969e9ab55a1f33ac7b + 89f3fc7f3fe396439d306f099d1a12d3 hash_algorithm md5 url - http://depot.alchemyviewer.org/pub/windows/lib-vc14/uriparser-0.8.4-windows-201601151009.tar.bz2 + https://depot.alchemyviewer.org/pub/windows/lib-vc141/uriparser-0.8.4-windows-201703090606.tar.bz2 name windows diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 7c8e616d6..7531bd335 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -40,6 +40,7 @@ set(cmake_SOURCE_FILES FindNDOF.cmake FindOpenJPEG.cmake FindTut.cmake + FindURIPARSER.cmake FindXmlRpcEpi.cmake FreeType.cmake GLOD.cmake @@ -93,6 +94,7 @@ set(cmake_SOURCE_FILES Tut.cmake UI.cmake UnixInstall.cmake + URIPARSER.cmake Variables.cmake ViewerMiscLibs.cmake WinManifest.cmake diff --git a/indra/cmake/FindURIPARSER.cmake b/indra/cmake/FindURIPARSER.cmake new file mode 100644 index 000000000..05f2d1737 --- /dev/null +++ b/indra/cmake/FindURIPARSER.cmake @@ -0,0 +1,46 @@ +# -*- cmake -*- + +# - Find uriparser +# Find the URIPARSER includes and library +# This module defines +# URIPARSER_INCLUDE_DIRS, where to find uriparser.h, etc. +# URIPARSER_LIBRARY, the libraries needed to use uriparser. +# URIPARSER_FOUND, If false, do not try to use uriparser. +# +# This FindURIPARSER is about 43 times as fast the one provided with cmake (2.8.x), +# because it doesn't look up the version of uriparser, resulting in a dramatic +# speed up for configure (from 4 minutes 22 seconds to 6 seconds). +# +# Note: Since this file is only used for standalone, the windows +# specific parts were left out. + +FIND_PATH(URIPARSER_INCLUDE_DIR uriparser/Uri.h + NO_SYSTEM_ENVIRONMENT_PATH + ) + +FIND_LIBRARY(URIPARSER_LIBRARY uriparser) + +if (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + SET(URIPARSER_INCLUDE_DIRS ${URIPARSER_INCLUDE_DIR}) + SET(URIPARSER_LIBRARY ${URIPARSER_LIBRARY}) + SET(URIPARSER_FOUND "YES") +else (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + SET(URIPARSER_FOUND "NO") +endif (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + +if (URIPARSER_FOUND) + if (NOT URIPARSER_FIND_QUIETLY) + message(STATUS "Found URIPARSER: ${URIPARSER_LIBRARY}") + SET(URIPARSER_FIND_QUIETLY TRUE) + endif (NOT URIPARSER_FIND_QUIETLY) +else (URIPARSER_FOUND) + if (URIPARSER_FIND_REQUIRED) + message(FATAL_ERROR "Could not find URIPARSER library") + endif (URIPARSER_FIND_REQUIRED) +endif (URIPARSER_FOUND) + +mark_as_advanced( + URIPARSER_LIBRARY + URIPARSER_INCLUDE_DIR + ) + diff --git a/indra/cmake/URIPARSER.cmake b/indra/cmake/URIPARSER.cmake new file mode 100644 index 000000000..47c3ccd37 --- /dev/null +++ b/indra/cmake/URIPARSER.cmake @@ -0,0 +1,20 @@ +# -*- cmake -*- + +set(URIPARSER_FIND_QUIETLY ON) +set(URIPARSER_FIND_REQUIRED ON) + +include(Prebuilt) + +if (USESYSTEMLIBS) + include(FindURIPARSER) +else (USESYSTEMLIBS) + use_prebuilt_binary(uriparser) + if (WINDOWS) + set(URIPARSER_LIBRARY + debug uriparserd + optimized uriparser) + elseif (DARWIN OR LINUX) + set(URIPARSER_LIBRARY uriparser) + endif (WINDOWS) + set(URIPARSER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/uriparser) +endif (USESYSTEMLIBS) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 80eeab0d4..8d0e54aa7 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -13,6 +13,7 @@ include(LLSharedLibs) include(GoogleBreakpad) include(Copy3rdPartyLibs) include(ZLIB) +include(URIPARSER) include_directories( ${EXPAT_INCLUDE_DIRS} @@ -101,6 +102,7 @@ set(llcommon_SOURCE_FILES llthreadsafequeue.cpp lltimer.cpp lluri.cpp + lluriparser.cpp lluuid.cpp llworkerthread.cpp metaclass.cpp @@ -238,6 +240,7 @@ set(llcommon_HEADER_FILES llunittype.h lltypeinfolookup.h lluri.h + lluriparser.h lluuid.h llwin32headers.h llwin32headerslean.h @@ -296,6 +299,7 @@ target_link_libraries( ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CORESERVICES_LIBRARY} + ${URIPARSER_LIBRARY} ) if (DARWIN) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 3562c044f..30b4266b7 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llstring.cpp * @brief String utility functions and the std::string class. @@ -28,12 +30,17 @@ #include "llstring.h" #include "llerror.h" +#include "llfasttimer.h" +#include "llsd.h" #if LL_WINDOWS #include "llwin32headerslean.h" #include // for WideCharToMultiByte #endif +LLTrace::BlockTimerStatHandle FT_STRING_FORMAT("String Format"); + + std::string ll_safe_string(const char* in) { if(in) return std::string(in); @@ -47,6 +54,23 @@ std::string ll_safe_string(const char* in, S32 maxlen) return std::string(); } +bool is_char_hex(char hex) +{ + if((hex >= '0') && (hex <= '9')) + { + return true; + } + else if((hex >= 'a') && (hex <='f')) + { + return true; + } + else if((hex >= 'A') && (hex <='F')) + { + return true; + } + return false; // uh - oh, not hex any more... +} + U8 hex_as_nybble(char hex) { if((hex >= '0') && (hex <= '9')) @@ -85,7 +109,7 @@ bool iswindividual(llwchar elem) bool _read_file_into_string(std::string& str, const std::string& filename) { - llifstream ifs(filename, llifstream::binary); + llifstream ifs(filename.c_str(), llifstream::binary); if (!ifs.is_open()) { LL_INFOS() << "Unable to open file " << filename << LL_ENDL; @@ -552,6 +576,33 @@ std::string utf8str_truncate(const std::string& utf8str, const S32 max_len) } } +std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len) +{ + if (0 == symbol_len) + { + return std::string(); + } + if ((S32)utf8str.length() <= symbol_len) + { + return utf8str; + } + else + { + int len = 0, byteIndex = 0; + const char* aStr = utf8str.c_str(); + size_t origSize = utf8str.size(); + + for (byteIndex = 0; len < symbol_len && byteIndex < origSize; byteIndex++) + { + if ((aStr[byteIndex] & 0xc0) != 0x80) + { + len += 1; + } + } + return utf8str.substr(0, byteIndex); + } +} + std::string utf8str_substChar( const std::string& utf8str, const llwchar target_char, @@ -628,10 +679,10 @@ std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page) 0, in, len_in, - NULL, + nullptr, 0, - 0, - 0); + nullptr, + nullptr); // We will need two more bytes for the double NULL ending // created in WideCharToMultiByte(). char* pout = new char [len_out + 2]; @@ -645,8 +696,8 @@ std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page) len_in, pout, len_out, - 0, - 0); + nullptr, + nullptr); out.assign(pout); delete[] pout; } @@ -667,8 +718,8 @@ wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page // reserve place to NULL terminator int output_str_len = in.length(); wchar_t* w_out = new wchar_t[output_str_len + 1]; - memset(w_out, 0, output_str_len + 1); + int real_output_str_len = MultiByteToWideChar (code_page, 0, in.c_str(), in.length(), w_out, output_str_len); //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858. @@ -689,7 +740,7 @@ std::string ll_convert_string_to_utf8_string(const std::string& in) long LLStringOps::sPacificTimeOffset = 0; long LLStringOps::sLocalTimeOffset = 0; -bool LLStringOps::sPacificDaylightTime = 0; +bool LLStringOps::sPacificDaylightTime = false; std::map LLStringOps::datetimeToCodes; std::vector LLStringOps::sWeekDayList; @@ -708,7 +759,7 @@ S32 LLStringOps::collate(const llwchar* a, const llwchar* b) #if LL_WINDOWS // in Windows, wide string functions operator on 16-bit strings, // not the proper 32 bit wide string - return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str()); + return strcoll(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str()); #else return wcscoll(a, b); #endif @@ -719,7 +770,7 @@ void LLStringOps::setupDatetimeInfo (bool daylight) time_t nowT, localT, gmtT; struct tm * tmpT; - nowT = time (NULL); + nowT = time (nullptr); tmpT = gmtime (&nowT); gmtT = mktime (tmpT); @@ -799,7 +850,7 @@ void LLStringOps::setupDayFormat(const std::string& data) } -std::string LLStringOps::getDatetimeCode (std::string key) +std::string LLStringOps::getDatetimeCode(const std::string& key) { std::map::iterator iter; @@ -1167,6 +1218,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, template<> S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) { + LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT); S32 res = 0; std::string output; @@ -1239,6 +1291,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) template<> S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) { + LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT); S32 res = 0; if (!substitutions.isMap()) @@ -1367,7 +1420,7 @@ void LLStringUtilBase::testHarness() s2.erase( 4, 1 ); llassert( s2 == "hell"); - s2.insert( 0, 1, 'y' ); + s2.insert( s2.begin(), 'y' ); llassert( s2 == "yhell"); s2.erase( 1, 3 ); llassert( s2 == "yl"); diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 41c38fe13..959833dd9 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -29,8 +29,11 @@ #include #include -#include +//#include #include +#include +#include +#include #include "llsd.h" #include "llfasttimer.h" @@ -49,6 +52,7 @@ #endif const char LL_UNKNOWN_CHAR = '?'; +class LLSD; #if LL_DARWIN || LL_LINUX || LL_SOLARIS // Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already) @@ -209,7 +213,7 @@ public: // currently in daylight savings time? static bool getPacificDaylightTime(void) { return sPacificDaylightTime;} - static std::string getDatetimeCode (std::string key); + static std::string getDatetimeCode (const std::string& key); }; /** @@ -298,7 +302,7 @@ public: static bool isValidIndex(const string_type& string, size_type i) { - return !string.empty() && (0 <= i) && (i <= string.size()); + return !string.empty() && (i <= string.size()); } static bool contains(const string_type& string, T c, size_type i=0) @@ -308,6 +312,7 @@ public: static void trimHead(string_type& string); static void trimTail(string_type& string); + static void trimTail(string_type& string, const string_type& tokens); static void trim(string_type& string) { trimHead(string); trimTail(string); } static void truncate(string_type& string, size_type count); @@ -337,6 +342,7 @@ public: static void addCRLF(string_type& string); static void removeCRLF(string_type& string); + static void removeWindowsCR(string_type& string); static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab ); static void replaceNonstandardASCII( string_type& string, T replacement ); @@ -445,7 +451,7 @@ public: struct LLDictionaryLess { public: - bool operator()(const std::string& a, const std::string& b) + bool operator()(const std::string& a, const std::string& b) const { return (LLStringUtil::precedesDict(a, b) ? true : false); } @@ -475,6 +481,7 @@ inline std::string chop_tail_copy( * @brief This translates a nybble stored as a hex value from 0-f back * to a nybble in the low order bits of the return byte. */ +LL_COMMON_API bool is_char_hex(char hex); LL_COMMON_API U8 hex_as_nybble(char hex); /** @@ -529,10 +536,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars); LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len); LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str); - LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len); LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str); +#if LL_WINDOWS +inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);} +#endif + // Length of this UTF32 string in bytes when transformed to UTF8 LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr); @@ -548,7 +558,7 @@ LL_COMMON_API S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len LL_COMMON_API S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen); // Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.) -LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL); +LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = nullptr); /** * @brief Properly truncate a utf8 string to a maximum byte count. @@ -568,6 +578,17 @@ LL_COMMON_API S32 utf8str_compare_insensitive( const std::string& lhs, const std::string& rhs); +/** +* @brief Properly truncate a utf8 string to a maximum character count. +* +* If symbol_len is longer than the string passed in, the return +* value == utf8str. +* @param utf8str A valid utf8 string to truncate. +* @param symbol_len The maximum number of symbols in the return value. +* @return Returns a valid utf8 string with symbol count <= max_len. +*/ +LL_COMMON_API std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len); + /** * @brief Replace all occurences of target_char with replace_char * @@ -810,8 +831,9 @@ public: } /// This implementation uses the answer cached by setiter(). - virtual bool escaped() const { return mIsEsc; } - virtual T next() + bool escaped() const override { return mIsEsc; } + + T next() override { // If we're looking at the escape character of an escape sequence, // skip that character. This is the one time we can modify 'mIter' @@ -827,21 +849,21 @@ public: return result; } - virtual bool is(T ch) const + bool is(T ch) const override { // Like base-class is(), except that an escaped character matches // nothing. return (! done()) && (! mIsEsc) && *mIter == ch; } - virtual bool oneof(const string_type& delims) const + bool oneof(const string_type& delims) const override { // Like base-class oneof(), except that an escaped character matches // nothing. return (! done()) && (! mIsEsc) && LLStringUtilBase::contains(delims, *mIter); } - virtual bool collect_until(string_type& into, const_iterator from, T delim) + bool collect_until(string_type& into, const_iterator from, T delim) override { // Deal with escapes in the characters we collect; that is, an escaped // character must become just that character without the preceding @@ -1155,7 +1177,7 @@ BOOL LLStringUtilBase::precedesDict( const string_type& a, const string_type& { if( a.size() && b.size() ) { - return (LLStringUtilBase::compareDict(a.c_str(), b.c_str()) < 0); + return (LLStringUtilBase::compareDict(a, b) < 0); } else { @@ -1210,7 +1232,7 @@ void LLStringUtilBase::trimHead(string_type& string) template void LLStringUtilBase::trimTail(string_type& string) { - if( string.size() ) + if(!string.empty()) { size_type len = string.length(); size_type i = len; @@ -1223,12 +1245,31 @@ void LLStringUtilBase::trimTail(string_type& string) } } +template +void LLStringUtilBase::trimTail(string_type& string, const string_type& tokens) +{ + if(!string.empty()) + { + size_type len = string.length(); + size_type i = len; + while( i > 0 && (tokens.find_first_of(string[i-1]) != string_type::npos) ) + { + i--; + } + + string.erase( i, len - i ); + } +} + // Replace line feeds with carriage return-line feed pairs. //static template void LLStringUtilBase::addCRLF(string_type& string) { + if (string.empty()) + return; + const T LF = 10; const T CR = 13; @@ -1271,6 +1312,9 @@ void LLStringUtilBase::addCRLF(string_type& string) template void LLStringUtilBase::removeCRLF(string_type& string) { + if (string.empty()) + return; + const T CR = 13; size_type cr_count = 0; @@ -1290,6 +1334,32 @@ void LLStringUtilBase::removeCRLF(string_type& string) //static template +void LLStringUtilBase::removeWindowsCR(string_type& string) +{ + if (string.empty()) + { + return; + } + const T LF = 10; + const T CR = 13; + + size_type cr_count = 0; + size_type len = string.size(); + size_type i; + for( i = 0; i < len - cr_count - 1; i++ ) + { + if( string[i+cr_count] == CR && string[i+cr_count+1] == LF) + { + cr_count++; + } + + string[i] = string[i+cr_count]; + } + string.erase(i, cr_count); +} + +//static +template void LLStringUtilBase::replaceChar( string_type& string, T target, T replacement ) { size_type found_pos = 0; @@ -1373,6 +1443,7 @@ BOOL LLStringUtilBase::containsNonprintable(const string_type& string) return rv; } +// *TODO: reimplement in terms of algorithm //static template void LLStringUtilBase::stripNonprintable(string_type& string) @@ -1383,33 +1454,23 @@ void LLStringUtilBase::stripNonprintable(string_type& string) { return; } - size_t src_size = string.size(); - char* c_string = nullptr; - try - { - c_string = new char[src_size + 1]; - } - catch (const std::bad_alloc&) - { - return; - } - copy(c_string, string.c_str(), src_size+1); - char* write_head = &c_string[0]; + const size_t src_size = string.size(); + auto c_string = std::make_unique(src_size + 1); + + copy(c_string.get(), string.c_str(), src_size+1); for (size_type i = 0; i < src_size; i++) { - char* read_head = &string[i]; - write_head = &c_string[j]; - if(!(*read_head < MIN)) + if(string[i] >= MIN) { - *write_head = *read_head; + c_string[j] = string[i]; ++j; } } c_string[j]= '\0'; - string = c_string; - delete []c_string; + string.assign(c_string.get()); } +// *TODO: reimplement in terms of algorithm template std::basic_string LLStringUtilBase::quote(const string_type& str, const string_type& triggers, @@ -1431,7 +1492,9 @@ std::basic_string LLStringUtilBase::quote(const string_type& str, } // For whatever reason, we must quote this string. + auto needed_escapes = std::count(str.begin(), str.end(), '"'); string_type result; + result.reserve(len + (needed_escapes * escape.length())); result.push_back('"'); for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci) { diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index b990dfe2a..5f5a67a88 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file lluri.cpp * @author Phoenix @@ -40,7 +42,8 @@ #include #include -void encode_character(std::ostream& ostr, std::string::value_type val) +// static +void LLURI::encodeCharacter(std::ostream& ostr, std::string::value_type val) { ostr << "%" @@ -95,7 +98,7 @@ std::string LLURI::escape( } else { - encode_character(ostr, c); + encodeCharacter(ostr, c); } } } @@ -106,7 +109,7 @@ std::string LLURI::escape( c = *it; if(allowed.find(c) == std::string::npos) { - encode_character(ostr, c); + encodeCharacter(ostr, c); } else { @@ -129,11 +132,30 @@ std::string LLURI::unescape(const std::string& str) { ++it; if(it == end) break; - U8 c = hex_as_nybble(*it++); - c = c << 4; - if (it == end) break; - c |= hex_as_nybble(*it); - ostr.put((char)c); + + if(is_char_hex(*it)) + { + U8 c = hex_as_nybble(*it++); + + c = c << 4; + if (it == end) break; + + if(is_char_hex(*it)) + { + c |= hex_as_nybble(*it); + ostr.put((char)c); + } + else + { + ostr.put((char)c); + ostr.put(*it); + } + } + else + { + ostr.put('%'); + ostr.put(*it); + } } else { @@ -169,11 +191,10 @@ namespace { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); } // sub_delims - "&;" + ":@" } -// *TODO: Consider using curl. After http textures gets merged everywhere. // static std::string LLURI::escape(const std::string& str) { - static std::string default_allowed(unreserved() + ":@!$'()*+,=/?&#;"); + static std::string default_allowed = unreserved(); static bool initialized = false; if(!initialized) { @@ -228,7 +249,8 @@ void LLURI::parseAuthorityAndPathUsingOpaque() { if (mScheme == "http" || mScheme == "https" || mScheme == "ftp" || mScheme == "secondlife" || - mScheme == "x-grid-location-info") + mScheme == "x-grid-info" || + mScheme == "x-grid-location-info") // legacy { if (mEscapedOpaque.substr(0,2) != "//") { @@ -401,6 +423,15 @@ LLURI LLURI::buildHTTP(const std::string& prefix, return uri; } +// static +LLURI LLURI::buildHTTP(const std::string& scheme, + const std::string& prefix, + const LLSD& path, + const LLSD& query) +{ + return buildHTTP(llformat("%s://%s", scheme.c_str(), prefix.c_str()), path, query); +} + // static LLURI LLURI::buildHTTP(const std::string& host, const U32& port, @@ -485,6 +516,14 @@ std::string LLURI::hostName() const return unescape(host); } +std::string LLURI::hostNameAndPort() const +{ + std::string user, host, port; + findAuthorityParts(mEscapedAuthority, user, host, port); + return port.empty() ? unescape(host) : unescape(host + ":" + port); +} + + std::string LLURI::userName() const { std::string user, userPass, host, port; diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 57bbedf42..faa5f3a75 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -4,31 +4,25 @@ * @date 2006-02-05 * @brief Declaration of the URI class. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -72,6 +66,13 @@ public: const std::string& prefix, const LLSD& path, const LLSD& query); + + static LLURI buildHTTP( + const std::string& scheme, + const std::string& prefix, + const LLSD& path, + const LLSD& query); + ///< prefix is either a full URL prefix of the form /// "http://example.com:8080", or it can be simply a host and /// optional port like "example.com" or "example.com:8080", in @@ -99,20 +100,21 @@ public: std::string scheme() const; ///< ex.: "http", note lack of colon std::string opaque() const; ///< everything after the colon - // for schemes that follow path like syntax (http, https, ftp) - std::string authority() const; // ex.: "host.com:80" - std::string hostName() const; // ex.: "host.com" - std::string userName() const; - std::string password() const; - U16 hostPort() const; // ex.: 80, will include implicit port - BOOL defaultPort() const; // true if port is default for scheme - const std::string& escapedPath() const { return mEscapedPath; } - std::string path() const; // ex.: "/abc/def", includes leading slash - LLSD pathArray() const; // above decoded into an array of strings - std::string query() const; // ex.: "x=34", section after "?" - const std::string& escapedQuery() const { return mEscapedQuery; } - LLSD queryMap() const; // above decoded into a map - static LLSD queryMap(std::string escaped_query_string); + // for schemes that follow path like syntax (http, https, ftp) + std::string authority() const; // ex.: "user:pass@host.com:80" + std::string hostName() const; // ex.: "host.com" + std::string hostNameAndPort() const; // ex.: "host.com:80" + std::string userName() const; + std::string password() const; + U16 hostPort() const; // ex.: 80, will include implicit port + BOOL defaultPort() const; // true if port is default for scheme + const std::string& escapedPath() const { return mEscapedPath; } + std::string path() const; // ex.: "/abc/def", includes leading slash + LLSD pathArray() const; // above decoded into an array of strings + std::string query() const; // ex.: "x=34", section after "?" + const std::string& escapedQuery() const { return mEscapedQuery; } + LLSD queryMap() const; // above decoded into a map + static LLSD queryMap(std::string escaped_query_string); /** * @brief given a name value map, return a serialized query string. @@ -127,27 +129,24 @@ public: /** @name Escaping Utilities */ //@{ /** - * @brief Escape a raw url with a reasonable set of allowed characters. + * @brief 'Escape' symbol into stream * - * The default set was chosen to match HTTP urls and general - * guidelines for naming resources. Passing in a raw url does not - * produce well defined results because you really need to know - * which segments are path parts because path parts are supposed - * to be escaped individually. The default set chosen is: + * @param ostr Output stream. + * @param val Symbol to encode. + */ + static void encodeCharacter(std::ostream& ostr, std::string::value_type val); + + /** + * @brief Escape the string passed except for unreserved * * ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz * 0123456789 * -._~ - * :@!$'()*+,=/?&#; * - * *NOTE: This API is basically broken because it does not - * allow you to specify significant path characters. For example, - * if the filename actually contained a /, then you cannot use - * this function to generate the serialized url for that - * resource. + * @see http://www.ietf.org/rfc/rfc1738.txt * * @param str The raw URI to escape. - * @return Returns the escaped uri or an empty string. + * @return Returns the rfc 1738 escaped uri or an empty string. */ static std::string escape(const std::string& str); diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp new file mode 100644 index 000000000..e32d9f5c0 --- /dev/null +++ b/indra/llcommon/lluriparser.cpp @@ -0,0 +1,266 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +/** + * @file lluriparser.cpp + * @author Protey + * @date 2014-10-07 + * @brief Implementation of the LLUriParser class. + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lluriparser.h" + +LLUriParser::LLUriParser(const std::string& u) +: mRes(0) +, mTmpScheme(false) +, mNormalizedTmp(false) +{ + mState.uri = &mUri; + + if (u.find("://") == std::string::npos) + { + mNormalizedUri = "http://"; + mTmpScheme = true; + } + + mNormalizedUri += u; + + mRes = parse(); +} + +LLUriParser::~LLUriParser() +{ + uriFreeUriMembersA(&mUri); +} + +S32 LLUriParser::parse() +{ + mRes = uriParseUriA(&mState, mNormalizedUri.c_str()); + return mRes; +} + +const char * LLUriParser::scheme() const +{ + return mScheme.c_str(); +} + +void LLUriParser::scheme(const std::string& s) +{ + mTmpScheme = !s.size(); + mScheme = s; +} + +const char * LLUriParser::port() const +{ + return mPort.c_str(); +} + +void LLUriParser::port(const std::string& s) +{ + mPort = s; +} + +const char * LLUriParser::host() const +{ + return mHost.c_str(); +} + +void LLUriParser::host(const std::string& s) +{ + mHost = s; +} + +const char * LLUriParser::path() const +{ + return mPath.c_str(); +} + +void LLUriParser::path(const std::string& s) +{ + mPath = s; +} + +const char * LLUriParser::query() const +{ + return mQuery.c_str(); +} + +void LLUriParser::query(const std::string& s) +{ + mQuery = s; +} + +const char * LLUriParser::fragment() const +{ + return mFragment.c_str(); +} + +void LLUriParser::fragment(const std::string& s) +{ + mFragment = s; +} + +void LLUriParser::textRangeToString(UriTextRangeA& textRange, std::string& str) +{ + if (textRange.first != nullptr && textRange.afterLast != nullptr && textRange.first < textRange.afterLast) + { + const ptrdiff_t len = textRange.afterLast - textRange.first; + str.assign(textRange.first, static_cast(len)); + } + else + { + str = LLStringUtil::null; + } +} + +void LLUriParser::extractParts() +{ + if (mTmpScheme || mNormalizedTmp) + { + mScheme.clear(); + } + else + { + textRangeToString(mUri.scheme, mScheme); + } + + textRangeToString(mUri.hostText, mHost); + textRangeToString(mUri.portText, mPort); + textRangeToString(mUri.query, mQuery); + textRangeToString(mUri.fragment, mFragment); + + UriPathSegmentA * pathHead = mUri.pathHead; + while (pathHead) + { + std::string partOfPath; + textRangeToString(pathHead->text, partOfPath); + + mPath += '/'; + mPath += partOfPath; + + pathHead = pathHead->next; + } +} + +S32 LLUriParser::normalize() +{ + mNormalizedTmp = mTmpScheme; + if (!mRes) + { + mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST); + + if (!mRes) + { + S32 chars_required; + mRes = uriToStringCharsRequiredA(&mUri, &chars_required); + + if (!mRes) + { + chars_required++; + std::vector label_buf(chars_required); + mRes = uriToStringA(&label_buf[0], &mUri, chars_required, nullptr); + + if (!mRes) + { + mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0]; + mTmpScheme = false; + } + } + } + } + + if(mTmpScheme) + { + mNormalizedUri = mNormalizedUri.substr(7); + mTmpScheme = false; + } + + return mRes; +} + +void LLUriParser::glue(std::string& uri) const +{ + std::string first_part; + glueFirst(first_part); + + std::string second_part; + glueSecond(second_part); + + uri = first_part + second_part; +} + +void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const +{ + if (use_scheme && mScheme.size()) + { + uri = mScheme; + uri += "://"; + } + else + { + uri.clear(); + } + + uri += mHost; +} + +void LLUriParser::glueSecond(std::string& uri) const +{ + if (mPort.size()) + { + uri = ':'; + uri += mPort; + } + else + { + uri.clear(); + } + + uri += mPath; + + if (mQuery.size()) + { + uri += '?'; + uri += mQuery; + } + + if (mFragment.size()) + { + uri += '#'; + uri += mFragment; + } +} + +bool LLUriParser::test() const +{ + std::string uri; + glue(uri); + + return uri == mNormalizedUri; +} + +const char * LLUriParser::normalizedUri() const +{ + return mNormalizedUri.c_str(); +} diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h new file mode 100644 index 000000000..773d4189f --- /dev/null +++ b/indra/llcommon/lluriparser.h @@ -0,0 +1,87 @@ +/** + * @file lluriparser.h + * @author Protey + * @date 20146-10-07 + * @brief Declaration of the UriParser class. + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLURIPARSER_H +#define LL_LLURIPARSER_H + +#include +#include + +class LL_COMMON_API LLUriParser +{ +public: + LLUriParser(const std::string& u); + ~LLUriParser(); + + const char * scheme() const; + void scheme (const std::string& s); + + const char * port() const; + void port (const std::string& s); + + const char * host() const; + void host (const std::string& s); + + const char * path() const; + void path (const std::string& s); + + const char * query() const; + void query (const std::string& s); + + const char * fragment() const; + void fragment (const std::string& s); + + const char * normalizedUri() const; + + void extractParts(); + void glue(std::string& uri) const; + void glueFirst(std::string& uri, bool use_scheme = true) const; + void glueSecond(std::string& uri) const; + bool test() const; + S32 normalize(); + +private: + S32 parse(); + void textRangeToString(UriTextRangeA& textRange, std::string& str); + std::string mScheme; + std::string mHost; + std::string mPort; + std::string mPath; + std::string mQuery; + std::string mFragment; + std::string mNormalizedUri; + + UriParserStateA mState; + UriUriA mUri; + + S32 mRes; + bool mTmpScheme; + bool mNormalizedTmp; +}; + +#endif // LL_LLURIPARSER_H diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 7f2327294..f1d99d0e7 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4238,6 +4238,23 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s { setLastSegmentToolTip(match.getTooltip()); } + + // show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries + std::string label = match.getQuery(); + if (!label.empty()) + { + /* Singu Note: Upstream uses hardcoded Grey here, they have no care for skins, this could be awful! For now just make it a normal link + link_params.color = LLColor4::grey; + link_params.readonly_color = LLColor4::grey; + appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly());*/ + append_link(label); + + // set the tooltip for the query part of url + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + } } else if (!replace_links) // Still link the link itself { @@ -4804,7 +4821,7 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) if (auto style = segment->getStyle()) { if (style->isLink()) - LLUrlAction::clickAction(style->getLinkHREF()); + LLUrlAction::clickAction(style->getLinkHREF(), true); } } } diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp index fd9b3d9a6..74ce67e88 100644 --- a/indra/llui/llurlaction.cpp +++ b/indra/llui/llurlaction.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llurlaction.cpp * @author Martin Reddy @@ -24,7 +26,6 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - #include "linden_common.h" #include "llurlaction.h" @@ -32,6 +33,7 @@ #include "llwindow.h" #include "llurlregistry.h" + // global state for the callback functions LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback; LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback; @@ -83,18 +85,19 @@ void LLUrlAction::openURLExternal(std::string url) } } -void LLUrlAction::executeSLURL(std::string url) +bool LLUrlAction::executeSLURL(std::string url, bool trusted_content) { if (sExecuteSLURLCallback) { - sExecuteSLURLCallback(url); + return sExecuteSLURLCallback(url, trusted_content); } + return false; } -void LLUrlAction::clickAction(std::string url) +void LLUrlAction::clickAction(std::string url, bool trusted_content) { // Try to handle as SLURL first, then http Url - if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url) ) + if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url, trusted_content) ) { if (sOpenURLCallback) { @@ -157,3 +160,86 @@ void LLUrlAction::showProfile(std::string url) } } } + +std::string LLUrlAction::getUserID(std::string url) +{ + LLURI uri(url); + LLSD path_array = uri.pathArray(); + std::string id_str; + if (path_array.size() == 4) + { + id_str = path_array.get(2).asString(); + } + return id_str; +} + +std::string LLUrlAction::getObjectId(std::string url) +{ + LLURI uri(url); + LLSD path_array = uri.pathArray(); + std::string id_str; + if (path_array.size() >= 3) + { + id_str = path_array.get(2).asString(); + } + return id_str; +} + +std::string LLUrlAction::getObjectName(std::string url) +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + std::string name; + if (query_map.has("name")) + { + name = query_map["name"].asString(); + } + return name; +} + +void LLUrlAction::sendIM(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/im"); + } +} + +void LLUrlAction::addFriend(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/requestfriend"); + } +} + +void LLUrlAction::removeFriend(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/removefriend"); + } +} + +void LLUrlAction::blockObject(std::string url) +{ + std::string object_id = getObjectId(url); + std::string object_name = getObjectName(url); + if (LLUUID::validate(object_id)) + { + executeSLURL("secondlife:///app/agent/" + object_id + "/block/" + LLURI::escape(object_name)); + } +} + +void LLUrlAction::unblockObject(std::string url) +{ + std::string object_id = getObjectId(url); + std::string object_name = getObjectName(url); + if (LLUUID::validate(object_id)) + { + executeSLURL("secondlife:///app/agent/" + object_id + "/unblock/" + object_name); + } +} diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h index 0747829b7..51ab8261b 100644 --- a/indra/llui/llurlaction.h +++ b/indra/llui/llurlaction.h @@ -28,11 +28,7 @@ #ifndef LL_LLURLACTION_H #define LL_LLURLACTION_H -#include -#ifndef BOOST_FUNCTION_HPP_INCLUDED -#include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif +#include /// /// The LLUrlAction class provides a number of static functions that @@ -60,7 +56,7 @@ public: static void openURLExternal(std::string url); /// execute the given secondlife: SLURL - static void executeSLURL(std::string url); + static bool executeSLURL(std::string url, bool trusted_content = true); /// if the Url specifies an SL location, teleport there static void teleportToLocation(std::string url); @@ -69,7 +65,7 @@ public: static void showLocationOnMap(std::string url); /// perform the appropriate action for left-clicking on a Url - static void clickAction(std::string url); + static void clickAction(std::string url, bool trusted_content); /// copy the label for a Url to the clipboard static void copyLabelToClipboard(std::string url); @@ -79,10 +75,18 @@ public: /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile static void showProfile(std::string url); + static std::string getUserID(std::string url); + static std::string getObjectName(std::string url); + static std::string getObjectId(std::string url); + static void sendIM(std::string url); + static void addFriend(std::string url); + static void removeFriend(std::string url); + static void blockObject(std::string url); + static void unblockObject(std::string url); /// specify the callbacks to enable this class's functionality - typedef boost::function url_callback_t; - typedef boost::function execute_url_callback_t; + typedef std::function url_callback_t; + typedef std::function execute_url_callback_t; static void setOpenURLCallback(url_callback_t cb); static void setOpenURLInternalCallback(url_callback_t cb); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 54dc1819c..0282ae626 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llurlentry.cpp * @author Martin Reddy @@ -31,6 +33,7 @@ #include "lluri.h" #include "llurlmatch.h" #include "llurlregistry.h" +#include "lluriparser.h" #include "llavatarnamecache.h" #include "llcachename.h" @@ -38,14 +41,18 @@ //#include "lluicolortable.h" #include "message.h" -#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" +#include // + +#define APP_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/app)|(secondlife:///app))" +#define X_GRID_OR_SECONDLIFE_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/)|(secondlife://))" // Utility functions std::string localize_slapp_label(const std::string& url, const std::string& full_name); LLUrlEntryBase::LLUrlEntryBase() -{} +{ +} LLUrlEntryBase::~LLUrlEntryBase() { @@ -111,7 +118,7 @@ std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const { // return the label part from [http://www.example.org Label] const char *text = url.c_str(); - S32 start = 0; + size_t start = 0; while (! isspace(text[start])) { start++; @@ -127,7 +134,7 @@ std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const { // return the url part from [http://www.example.org Label] const char *text = string.c_str(); - S32 end = 0; + size_t end = 0; while (! isspace(text[end])) { end++; @@ -142,11 +149,14 @@ void LLUrlEntryBase::addObserver(const std::string &id, // add a callback to be notified when we have a label for the uuid LLUrlEntryObserver observer; observer.url = url; - observer.signal = new LLUrlLabelSignal(); - if (observer.signal) + try { + observer.signal = new LLUrlLabelSignal(); observer.signal->connect(cb); - mObservers.insert(std::pair(id, observer)); + mObservers.emplace(id, observer); + } + catch (...) + { } } @@ -178,12 +188,49 @@ bool LLUrlEntryBase::isLinkDisabled() const return globally_disabled; } -static std::string getStringAfterToken(const std::string str, const std::string token) +bool LLUrlEntryBase::isWikiLinkCorrect(const std::string& url) +{ + LLWString label = utf8str_to_wstring(getLabelFromWikiLink(url)); + label.erase(std::remove(label.begin(), label.end(), L'\u200B'), label.end()); + return (LLUrlRegistry::instance().hasUrl(wstring_to_utf8str(label))) ? false : true; +} + +std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const +{ + LLUriParser up(unescapeUrl(url)); + up.normalize(); + + std::string label; + up.extractParts(); + up.glueFirst(label); + + return label; +} + +std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const +{ + LLUriParser up(unescapeUrl(url)); + + std::string label; + up.extractParts(); + up.glueFirst(label, false); + + size_t pos = url.find(label); + if (pos == std::string::npos) + { + return ""; + } + pos += label.size(); + return url.substr(pos); +} + + +static std::string getStringAfterToken(const std::string& str, const std::string& token) { size_t pos = str.find(token); if (pos == std::string::npos) { - return ""; + return std::string(); } pos += token.size(); @@ -194,8 +241,9 @@ static std::string getStringAfterToken(const std::string str, const std::string // LLUrlEntryHTTP Describes generic http: and https: Urls // LLUrlEntryHTTP::LLUrlEntryHTTP() + : LLUrlEntryBase() { - mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", + mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -206,6 +254,20 @@ std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCal return unescapeUrl(url); } +std::string LLUrlEntryHTTP::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const +{ + return mTooltip;; +} + // // LLUrlEntryHTTP Describes generic http: and https: Urls with custom label // We use the wikipedia syntax of [http://www.example.org Text] @@ -238,20 +300,22 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const // LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com // LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() + : LLUrlEntryBase() { - mPattern = boost::regex("(" - "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR - "|" // or - "(?]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net - ")", - boost::regex::perl|boost::regex::icase); + mPattern = boost::regex("\\bwww\\.\\S+\\.([^\\s<]*)?\\b", // i.e. www.FOO.BAR + boost::regex::perl | boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); } std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) { - return unescapeUrl(url); + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); } std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const @@ -263,6 +327,95 @@ std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const return escapeUrl(string); } +std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + + return escapeUrl(url); +} + +std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const +{ + S32 actual_parts; + + if(url.find(".com/secondlife/") != std::string::npos) + { + actual_parts = 5; + } + else if(url.find("/app/") != std::string::npos) + { + actual_parts = 6; + } + else + { + actual_parts = 3; + } + + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + S32 x,y,z; + + if (path_parts == actual_parts) + { + // handle slurl with (X,Y,Z) coordinates + LLStringUtil::convertToS32(path_array[path_parts-3],x); + LLStringUtil::convertToS32(path_array[path_parts-2],y); + LLStringUtil::convertToS32(path_array[path_parts-1],z); + + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0)) + { + return TRUE; + } + } + else if (path_parts == (actual_parts-1)) + { + // handle slurl with (X,Y) coordinates + + LLStringUtil::convertToS32(path_array[path_parts-2],x); + LLStringUtil::convertToS32(path_array[path_parts-1],y); + ; + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256)) + { + return TRUE; + } + } + else if (path_parts == (actual_parts-2)) + { + // handle slurl with (X) coordinate + LLStringUtil::convertToS32(path_array[path_parts-1],x); + if(x>= 0 && x<= 256) + { + return TRUE; + } + } + + return FALSE; +} + // // LLUrlEntrySLURL Describes generic http: and https: Urls // @@ -284,6 +437,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa // - http://slurl.com/secondlife/Place/X // - http://slurl.com/secondlife/Place // + LLURI uri(url); LLSD path_array = uri.pathArray(); S32 path_parts = path_array.size(); @@ -335,13 +489,62 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const return url.substr(pos, url.size() - pos); } +// +// LLUrlEntrySeconlifeURL Describes *secondlife.com/ and *lindenlab.com/ urls to substitute icon 'hand.png' before link +// +LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() +{ + mPattern = boost::regex("\\b(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?(/\\S*)?\\b", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +/// Return the url from a string that matched the regex +std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "https://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const +{ + return url; +} + +// +// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com and *lindenlab.com urls to substitute icon 'hand.png' before link +// +LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() + { + mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; +} + // // LLUrlEntryAgent Describes a Second Life agent Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // -LLUrlEntryAgent::LLUrlEntryAgent() : - mAvatarNameCacheConnection() +LLUrlEntryAgent::LLUrlEntryAgent() { mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); @@ -372,7 +575,15 @@ void LLUrlEntryAgent::callObservers(const std::string &id, void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + auto range = mAvatarNameCacheConnections.equal_range(id); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = av_name.getCompleteName(); @@ -459,11 +670,8 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, connection); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -522,16 +730,23 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) // // LLUrlEntryAgentName describes a Second Life agent name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // -LLUrlEntryAgentName::LLUrlEntryAgentName() : - mAvatarNameCacheConnection() +LLUrlEntryAgentName::LLUrlEntryAgentName() {} void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + auto range = mAvatarNameCacheConnections.equal_range(id); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = getName(av_name); // received the agent name from the server - tell our observers @@ -566,11 +781,8 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, connection); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -585,7 +797,7 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab // // LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename // LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() { @@ -598,10 +810,26 @@ std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name return avatar_name.getCompleteName(); } +// +// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// +LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getLegacyName(); +} + // // LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname // LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() { @@ -617,7 +845,7 @@ std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) // // LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username // LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() { @@ -634,7 +862,7 @@ std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) // LLUrlEntryGroup Describes a Second Life group Url, e.g., // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect -// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect // LLUrlEntryGroup::LLUrlEntryGroup() { @@ -714,7 +942,7 @@ LLUrlEntryInventory::LLUrlEntryInventory() //*TODO: add supporting of inventory item names with whitespaces //this pattern cann't parse for example //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + //x-grid-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_inventory.xml"; @@ -732,7 +960,7 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab // LLUrlEntryObjectIM::LLUrlEntryObjectIM() { - mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*", + mPattern = boost::regex(APP_HEADER_REGEX "/objectim/[\\da-f-]+\?\\S*\\w", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_objectim.xml"; } @@ -765,7 +993,7 @@ std::set LLUrlEntryParcel::sParcelInfoObservers; /// /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about -/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about /// LLUrlEntryParcel::LLUrlEntryParcel() { @@ -860,7 +1088,7 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) // LLUrlEntryPlace::LLUrlEntryPlace() { - mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + mPattern = boost::regex("((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); @@ -968,7 +1196,7 @@ std::string LLUrlEntryRegion::getLocation(const std::string &url) const // // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., // secondlife:///app/teleport/Ahern/50/50/50/ -// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ +// x-grid-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ // LLUrlEntryTeleport::LLUrlEntryTeleport() { @@ -1042,7 +1270,7 @@ std::string LLUrlEntryTeleport::getLocation(const std::string &url) const // LLUrlEntrySL::LLUrlEntrySL() { - mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", + mPattern = boost::regex(X_GRID_OR_SECONDLIFE_HEADER_REGEX "(\\w+)?(:\\d+)?/\\S+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slapp.xml"; mTooltip = LLTrans::getString("TooltipSLAPP"); @@ -1059,7 +1287,7 @@ std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallb // LLUrlEntrySLLabel::LLUrlEntrySLLabel() { - mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", + mPattern = boost::regex("\\[" X_GRID_OR_SECONDLIFE_HEADER_REGEX "\\S+[ \t]+[^\\]]+\\]", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slapp.xml"; mTooltip = LLTrans::getString("TooltipSLAPP"); @@ -1201,3 +1429,75 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url) LLStringUtil::trim(mIcon); return mIcon; } + +// +// LLUrlEntryEmail Describes a generic mailto: Urls +// +LLUrlEntryEmail::LLUrlEntryEmail() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_email.xml"; + mTooltip = LLTrans::getString("TooltipEmail"); +} + +std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + size_t pos = url.find("mailto:"); + + if (pos == std::string::npos) + { + return escapeUrl(url); + } + + std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); + return ret; +} + +std::string LLUrlEntryEmail::getUrl(const std::string &string) const +{ + if (string.find("mailto:") == std::string::npos) + { + return "mailto:" + escapeUrl(string); + } + return escapeUrl(string); +} + +// +// +// LLUrlEntryJIRA describes a Jira Issue Tracker entry +// +LLUrlEntryJira::LLUrlEntryJira() +{ + mPattern = boost::regex("((?:ALCH|SV|BUG|CHOP|FIRE|MAINT|OPEN|SCR|STORM|SVC|VWR|WEB)-\\d+)", + boost::regex::perl); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryJira::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +std::string LLUrlEntryJira::getTooltip(const std::string &string) const +{ + return getUrl(string); +} + +std::string LLUrlEntryJira::getUrl(const std::string &url) const +{ + return (boost::format( + (url.find("ALCH") != std::string::npos) ? + "http://alchemy.atlassian.net/browse/%1%" : + (url.find("SV") != std::string::npos) ? + "https://singularityviewer.atlassian.net/browse/%1%" : + (url.find("FIRE") != std::string::npos) ? + "http://jira.phoenixviewer.com/browse/%1%" : + "http://jira.secondlife.com/browse/%1%" + ) % url).str(); +} +// + + diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index e52fe8040..a772cf8c9 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -35,10 +35,7 @@ #include "llavatarname.h" #include "llhost.h" // for resolving parcel name by parcel id -#include #include -#include -#include class LLAvatarName; @@ -78,6 +75,9 @@ public: /// Given a matched Url, return a label for the Url virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } + /// Return port, query and fragment parts for the Url + virtual std::string getQuery(const std::string &url) const { return ""; } + /// Return an icon that can be displayed next to Urls of this type virtual std::string getIcon(const std::string &url); @@ -96,10 +96,16 @@ public: /// Should this link text be underlined only when mouse is hovered over it? virtual bool underlineOnHoverOnly(const std::string &string) const { return true; } // + virtual bool isTrusted() const { return false; } + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } bool isLinkDisabled() const; + bool isWikiLinkCorrect(const std::string& url); + + virtual bool isSLURLvalid(const std::string &url) const { return TRUE; }; + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; @@ -107,6 +113,8 @@ protected: std::string getLabelFromWikiLink(const std::string &url) const; std::string getUrlFromWikiLink(const std::string &string) const; void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); + std::string urlToLabelWithGreyQuery(const std::string &url) const; + std::string urlToGreyQuery(const std::string &url) const; virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); typedef struct { @@ -128,7 +136,9 @@ class LLUrlEntryHTTP : public LLUrlEntryBase { public: LLUrlEntryHTTP(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; }; /// @@ -138,9 +148,9 @@ class LLUrlEntryHTTPLabel : public LLUrlEntryBase { public: LLUrlEntryHTTPLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ std::string getUrl(const std::string &string) const override; }; /// @@ -150,8 +160,21 @@ class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase { public: LLUrlEntryHTTPNoProtocol(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; + std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + std::string getQuery(const std::string &url) const override; + std::string getUrl(const std::string &string) const override; + std::string getTooltip(const std::string &url) const override; +}; + +class LLUrlEntryInvalidSLURL : public LLUrlEntryBase +{ +public: + LLUrlEntryInvalidSLURL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; + + bool isSLURLvalid(const std::string &url) const override; }; /// @@ -161,8 +184,31 @@ class LLUrlEntrySLURL : public LLUrlEntryBase { public: LLUrlEntrySLURL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySecondlifeURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySecondlifeURL(); + /*virtual*/ bool isTrusted() const override { return true; } + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getQuery(const std::string &url) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL +{ +public: + LLUrlEntrySimpleSecondlifeURL(); }; /// @@ -174,22 +220,27 @@ public: LLUrlEntryAgent(); ~LLUrlEntryAgent() { - if (mAvatarNameCacheConnection.connected()) + for(const auto& conn_pair : mAvatarNameCacheConnections) { - mAvatarNameCacheConnection.disconnect(); + if (conn_pair.second.connected()) + { + conn_pair.second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); - /*virtual*/ std::string getTooltip(const std::string &string) const; - //*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getIcon(const std::string &url) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + ///*virtual*/ LLStyle::Params getStyle() const override; + /*virtual*/ LLUUID getID(const std::string &string) const override; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const override; protected: - /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); + /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon) override; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + using avatar_name_cache_connection_map_t = std::multimap; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; /// @@ -203,19 +254,25 @@ public: LLUrlEntryAgentName(); ~LLUrlEntryAgentName() { - if (mAvatarNameCacheConnection.connected()) + for (const auto& conn_pair : mAvatarNameCacheConnections) { - mAvatarNameCacheConnection.disconnect(); + if (conn_pair.second.connected()) + { + conn_pair.second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); + } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + ///*virtual*/ LLStyle::Params getStyle() const override; protected: // override this to pull out relevant name fields virtual std::string getName(const LLAvatarName& avatar_name) = 0; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + using avatar_name_cache_connection_map_t = std::multimap; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; @@ -229,7 +286,15 @@ class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName public: LLUrlEntryAgentCompleteName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; +}; + +class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentLegacyName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -242,7 +307,7 @@ class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName public: LLUrlEntryAgentDisplayName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -255,7 +320,7 @@ class LLUrlEntryAgentUserName : public LLUrlEntryAgentName public: LLUrlEntryAgentUserName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -266,9 +331,9 @@ class LLUrlEntryGroup : public LLUrlEntryBase { public: LLUrlEntryGroup(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - //*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + ///*virtual*/ LLStyle::Params getStyle() const override; + /*virtual*/ LLUUID getID(const std::string &string) const override; private: void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); }; @@ -281,7 +346,7 @@ class LLUrlEntryInventory : public LLUrlEntryBase { public: LLUrlEntryInventory(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; private: }; @@ -293,8 +358,8 @@ class LLUrlEntryObjectIM : public LLUrlEntryBase { public: LLUrlEntryObjectIM(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; private: }; @@ -317,7 +382,7 @@ public: LLUrlEntryParcel(); ~LLUrlEntryParcel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; // Sends a parcel info request to sim. void sendParcelInfoRequest(const LLUUID& parcel_id); @@ -353,8 +418,8 @@ class LLUrlEntryPlace : public LLUrlEntryBase { public: LLUrlEntryPlace(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -365,8 +430,8 @@ class LLUrlEntryRegion : public LLUrlEntryBase { public: LLUrlEntryRegion(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -377,8 +442,8 @@ class LLUrlEntryTeleport : public LLUrlEntryBase { public: LLUrlEntryTeleport(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -389,7 +454,7 @@ class LLUrlEntrySL : public LLUrlEntryBase { public: LLUrlEntrySL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; }; /// @@ -400,10 +465,10 @@ class LLUrlEntrySLLabel : public LLUrlEntryBase { public: LLUrlEntrySLLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const override; }; /// @@ -414,8 +479,8 @@ class LLUrlEntryWorldMap : public LLUrlEntryBase { public: LLUrlEntryWorldMap(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -425,9 +490,9 @@ class LLUrlEntryNoLink : public LLUrlEntryBase { public: LLUrlEntryNoLink(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + ///*virtual*/ LLStyle::Params getStyle() const override; }; /// @@ -437,10 +502,35 @@ class LLUrlEntryIcon : public LLUrlEntryBase { public: LLUrlEntryIcon(); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getIcon(const std::string &url) override; }; +/// +/// LLUrlEntryEmail Describes a generic mailto: Urls +/// +class LLUrlEntryEmail : public LLUrlEntryBase +{ +public: + LLUrlEntryEmail(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; +}; + +// +/// +/// LLUrlEntryJira describes a Jira Issue +/// +class LLUrlEntryJira : public LLUrlEntryBase +{ +public: + LLUrlEntryJira(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + +}; +// #endif diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index af4955ac4..3d4e808ec 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llurlmatch.cpp * @author Martin Reddy @@ -37,20 +39,22 @@ LLUrlMatch::LLUrlMatch() : mIcon(""), mMenuName(""), mLocation(""), - mUnderlineOnHoverOnly(false) + mUnderlineOnHoverOnly(false), + mTrusted(false) { } -void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, - const std::string &label, const std::string &tooltip, +void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label, + const std::string& query, const std::string &tooltip, const std::string &icon, /*const LLStyle::Params& style,*/ const std::string &menu, const std::string &location, - const LLUUID& id, bool underline_on_hover_only) + const LLUUID& id, bool underline_on_hover_only, bool trusted) { mStart = start; mEnd = end; mUrl = url; mLabel = label; + mQuery = query; mTooltip = tooltip; mIcon = icon; //mStyle = style; @@ -59,4 +63,5 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, mLocation = location; mID = id; mUnderlineOnHoverOnly = underline_on_hover_only; + mTrusted = trusted; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index af36e6925..67255d3ae 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -30,8 +30,6 @@ //#include "linden_common.h" -#include -#include #include "llstyle.h" /// @@ -62,6 +60,9 @@ public: /// return a label that can be used for the display of this Url std::string getLabel() const { return mLabel; } + /// return a right part of url which should be drawn in grey + std::string getQuery() const { return mQuery; } + /// return a message that could be displayed in a tooltip or status bar std::string getTooltip() const { return mTooltip; } @@ -80,12 +81,15 @@ public: /// Should this link text be underlined only when mouse is hovered over it? bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; } + /// Return true if Url is trusted. + bool isTrusted() const { return mTrusted; } + /// Change the contents of this match object (used by LLUrlRegistry) void setValues(U32 start, U32 end, const std::string &url, const std::string &label, - const std::string &tooltip, const std::string &icon, - /*const LLStyle::Params& style, */const std::string &menu, + const std::string& query, const std::string &tooltip, const std::string &icon, + /*const LLStyle::Params& style,*/ const std::string &menu, const std::string &location, const LLUUID& id, - bool underline_on_hover_only = false ); + bool underline_on_hover_only = false, bool trusted = false); const LLUUID& getID() const { return mID; } private: @@ -93,6 +97,7 @@ private: U32 mEnd; std::string mUrl; std::string mLabel; + std::string mQuery; std::string mTooltip; std::string mIcon; std::string mMenuName; @@ -100,6 +105,7 @@ private: LLUUID mID; //LLStyle::Params mStyle; bool mUnderlineOnHoverOnly; + bool mTrusted; }; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 235b002d0..92d85ff91 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llurlregistry.cpp * @author Martin Reddy @@ -27,6 +29,7 @@ #include "linden_common.h" #include "llurlregistry.h" +#include "lluriparser.h" #include @@ -37,15 +40,26 @@ void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, LLUrlRegistry::LLUrlRegistry() { - mUrlEntry.reserve(20); + mUrlEntry.reserve(23); // // Urls are matched in the order that they were registered registerUrl(new LLUrlEntryNoLink()); - registerUrl(new LLUrlEntryIcon()); + mUrlEntryIcon = new LLUrlEntryIcon(); + registerUrl(mUrlEntryIcon); + mLLUrlEntryInvalidSLURL = new LLUrlEntryInvalidSLURL(); + registerUrl(mLLUrlEntryInvalidSLURL); registerUrl(new LLUrlEntrySLURL()); + + // decorated links for host names like: secondlife.com and lindenlab.com + mUrlEntryTrusted = new LLUrlEntrySecondlifeURL(); + registerUrl(mUrlEntryTrusted); + registerUrl(new LLUrlEntrySimpleSecondlifeURL()); + registerUrl(new LLUrlEntryHTTP()); - registerUrl(new LLUrlEntryHTTPLabel()); + mUrlEntryHTTPLabel = new LLUrlEntryHTTPLabel(); + registerUrl(mUrlEntryHTTPLabel); registerUrl(new LLUrlEntryAgentCompleteName()); + registerUrl(new LLUrlEntryAgentLegacyName()); registerUrl(new LLUrlEntryAgentDisplayName()); registerUrl(new LLUrlEntryAgentUserName()); // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since @@ -59,14 +73,17 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); registerUrl(new LLUrlEntryInventory()); - registerUrl(new LLUrlEntryObjectIM()); //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern, //so it should be registered in the end of list registerUrl(new LLUrlEntrySL()); - registerUrl(new LLUrlEntrySLLabel()); - // most common pattern is a URL without any protocol, - // e.g., "secondlife.com" - registerUrl(new LLUrlEntryHTTPNoProtocol()); + mUrlEntrySLLabel = new LLUrlEntrySLLabel(); + registerUrl(mUrlEntrySLLabel); + registerUrl(new LLUrlEntryEmail()); + // Parse teh jiras! + registerUrl(new LLUrlEntryJira()); // + // most common pattern is a URL without any protocol starting with "www", + // e.g., "www.secondlife.com" + registerUrl(new LLUrlEntryHTTPNoProtocol()); } LLUrlRegistry::~LLUrlRegistry() @@ -90,7 +107,7 @@ void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front) } } -static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end) +static bool matchRegex(const char *text, const boost::regex& regex, U32 &start, U32 &end) { boost::cmatch result; bool found; @@ -100,7 +117,7 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en { found = boost::regex_search(text, result, regex); } - catch (std::runtime_error &) + catch (const std::runtime_error &) { return false; } @@ -127,6 +144,11 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en end--; } + else if (text[end] == ']' && std::string(text+start, end-start).find('[') == std::string::npos) + { + end--; + } + return true; } @@ -142,32 +164,77 @@ static bool stringHasUrl(const std::string &text) text.find(".edu") != std::string::npos || text.find(".org") != std::string::npos || text.find("") != std::string::npos || - text.find(" +static bool stringHasJira(const std::string &text) +{ + // fast heuristic test for a URL in a string. This is used + // to avoid lots of costly regex calls, BUT it needs to be + // kept in sync with the LLUrlEntry regexes we support. + return (text.find("ALCH") != std::string::npos || + text.find("SV") != std::string::npos || + text.find("BUG") != std::string::npos || + text.find("CHOP") != std::string::npos || + text.find("FIRE") != std::string::npos || + text.find("MAINT") != std::string::npos || + text.find("OPEN") != std::string::npos || + text.find("SCR") != std::string::npos || + text.find("STORM") != std::string::npos || + text.find("SVC") != std::string::npos || + text.find("VWR") != std::string::npos || + text.find("WEB") != std::string::npos); +} +// + +bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted) { // avoid costly regexes if there is clearly no URL in the text - if (! stringHasUrl(text)) + if (!(stringHasUrl(text) || stringHasJira(text))) // { return false; } // find the first matching regex from all url entries in the registry U32 match_start = 0, match_end = 0; - LLUrlEntryBase *match_entry = NULL; + LLUrlEntryBase *match_entry = nullptr; std::vector::iterator it; for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) { + //Skip for url entry icon if content is not trusted + if(!is_content_trusted && (mUrlEntryIcon == *it)) + { + continue; + } + LLUrlEntryBase *url_entry = *it; U32 start = 0, end = 0; if (matchRegex(text.c_str(), url_entry->getPattern(), start, end)) { // does this match occur in the string before any other match - if (start < match_start || match_entry == NULL) + if (start < match_start || match_entry == nullptr) { + + if (mLLUrlEntryInvalidSLURL == *it) + { + if(url_entry && url_entry->isSLURLvalid(text.substr(start, end - start + 1))) + { + continue; + } + } + + if((mUrlEntryHTTPLabel == *it) || (mUrlEntrySLLabel == *it)) + { + if(url_entry && !url_entry->isWikiLinkCorrect(text.substr(start, end - start + 1))) + { + continue; + } + } + match_start = start; match_end = end; match_entry = url_entry; @@ -178,18 +245,32 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL // did we find a match? if so, return its details in the match object if (match_entry) { + // Skip if link is an email with an empty username (starting with @). See MAINT-5371. + if (match_start > 0 && text.substr(match_start - 1, 1) == "@") + return false; + // fill in the LLUrlMatch object and return it std::string url = text.substr(match_start, match_end - match_start + 1); + + if (match_entry == mUrlEntryTrusted) + { + LLUriParser up(url); + up.normalize(); + url = up.normalizedUri(); + } + match.setValues(match_start, match_end, match_entry->getUrl(url), match_entry->getLabel(url, cb), + match_entry->getQuery(url), match_entry->getTooltip(url), match_entry->getIcon(url), //match_entry->getStyle(), match_entry->getMenuName(), match_entry->getLocation(url), match_entry->getID(url), - match_entry->underlineOnHoverOnly(url)); + match_entry->underlineOnHoverOnly(url), + match_entry->isTrusted()); return true; } @@ -209,7 +290,7 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr // character encoding, so we need to update the start // and end values to be correct for the wide string. LLWString wurl = utf8str_to_wstring(match.getUrl()); - S32 start = text.find(wurl); + size_t start = text.find(wurl); if (start == std::string::npos) { return false; @@ -218,6 +299,7 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr match.setValues(start, end, match.getUrl(), match.getLabel(), + match.getQuery(), match.getTooltip(), match.getIcon(), //match.getStyle(), diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index da16171a9..90099c6d1 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -33,9 +33,6 @@ #include "llsingleton.h" #include "llstring.h" -#include -#include - /// This default callback for findUrl() simply ignores any label updates void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, @@ -73,7 +70,8 @@ public: /// get the next Url in an input string, starting at a given character offset /// your callback is invoked if the matched Url's label changes in the future bool findUrl(const std::string &text, LLUrlMatch &match, - const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback, + bool is_content_trusted = false); /// a slightly less efficient version of findUrl for wide strings bool findUrl(const LLWString &text, LLUrlMatch &match, @@ -92,6 +90,11 @@ private: friend class LLSingleton; std::vector mUrlEntry; + LLUrlEntryBase* mUrlEntryTrusted; + LLUrlEntryBase* mUrlEntryIcon; + LLUrlEntryBase* mLLUrlEntryInvalidSLURL; + LLUrlEntryBase* mUrlEntryHTTPLabel; + LLUrlEntryBase* mUrlEntrySLLabel; }; #endif diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 762155d8f..f64b48667 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -49,8 +49,8 @@ LLSplashScreen *gSplashScreenp = NULL; BOOL gDebugClicks = FALSE; BOOL gDebugWindowProc = FALSE; -const S32 gURLProtocolWhitelistCount = 4; -const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:"/*, "file:"*/ }; +const S32 gURLProtocolWhitelistCount = 5; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:", "mailto:" }; // CP: added a handler list - this is what's used to open the protocol and is based on registry entry // only meaningful difference currently is that file: protocols are opened using http: diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d49eb8a29..258d44e95 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -43,6 +43,7 @@ include(UI) include(ViewerMiscLibs) include(WinManifest) include(ZLIB) +include(URIPARSER) if (MSVC) use_prebuilt_binary(vcredist) @@ -1635,6 +1636,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${NVAPI_LIBRARY} + ${URIPARSER_LIBRARY} ${viewer_LIBRARIES} ${Boost_CONTEXT_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index 7021ace43..ee069ecbd 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -535,7 +535,7 @@ bool cmd_line_chat(std::string data, EChatType type) LLAvatarActions::showProfile(id); return false; } - LLUrlAction::clickAction(sub); + LLUrlAction::clickAction(sub, true); } return false; } diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index f2730839b..f6b27eb7a 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -507,14 +507,40 @@ Section "Viewer" WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" "$INSTDIR\$INSTEXE" ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" + DeleteRegKey HKEY_CLASSES_ROOT "x-grid-info" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info" "" "URL:Hypergrid" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info" "URL Protocol" "" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\DefaultIcon" "" "$INSTDIR\$INSTEXE" + ;; URL param must be last item passed to viewer, it ignores subsequent params + ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif + WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-info\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" + DeleteRegKey HKEY_CLASSES_ROOT "x-grid-location-info" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "" "URL:Hypergrid legacy" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\"" ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" ;Create uninstaller diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index e1a0806f8..3610cab14 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -79,7 +79,7 @@ BOOL LLFloaterObjectIMInfo::postBuild() { getChild("Mute")->setCommitCallback(boost::bind(&LLFloaterObjectIMInfo::onClickMute, this)); getChild("OwnerName")->setClickedCallback(boost::bind(boost::ref(mGroupOwned) ? boost::bind(LLGroupActions::show, boost::ref(mOwnerID)) : boost::bind(show_avatar_profile, boost::ref(mOwnerID)))); - getChild("Slurl")->setClickedCallback(boost::bind(LLUrlAction::executeSLURL, boost::bind(std::plus(), "secondlife:///app/worldmap/", boost::ref(mSLurl)))); + getChild("Slurl")->setClickedCallback(boost::bind(LLUrlAction::executeSLURL, boost::bind(std::plus(), "secondlife:///app/worldmap/", boost::ref(mSLurl)), true)); return true; } diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index d22178377..e4523889a 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -1,5 +1,7 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** - * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @file llslurl.cpp (was llsimurlstring.cpp) * @brief Handles "SLURL fragments" like Ahern/123/45 for * startup processing, login screen, prefs, etc. * @@ -48,7 +50,8 @@ const char* LLSLURL::SLURL_COM = "slurl.com"; const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com"; const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com"; -const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_X_GRID_INFO_SCHEME = "x-grid-info"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; // <- deprecated! const char* LLSLURL::SLURL_APP_PATH = "app"; const char* LLSLURL::SLURL_REGION_PATH = "region"; const char* LLSLURL::SIM_LOCATION_HOME = "home"; @@ -214,7 +217,8 @@ LLSLURL::LLSLURL(const std::string& slurl) } else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || - (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_INFO_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) // deprecated legacy { // We're dealing with either a Standalone style slurl or slurl.com slurl if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || @@ -239,7 +243,7 @@ LLSLURL::LLSLURL(const std::string& slurl) // As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style // urls are properly formed, unlike the stinky maingrid style - mGrid = slurl_uri.hostName(); + mGrid = slurl_uri.hostNameAndPort(); } if (path_array.size() == 0) { @@ -266,7 +270,7 @@ LLSLURL::LLSLURL(const std::string& slurl) } else { - // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + // not a valid https/http/x-grid-info slurl, so it'll likely just be a URL return; } } @@ -304,7 +308,14 @@ LLSLURL::LLSLURL(const std::string& slurl) // at this point, head of the path array should be [ , , , ] where x, y and z // are collectively optional // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + + if(LLStringUtil::containsNonprintable(mRegion)) + { + LLStringUtil::stripNonprintable(mRegion); + } + path_array.erase(0); // parse the x, y, and optionally z @@ -313,11 +324,11 @@ LLSLURL::LLSLURL(const std::string& slurl) mPosition = LLVector3(path_array); // this construction handles LLSD without all components (values default to 0.f) if((F32(mPosition[VX]) < 0.f) || - (mPosition[VX] > 8192.f/*REGION_WIDTH_METERS*/) || + (mPosition[VX] > 8192.f) || (F32(mPosition[VY]) < 0.f) || - (mPosition[VY] > 8192.f/*REGION_WIDTH_METERS*/) || + (mPosition[VY] > 8192.f) || (F32(mPosition[VZ]) < 0.f) || - (mPosition[VZ] > 8192.f/*REGION_HEIGHT_METERS*/)) + (mPosition[VZ] > 8192.f)) { mType = INVALID; return; @@ -444,9 +455,9 @@ std::string LLSLURL::getSLURLString() const app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; else app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()) << "/" << mAppCmd; - for(LLSD::array_const_iterator i = mAppPath.beginArray(); + for(auto i = mAppPath.beginArray(); i != mAppPath.endArray(); - i++) + ++i) { app_url << "/" << i->asString(); } diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index b86cf7949..3beff26c7 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -44,6 +44,7 @@ public: static const char* WWW_SLURL_COM; static const char* SECONDLIFE_COM; static const char* MAPS_SECONDLIFE_COM; + static const char* SLURL_X_GRID_INFO_SCHEME; static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; static LLSLURL START_LOCATION; static const char* SIM_LOCATION_HOME; diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index b9535d36a..eb976171d 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -144,7 +144,7 @@ bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { const bool right_click = true; - LLMediaCtrl* web = NULL; + LLMediaCtrl* web = nullptr; const bool trusted_browser = false; return dispatchCore(slurl, "clicked", right_click, web, trusted_browser); } @@ -187,11 +187,18 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string LLPanelLogin::setLocation(slurl); return true; } + LLSLURL _slurl = slurl; + const std::string& grid = slurl.getGrid(); + const std::string& current_grid = gHippoGridManager->getCurrentGrid()->getGridName(); + if (grid != current_grid) + { + _slurl = LLSLURL(llformat("%s:%s", grid.c_str(), slurl.getRegion().c_str()), slurl.getPosition()); + } // Request a region handle by name LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, - slurl.getSLURLString(), + _slurl.getSLURLString(), LLUI::sConfigGroup->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } @@ -263,7 +270,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled -// to URL parsing and sim-fragment parsing +// to SLURL parsing and sim-fragment parsing + class LLTeleportHandler : public LLCommandHandler { public: @@ -272,8 +280,9 @@ public: // cause a constant teleport loop. JC LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_THROTTLE) { } + bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + LLMediaCtrl* web) override { // construct a "normal" SLURL, resolve the region to // a global position, and teleport to it @@ -287,12 +296,11 @@ public: tokens[3].asReal()); } - LLSD args; - args["LOCATION"] = tokens[0]; - // Region names may be %20 escaped. std::string region_name = LLURI::unescape(tokens[0]); + LLSD args; + args["LOCATION"] = region_name; LLSD payload; payload["region_name"] = region_name; @@ -351,7 +359,7 @@ bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl, bool trusted_content) { // *NOTE: Text editors are considered sources of trusted URLs // in order to make avatar profile links in chat history work. @@ -359,9 +367,9 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) // receiving resident will see it and must affirmatively // click on it. // *TODO: Make this trust model more refined. JC - const bool trusted_browser = true; - LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_browser); + + LLMediaCtrl* web = nullptr; + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_content); } diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index 82e1d3152..9b05260af 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -2,36 +2,29 @@ * @file llurldispatcher.h * @brief Central registry for all SL URL handlers * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LLURLDISPATCHER_H #define LLURLDISPATCHER_H - class LLMediaCtrl; @@ -49,6 +42,8 @@ public: // secondlife://RegionName/123/45/67/ // secondlife:///app/agent/3d6181b0-6a4b-97ef-18d8-722652995cf1/show // sl://app/foo/bar + // @param nav_type + // type of navigation type (see LLQtWebKit::LLWebPage::acceptNavigationRequest) // @param web // Pointer to LLMediaCtrl sending URL, can be NULL // @param trusted_browser @@ -58,7 +53,7 @@ public: static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& slurl); + static bool dispatchFromTextEditor(const std::string& slurl, bool trusted_content); }; #endif diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index a3e2c9f63..76d75d7c4 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -125,6 +125,9 @@ Klicken, um diese Position auf der Karte anzuzeigen Anklicken, um Befehl secondlife:// auszuführen + + Klicken, um eine E-Mail zu verfassen + Teleportieren zu diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index bc261fdcb..66e72db88 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -141,6 +141,7 @@ Make sure you entered the correct Login URI. An example of a Login URI is: \"htt Click to view this location on a map Click to run the secondlife:// command + Click to compose an email Teleport to diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 03b29be1e..ac9282a11 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -133,6 +133,9 @@ Asegurate que has ingresado el URI de inicio de sesión correcto. Un ejemplo de Pulsa para ver esta ubicación en el mapa Pulsa para ejecutar el comando secondlife:// + + Haz clic para redactar un correo electrónico + Teleportar a diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 143c4038b..ee9dc7f60 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -403,6 +403,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Clique para ativar no secondlife:// comando + + Clique para escrever um email + Teletransportar para @@ -427,6 +430,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Pedido de amizade + + Remoção de amigo + Fechar (⌘W) From 1317f26d042af7e0f97a12d399b44c9d88617409 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 09:24:44 -0500 Subject: [PATCH 050/332] Remove debug spew, my bad --- indra/llui/lltexteditor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index f1d99d0e7..9da1a3fc4 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -2224,7 +2224,6 @@ void LLTextEditor::copy(bool raw) const S32 label_length = (segment.getEnd() - segment.getStart()); const S32 start = (segment.getStart()+offset)-left_pos; const auto label = text.substr(start, label_length); - LL_INFOS() << "Our label is " << wstring_to_utf8str(label) << LL_ENDL; const auto wlink = utf8str_to_wstring(link); const auto pos = wlink.find(label); // Do not replace if normal link, or contains normal link (but may omit protocol) but ends the same way From 61b1128d76b5f42bf87feeeafe4a165ea952c08c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 09:32:13 -0500 Subject: [PATCH 051/332] Allow menu xmls from v3 that use context_menu tag instead of menu Just parse them as LLMenuGL for now, that's what they basically are. --- indra/llui/lluictrlfactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 9209d484f..11605bafd 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -412,13 +412,13 @@ LLMenuGL *LLUICtrlFactory::buildMenu(const std::string &filename, LLView* parent } // root must be called panel - if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" )) + if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" ) && !root->hasName("context_menu")) { LL_WARNS() << "Root node should be named menu bar or menu in : " << filename << LL_ENDL; return NULL; } - if (root->hasName("menu")) + if (root->hasName("menu") || root->hasName("context_menu")) { menu = (LLMenuGL*)LLMenuGL::fromXML(root, parentp, this); } From d8c95f844ffabeb0f21068ae6350a8f04c75275c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 09:40:56 -0500 Subject: [PATCH 052/332] Add url menus to the text editor!! Hey there, Alpha User! This commit is riddled with complex words, but here's what matters to you: This adds the ability to right click a Name, URL, SLURL, or Object Name, or even an Email, and get some awesome options just like v3 gets! Buuuut, unlike V3, if there was something else you might want to do, we've still got you covered in the same menu!! Also make the text editor's default menu an xml Synced a lot from upstream Unlike Upstream, actually combine the base menu with the url menu if we have a selection Unlike Upstream and previously, don't create a new menu per text editor Unlike Upstream, don't create a separate FriendSignal and ObjectBlockedSignal per text editor we need it in!! Unlike Upstream, always show a Select All option --- indra/llui/llmenugl.h | 1 + indra/llui/lltexteditor.cpp | 237 ++++++++++++------ indra/llui/lltexteditor.h | 23 +- indra/newview/llviewermenu.cpp | 6 + .../skins/default/xui/de/menu_url_agent.xml | 9 + .../skins/default/xui/de/menu_url_email.xml | 5 + .../default/xui/de/menu_url_experience.xml | 4 + .../skins/default/xui/de/menu_url_group.xml | 6 + .../skins/default/xui/de/menu_url_http.xml | 7 + .../default/xui/de/menu_url_inventory.xml | 6 + .../skins/default/xui/de/menu_url_map.xml | 6 + .../default/xui/de/menu_url_objectim.xml | 10 + .../skins/default/xui/de/menu_url_parcel.xml | 6 + .../skins/default/xui/de/menu_url_slapp.xml | 5 + .../skins/default/xui/de/menu_url_slurl.xml | 7 + .../default/xui/de/menu_url_teleport.xml | 6 + .../default/xui/en-us/menu_texteditor.xml | 21 ++ .../default/xui/en-us/menu_url_agent.xml | 53 ++++ .../default/xui/en-us/menu_url_agent_mini.xml | 32 +++ .../default/xui/en-us/menu_url_email.xml | 25 ++ .../default/xui/en-us/menu_url_experience.xml | 16 ++ .../default/xui/en-us/menu_url_group.xml | 32 +++ .../skins/default/xui/en-us/menu_url_http.xml | 41 +++ .../default/xui/en-us/menu_url_inventory.xml | 32 +++ .../skins/default/xui/en-us/menu_url_map.xml | 34 +++ .../default/xui/en-us/menu_url_objectim.xml | 62 +++++ .../default/xui/en-us/menu_url_parcel.xml | 34 +++ .../default/xui/en-us/menu_url_slapp.xml | 25 ++ .../default/xui/en-us/menu_url_slurl.xml | 41 +++ .../default/xui/en-us/menu_url_teleport.xml | 34 +++ .../skins/default/xui/es/menu_url_agent.xml | 9 + .../skins/default/xui/es/menu_url_email.xml | 5 + .../default/xui/es/menu_url_experience.xml | 4 + .../skins/default/xui/es/menu_url_group.xml | 6 + .../skins/default/xui/es/menu_url_http.xml | 7 + .../default/xui/es/menu_url_inventory.xml | 6 + .../skins/default/xui/es/menu_url_map.xml | 6 + .../default/xui/es/menu_url_objectim.xml | 10 + .../skins/default/xui/es/menu_url_parcel.xml | 6 + .../skins/default/xui/es/menu_url_slapp.xml | 5 + .../skins/default/xui/es/menu_url_slurl.xml | 7 + .../default/xui/es/menu_url_teleport.xml | 6 + .../skins/default/xui/fr/menu_url_agent.xml | 9 + .../skins/default/xui/fr/menu_url_email.xml | 5 + .../default/xui/fr/menu_url_experience.xml | 4 + .../skins/default/xui/fr/menu_url_group.xml | 6 + .../skins/default/xui/fr/menu_url_http.xml | 7 + .../default/xui/fr/menu_url_inventory.xml | 6 + .../skins/default/xui/fr/menu_url_map.xml | 6 + .../default/xui/fr/menu_url_objectim.xml | 10 + .../skins/default/xui/fr/menu_url_parcel.xml | 6 + .../skins/default/xui/fr/menu_url_slapp.xml | 5 + .../skins/default/xui/fr/menu_url_slurl.xml | 7 + .../default/xui/fr/menu_url_teleport.xml | 6 + .../skins/default/xui/it/menu_url_agent.xml | 9 + .../skins/default/xui/it/menu_url_email.xml | 5 + .../default/xui/it/menu_url_experience.xml | 4 + .../skins/default/xui/it/menu_url_group.xml | 6 + .../skins/default/xui/it/menu_url_http.xml | 7 + .../default/xui/it/menu_url_inventory.xml | 6 + .../skins/default/xui/it/menu_url_map.xml | 6 + .../default/xui/it/menu_url_objectim.xml | 10 + .../skins/default/xui/it/menu_url_parcel.xml | 6 + .../skins/default/xui/it/menu_url_slapp.xml | 5 + .../skins/default/xui/it/menu_url_slurl.xml | 7 + .../default/xui/it/menu_url_teleport.xml | 6 + .../skins/default/xui/pt/menu_url_agent.xml | 9 + .../skins/default/xui/pt/menu_url_email.xml | 5 + .../default/xui/pt/menu_url_experience.xml | 4 + .../skins/default/xui/pt/menu_url_group.xml | 6 + .../skins/default/xui/pt/menu_url_http.xml | 7 + .../default/xui/pt/menu_url_inventory.xml | 6 + .../skins/default/xui/pt/menu_url_map.xml | 6 + .../default/xui/pt/menu_url_objectim.xml | 10 + .../skins/default/xui/pt/menu_url_parcel.xml | 6 + .../skins/default/xui/pt/menu_url_slapp.xml | 5 + .../skins/default/xui/pt/menu_url_slurl.xml | 7 + .../default/xui/pt/menu_url_teleport.xml | 6 + 78 files changed, 1055 insertions(+), 79 deletions(-) create mode 100644 indra/newview/skins/default/xui/de/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/de/menu_url_teleport.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_texteditor.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_agent_mini.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_url_teleport.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/es/menu_url_teleport.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_url_teleport.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/it/menu_url_teleport.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_agent.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_email.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_experience.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_group.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_http.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_inventory.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_map.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_objectim.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_parcel.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_slapp.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_slurl.xml create mode 100644 indra/newview/skins/default/xui/pt/menu_url_teleport.xml diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 4c5811321..7c3626d35 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -580,6 +580,7 @@ public: void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; } bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; } protected: + friend class LLTextEditor; void createSpilloverBranch(); void cleanupSpilloverBranch(); // Add the menu item to this menu. diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 9da1a3fc4..0062eddc8 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -65,6 +65,7 @@ #include "lltextparser.h" #include "lldir.h" #include +#include "llmemberlistener.h" #include "llmenugl.h" #include "../newview/lgghunspell_wrapper.h" @@ -243,6 +244,8 @@ private: /////////////////////////////////////////////////////////////////// +LLTextEditor::is_friend_signal_t* LLTextEditor::mIsFriendSignal = nullptr; +LLTextEditor::is_blocked_signal_t* LLTextEditor::mIsObjectBlockedSignal = nullptr; LLTextEditor::LLTextEditor( const std::string& name, @@ -345,32 +348,6 @@ LLTextEditor::LLTextEditor( resetDirty(); // Update saved text state - // make the popup menu available - //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", parent_view); - LLMenuGL* menu = new LLMenuGL("rclickmenu"); - /*if (!menu) - { - menu = new LLMenuGL(LLStringUtil::null); - }*/ - menu->addChild(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Copy Raw", [](void* data) { - if (LLTextEditor* line = static_cast(data)) line->copyRaw(); - }, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Copy URL", [](void* data) { - if (std::string* str = static_cast(data)) - { - auto url = utf8str_to_wstring(*str); - gClipboard.copyFromSubstring(url, 0, url.size()); - } - }, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); - menu->addSeparator(); - menu->setCanTearOff(FALSE); - menu->setVisible(FALSE); - mPopupMenuHandle = menu->getHandle(); setCommitCallback(boost::bind(&LLTextEditor::setControlValue, this, _2)); } @@ -387,19 +364,76 @@ LLTextEditor::~LLTextEditor() // Scrollbar is deleted by LLView std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - //LLView::deleteViewByHandle(mPopupMenuHandle); + mSegments.clear(); + auto menu = mPopupMenuHandle.get(); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + /* Singu Note: Static this, we'll use it wherever we can! + delete mIsFriendSignal; + delete mIsObjectBlockedSignal;*/ } -void LLTextEditor::context_cut(void* data) + +const std::string& LLTextEditor::getMenuSegmentUrl() const { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->cut(); + auto segment = getSegmentAtLocalPos(mLastContextMenuX, mLastContextMenuY); + auto style = segment->getStyle(); + return style ? style->getLinkHREF() : LLStringUtil::null; } -void LLTextEditor::context_copy(void* data) + +static LLTextEditor* get_focused_text_editor() { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->copy(); + auto* list = dynamic_cast(gFocusMgr.getKeyboardFocus()); + llassert(list); // This listener only applies to lists + return list; } +class ContextText : public LLMemberListener +{ + bool handleEvent(LLPointer, const LLSD& userdata) override + { + auto text = get_focused_text_editor(); + const auto& op = userdata.asStringRef(); + if (op == "Cut") text->cut(); + else if (op == "Copy") text->copy(); + else if (op == "CopyRaw") text->copyRaw(); + else if (op == "Paste") text->paste(); + else if (op == "Delete") text->doDelete(); + else if (op == "SelectAll") text->selectAll(); + return true; + } +}; + +class ContextUrl : public LLMemberListener +{ + static const std::string& get_focused_url() + { + return get_focused_text_editor()->getMenuSegmentUrl(); + } + bool handleEvent(LLPointer, const LLSD& userdata) override + { + const auto& url = get_focused_url(); + const auto& op = userdata.asStringRef(); + if (op == "Open") LLUrlAction::openURL(url); + else if (op == "OpenInternal") LLUrlAction::openURLInternal(url); + else if (op == "OpenExternal") LLUrlAction::openURLExternal(url); + else if (op == "Execute") LLUrlAction::executeSLURL(url, true); + else if (op == "Block") LLUrlAction::blockObject(url); + else if (op == "Unblock") LLUrlAction::unblockObject(url); + else if (op == "Teleport") LLUrlAction::teleportToLocation(url); + else if (op == "ShowProfile") LLUrlAction::showProfile(url); + else if (op == "AddFriend") LLUrlAction::addFriend(url); + else if (op == "RemoveFriend") LLUrlAction::removeFriend(url); + else if (op == "SendIM") LLUrlAction::sendIM(url); + else if (op == "ShowOnMap") LLUrlAction::showLocationOnMap(url); + else if (op == "CopyLabel") LLUrlAction::copyLabelToClipboard(url); + else if (op == "CopyUrl") LLUrlAction::copyURLToClipboard(url); + return true; + } +}; + void LLTextEditor::spell_correct(void* data) { @@ -477,21 +511,11 @@ void LLTextEditor::spell_add(void* data) } } - -void LLTextEditor::context_paste(void* data) +//static +void LLTextEditor::addMenuListeners() { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->paste(); -} -void LLTextEditor::context_delete(void* data) -{ - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->doDelete(); -} -void LLTextEditor::context_selectall(void* data) -{ - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->selectAll(); + (new ContextText)->registerListener(LLMenuGL::sMenuContainer, "Text"); + (new ContextUrl)->registerListener(LLMenuGL::sMenuContainer, "Text.Url"); } void LLTextEditor::setTrackColor( const LLColor4& color ) @@ -651,6 +675,56 @@ BOOL LLTextEditor::truncate() return did_truncate; } +LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) +{ + // work out the XUI menu file to use for this url + LLUrlMatch match; + std::string url = in_url; + if (!LLUrlRegistry::instance().findUrl(url, match)) + { + return nullptr; + } + + std::string xui_file = match.getMenuName(); + if (xui_file.empty()) + { + return nullptr; + } + + // create and return the context menu from the XUI file + llassert(LLMenuGL::sMenuContainer != NULL); + auto menu = LLUICtrlFactory::getInstance()->buildMenu(xui_file, LLMenuGL::sMenuContainer); + if (menu) + { + if (mIsFriendSignal) + { + bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); + LLView* addFriendButton = menu->findChild("add_friend"); + LLView* removeFriendButton = menu->findChild("remove_friend"); + + if (addFriendButton && removeFriendButton) + { + addFriendButton->setEnabled(!isFriend); + removeFriendButton->setEnabled(isFriend); + } + } + + if (mIsObjectBlockedSignal) + { + bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); + LLView* blockButton = menu->findChild("block_object"); + LLView* unblockButton = menu->findChild("unblock_object"); + + if (blockButton && unblockButton) + { + blockButton->setVisible(!is_blocked); + unblockButton->setVisible(is_blocked); + } + } + } + return menu; +} + void LLTextEditor::setText(const LLStringExplicit &utf8str) { // clear out the existing text and segments @@ -1382,6 +1456,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) { + setFocus(TRUE); //setCursorAtLocalPos( x, y, TRUE ); @@ -1392,43 +1467,56 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); if (menu) { - for(int i = 0;i<(int)suggestionMenuItems.size();i++) + for (auto tempBind : suggestionMenuItems) { - SpellMenuBind * tempBind = suggestionMenuItems[i]; - if(tempBind) + if (tempBind) { menu->removeChild(tempBind->menuItem); tempBind->menuItem->die(); - //delete tempBind->menuItem; - //tempBind->menuItem = NULL; delete tempBind; } } suggestionMenuItems.clear(); + menu->die(); + } + + auto segment = getSegmentAtLocalPos(x, y); + const LLStyleSP style = segment ? segment->getStyle() : nullptr; + auto submenu = (style && style->isLink()) ? createUrlContextMenu(x, y, style->getLinkHREF()) : nullptr; + // Add url menu to base menu if we have a selection, otherwise make it the menu. + menu = (submenu && !hasSelection()) ? submenu : LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", LLMenuGL::sMenuContainer); + mPopupMenuHandle = menu->getHandle(); + if (menu) + { + if (submenu && submenu != menu) + { + submenu->removeChild(submenu->getChild("Select All")); // There can be only one! + menu->appendMenu(submenu); + } // spell_check="true" in xui - menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable); if (!mReadOnly && mSpellCheckable) { bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen); if (is_word_part) { const LLWString &text = mWText; - std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen)); + std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart, wordLen)); if (!glggHunSpell->isSpelledRight(selectedWord)) { //misspelled word here, and you have just right clicked on it! std::vector suggs = glggHunSpell->getSuggestionList(selectedWord); - for (int i = 0; i<(int)suggs.size(); i++) + menu->addSeparator(); + for (auto word : suggs) { SpellMenuBind * tempStruct = new SpellMenuBind; tempStruct->origin = this; - tempStruct->word = suggs[i]; + tempStruct->word = word; tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - tempStruct->wordY=y; + tempStruct->wordPositionStart = wordStart; + tempStruct->wordY = y; LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( tempStruct->word, spell_correct, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; @@ -1439,8 +1527,8 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) tempStruct->origin = this; tempStruct->word = selectedWord; tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - tempStruct->wordY=y; + tempStruct->wordPositionStart = wordStart; + tempStruct->wordY = y; LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( "Add Word", spell_add, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; @@ -1467,17 +1555,6 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) menu->addChild(suggMenuItem); } - { - const LLStyleSP style = mHoverSegment ? mHoverSegment->getStyle() : nullptr; - auto copy_url = menu->findChild("Copy URL"); - if (mReadOnly && mParseHTML && style && !style->getLinkHREF().empty()) - { - copy_url->setVisible(true); - copy_url->setUserData((void*)&style->getLinkHREF()); // Yes, this can be invalidated, but they'll never click it then. - } - else copy_url->setVisible(false); - } - mLastContextMenuX = x; mLastContextMenuY = y; menu->buildDrawLabels(); @@ -4975,6 +5052,24 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer ) return TRUE; } +boost::signals2::connection LLTextEditor::setIsFriendCallback(const is_friend_signal_t::slot_type& cb) +{ + if (!mIsFriendSignal) + { + mIsFriendSignal = new is_friend_signal_t(); + } + return mIsFriendSignal->connect(cb); +} + +boost::signals2::connection LLTextEditor::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb) +{ + if (!mIsObjectBlockedSignal) + { + mIsObjectBlockedSignal = new is_blocked_signal_t(); + } + return mIsObjectBlockedSignal->connect(cb); +} + ////////////////////////////////////////////////////////////////////////// // LLTextSegment diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index a0755f067..66df57ad0 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -72,8 +72,15 @@ public: virtual ~LLTextEditor(); - typedef boost::signals2::signal keystroke_signal_t; + const std::string& getMenuSegmentUrl() const; + typedef boost::signals2::signal keystroke_signal_t; + typedef boost::signals2::signal is_friend_signal_t; + typedef boost::signals2::signal is_blocked_signal_t; + + static boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb); + static boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb); + static void addMenuListeners(); void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback); virtual LLXMLNodePtr getXML(bool save_children = true) const; @@ -152,12 +159,6 @@ public: virtual BOOL canSelectAll() const; virtual void deselect(); virtual BOOL canDeselect() const; - static void context_cut(void* data); - - static void context_copy(void* data); - static void context_paste(void* data); - static void context_delete(void* data); - static void context_selectall(void* data); static void spell_correct(void* data); static void spell_add(void* data); static void spell_show(void* data); @@ -270,7 +271,9 @@ public: LLSD getValue() const; const std::string& getText() const; - + + LLMenuGL* createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url + // Non-undoable void setText(const LLStringExplicit &utf8str); void setWText(const LLWString &wtext); @@ -468,6 +471,10 @@ protected: void (*mOnScrollEndCallback)(void*); void *mOnScrollEndData; + // Used to check if user with given ID is avatar's friend + static is_friend_signal_t* mIsFriendSignal; + static is_blocked_signal_t* mIsObjectBlockedSignal; + LLWString mPreeditWString; LLWString mPreeditOverwrittenWString; std::vector mPreeditPositions; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e23353a47..aee2075ed 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -127,6 +127,7 @@ #include "llfloaternotificationsconsole.h" // +#include "lltexteditor.h" // Initialize the text editor menu listeners in here #include "llfloatermessagelog.h" #include "shfloatermediaticker.h" #include "llpacketring.h" @@ -9604,6 +9605,11 @@ void initialize_menus() add_radar_listeners(); + // Text Editor menus + LLTextEditor::setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); + LLTextEditor::setIsFriendCallback(LLAvatarActions::isFriend); + LLTextEditor::addMenuListeners(); + class LLViewBuildMode : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) diff --git a/indra/newview/skins/default/xui/de/menu_url_agent.xml b/indra/newview/skins/default/xui/de/menu_url_agent.xml new file mode 100644 index 000000000..ddb072ba8 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_agent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_email.xml b/indra/newview/skins/default/xui/de/menu_url_email.xml new file mode 100644 index 000000000..3d8e61bc8 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_experience.xml b/indra/newview/skins/default/xui/de/menu_url_experience.xml new file mode 100644 index 000000000..45c118cb6 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_experience.xml @@ -0,0 +1,4 @@ + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_group.xml b/indra/newview/skins/default/xui/de/menu_url_group.xml new file mode 100644 index 000000000..6bd86414b --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_group.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_http.xml b/indra/newview/skins/default/xui/de/menu_url_http.xml new file mode 100644 index 000000000..30eb1668a --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_http.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_inventory.xml b/indra/newview/skins/default/xui/de/menu_url_inventory.xml new file mode 100644 index 000000000..dc069df02 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_inventory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_map.xml b/indra/newview/skins/default/xui/de/menu_url_map.xml new file mode 100644 index 000000000..2f6ffcd45 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_objectim.xml b/indra/newview/skins/default/xui/de/menu_url_objectim.xml new file mode 100644 index 000000000..95fe97407 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_objectim.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_parcel.xml b/indra/newview/skins/default/xui/de/menu_url_parcel.xml new file mode 100644 index 000000000..9169bca24 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_parcel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_slapp.xml b/indra/newview/skins/default/xui/de/menu_url_slapp.xml new file mode 100644 index 000000000..72e916b90 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_slapp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_slurl.xml b/indra/newview/skins/default/xui/de/menu_url_slurl.xml new file mode 100644 index 000000000..5d48230eb --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_slurl.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/de/menu_url_teleport.xml b/indra/newview/skins/default/xui/de/menu_url_teleport.xml new file mode 100644 index 000000000..4cc1ecc70 --- /dev/null +++ b/indra/newview/skins/default/xui/de/menu_url_teleport.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_texteditor.xml b/indra/newview/skins/default/xui/en-us/menu_texteditor.xml new file mode 100644 index 000000000..425eef2d4 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_texteditor.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_agent.xml b/indra/newview/skins/default/xui/en-us/menu_url_agent.xml new file mode 100644 index 000000000..96f771d43 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_agent.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_agent_mini.xml b/indra/newview/skins/default/xui/en-us/menu_url_agent_mini.xml new file mode 100644 index 000000000..5a0b4f9ce --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_agent_mini.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_email.xml b/indra/newview/skins/default/xui/en-us/menu_url_email.xml new file mode 100644 index 000000000..cfd91a65d --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_email.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_experience.xml b/indra/newview/skins/default/xui/en-us/menu_url_experience.xml new file mode 100644 index 000000000..76d91ca83 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_experience.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_group.xml b/indra/newview/skins/default/xui/en-us/menu_url_group.xml new file mode 100644 index 000000000..1abc9ecb3 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_group.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_http.xml b/indra/newview/skins/default/xui/en-us/menu_url_http.xml new file mode 100644 index 000000000..ed2d71435 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_http.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_inventory.xml b/indra/newview/skins/default/xui/en-us/menu_url_inventory.xml new file mode 100644 index 000000000..d74067a71 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_inventory.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_map.xml b/indra/newview/skins/default/xui/en-us/menu_url_map.xml new file mode 100644 index 000000000..b7314726f --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_map.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_objectim.xml b/indra/newview/skins/default/xui/en-us/menu_url_objectim.xml new file mode 100644 index 000000000..1b4317ff9 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_objectim.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_parcel.xml b/indra/newview/skins/default/xui/en-us/menu_url_parcel.xml new file mode 100644 index 000000000..f727631a2 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_parcel.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_slapp.xml b/indra/newview/skins/default/xui/en-us/menu_url_slapp.xml new file mode 100644 index 000000000..fd5241947 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_slapp.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_slurl.xml b/indra/newview/skins/default/xui/en-us/menu_url_slurl.xml new file mode 100644 index 000000000..0b0f5fd92 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_slurl.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_url_teleport.xml b/indra/newview/skins/default/xui/en-us/menu_url_teleport.xml new file mode 100644 index 000000000..1f51e0ba8 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/menu_url_teleport.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_agent.xml b/indra/newview/skins/default/xui/es/menu_url_agent.xml new file mode 100644 index 000000000..610d65465 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_agent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_email.xml b/indra/newview/skins/default/xui/es/menu_url_email.xml new file mode 100644 index 000000000..fa9c74077 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_experience.xml b/indra/newview/skins/default/xui/es/menu_url_experience.xml new file mode 100644 index 000000000..a18cb0e8e --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_experience.xml @@ -0,0 +1,4 @@ + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_group.xml b/indra/newview/skins/default/xui/es/menu_url_group.xml new file mode 100644 index 000000000..79374b973 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_group.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_http.xml b/indra/newview/skins/default/xui/es/menu_url_http.xml new file mode 100644 index 000000000..585c059ff --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_http.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_inventory.xml b/indra/newview/skins/default/xui/es/menu_url_inventory.xml new file mode 100644 index 000000000..13a8711c7 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_inventory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_map.xml b/indra/newview/skins/default/xui/es/menu_url_map.xml new file mode 100644 index 000000000..f96a0c717 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_objectim.xml b/indra/newview/skins/default/xui/es/menu_url_objectim.xml new file mode 100644 index 000000000..51803e93c --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_objectim.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_parcel.xml b/indra/newview/skins/default/xui/es/menu_url_parcel.xml new file mode 100644 index 000000000..9e789ef8e --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_parcel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_slapp.xml b/indra/newview/skins/default/xui/es/menu_url_slapp.xml new file mode 100644 index 000000000..7147dcd3c --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_slapp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_slurl.xml b/indra/newview/skins/default/xui/es/menu_url_slurl.xml new file mode 100644 index 000000000..4ab47c2f6 --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_slurl.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/es/menu_url_teleport.xml b/indra/newview/skins/default/xui/es/menu_url_teleport.xml new file mode 100644 index 000000000..8f86a91be --- /dev/null +++ b/indra/newview/skins/default/xui/es/menu_url_teleport.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_agent.xml b/indra/newview/skins/default/xui/fr/menu_url_agent.xml new file mode 100644 index 000000000..2c7443c6c --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_agent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_email.xml b/indra/newview/skins/default/xui/fr/menu_url_email.xml new file mode 100644 index 000000000..a86128bc0 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_experience.xml b/indra/newview/skins/default/xui/fr/menu_url_experience.xml new file mode 100644 index 000000000..90c0ce676 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_experience.xml @@ -0,0 +1,4 @@ + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_group.xml b/indra/newview/skins/default/xui/fr/menu_url_group.xml new file mode 100644 index 000000000..de90c3ff7 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_group.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_http.xml b/indra/newview/skins/default/xui/fr/menu_url_http.xml new file mode 100644 index 000000000..5e9635299 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_http.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_inventory.xml b/indra/newview/skins/default/xui/fr/menu_url_inventory.xml new file mode 100644 index 000000000..8ab88b4be --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_inventory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_map.xml b/indra/newview/skins/default/xui/fr/menu_url_map.xml new file mode 100644 index 000000000..67e6986f5 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_objectim.xml b/indra/newview/skins/default/xui/fr/menu_url_objectim.xml new file mode 100644 index 000000000..95310dd1f --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_objectim.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_parcel.xml b/indra/newview/skins/default/xui/fr/menu_url_parcel.xml new file mode 100644 index 000000000..07b0eeca4 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_parcel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_slapp.xml b/indra/newview/skins/default/xui/fr/menu_url_slapp.xml new file mode 100644 index 000000000..f4b7e212c --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_slapp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_slurl.xml b/indra/newview/skins/default/xui/fr/menu_url_slurl.xml new file mode 100644 index 000000000..e44943cf1 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_slurl.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_url_teleport.xml b/indra/newview/skins/default/xui/fr/menu_url_teleport.xml new file mode 100644 index 000000000..a5075a274 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/menu_url_teleport.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_agent.xml b/indra/newview/skins/default/xui/it/menu_url_agent.xml new file mode 100644 index 000000000..e5c20fb9e --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_agent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_email.xml b/indra/newview/skins/default/xui/it/menu_url_email.xml new file mode 100644 index 000000000..a8ef2cde9 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_experience.xml b/indra/newview/skins/default/xui/it/menu_url_experience.xml new file mode 100644 index 000000000..d6ea3f352 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_experience.xml @@ -0,0 +1,4 @@ + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_group.xml b/indra/newview/skins/default/xui/it/menu_url_group.xml new file mode 100644 index 000000000..d9a792ebd --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_group.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_http.xml b/indra/newview/skins/default/xui/it/menu_url_http.xml new file mode 100644 index 000000000..e0153d916 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_http.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_inventory.xml b/indra/newview/skins/default/xui/it/menu_url_inventory.xml new file mode 100644 index 000000000..a45a6bbc1 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_inventory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_map.xml b/indra/newview/skins/default/xui/it/menu_url_map.xml new file mode 100644 index 000000000..cb7935b3e --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_objectim.xml b/indra/newview/skins/default/xui/it/menu_url_objectim.xml new file mode 100644 index 000000000..67bc34a5d --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_objectim.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_parcel.xml b/indra/newview/skins/default/xui/it/menu_url_parcel.xml new file mode 100644 index 000000000..a032c3358 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_parcel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_slapp.xml b/indra/newview/skins/default/xui/it/menu_url_slapp.xml new file mode 100644 index 000000000..b368aa914 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_slapp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_slurl.xml b/indra/newview/skins/default/xui/it/menu_url_slurl.xml new file mode 100644 index 000000000..355c7dbed --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_slurl.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/it/menu_url_teleport.xml b/indra/newview/skins/default/xui/it/menu_url_teleport.xml new file mode 100644 index 000000000..c129e61f2 --- /dev/null +++ b/indra/newview/skins/default/xui/it/menu_url_teleport.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_agent.xml b/indra/newview/skins/default/xui/pt/menu_url_agent.xml new file mode 100644 index 000000000..102df18b2 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_agent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_email.xml b/indra/newview/skins/default/xui/pt/menu_url_email.xml new file mode 100644 index 000000000..1dbb5cd99 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_email.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_experience.xml b/indra/newview/skins/default/xui/pt/menu_url_experience.xml new file mode 100644 index 000000000..af7634b0d --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_experience.xml @@ -0,0 +1,4 @@ + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_group.xml b/indra/newview/skins/default/xui/pt/menu_url_group.xml new file mode 100644 index 000000000..5b67a69c9 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_group.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_http.xml b/indra/newview/skins/default/xui/pt/menu_url_http.xml new file mode 100644 index 000000000..e53a2572b --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_http.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_inventory.xml b/indra/newview/skins/default/xui/pt/menu_url_inventory.xml new file mode 100644 index 000000000..45c14355d --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_inventory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_map.xml b/indra/newview/skins/default/xui/pt/menu_url_map.xml new file mode 100644 index 000000000..ba114ccca --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_objectim.xml b/indra/newview/skins/default/xui/pt/menu_url_objectim.xml new file mode 100644 index 000000000..0ebb358b1 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_objectim.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_parcel.xml b/indra/newview/skins/default/xui/pt/menu_url_parcel.xml new file mode 100644 index 000000000..6cc668bfd --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_parcel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_slapp.xml b/indra/newview/skins/default/xui/pt/menu_url_slapp.xml new file mode 100644 index 000000000..d0784149a --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_slapp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_slurl.xml b/indra/newview/skins/default/xui/pt/menu_url_slurl.xml new file mode 100644 index 000000000..7216ccf0b --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_slurl.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/pt/menu_url_teleport.xml b/indra/newview/skins/default/xui/pt/menu_url_teleport.xml new file mode 100644 index 000000000..f00742564 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/menu_url_teleport.xml @@ -0,0 +1,6 @@ + + + + + + From f9fe8a056a3ee3c7ea178d15eb2749accb44a8d1 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 12:12:27 -0500 Subject: [PATCH 053/332] Remove duplicate and old/invalid code from llviewertexteditor.cpp --- indra/newview/llviewertexteditor.cpp | 262 +++------------------------ 1 file changed, 26 insertions(+), 236 deletions(-) diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 404dce4bf..8097f6e13 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -587,26 +587,11 @@ LLViewerTextEditor::LLViewerTextEditor(const std::string& name, { mEmbeddedItemList = new LLEmbeddedItems(this); mInventoryCallback->setEditor(this); - - // *TODO: Add right click menus for SLURLs - // Build the right click menu - // make the popup menu available - - //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_slurl.xml", this); - //if (!menu) - //{ - // menu = new LLMenuGL(LLStringUtil::null); - //} - //menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor")); - //// menu->setVisible(FALSE); - //mPopupMenuHandle = menu->getHandle(); } LLViewerTextEditor::~LLViewerTextEditor() { delete mEmbeddedItemList; - - // The inventory callback may still be in use by gInventoryCallbackManager... // so set its reference to this to null. mInventoryCallback->setEditor(NULL); @@ -673,6 +658,7 @@ BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* s return TRUE; } +// Singu TODO: This is mostly duplicated from LLTextEditor BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -795,114 +781,28 @@ BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if (!mDragItem) + if (!mIsSelecting && mDragItem && hasMouseCapture()) { - // leave hover segment active during drag and drop - mHoverSegment = NULL; - } - if(hasMouseCapture() ) - { - if( mIsSelecting ) + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } + LLToolDragAndDrop::getInstance()->beginDrag( + LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ), + mDragItem->getUUID(), + LLToolDragAndDrop::SOURCE_NOTECARD, + getSourceID(), mObjectID); - if( y > getTextRect().mTop ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); - } - else - if( y < getTextRect().mBottom ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); - } - - setCursorAtLocalPos( x, y, TRUE ); - mSelectionEnd = mCursorPos; - - updateScrollFromCursor(); - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else if( mDragItem ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLToolDragAndDrop::getInstance()->beginDrag( - LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ), - mDragItem->getUUID(), - LLToolDragAndDrop::SOURCE_NOTECARD, - getSourceID(), mObjectID); - - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - getWindow()->setCursor(UI_CURSOR_HAND); + return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); } + getWindow()->setCursor(UI_CURSOR_HAND); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; handled = TRUE; } - if( !handled ) - { - // Pass to children - handled = LLView::childrenHandleHover(x, y, mask) != NULL; - } - - if( handled ) - { - // Delay cursor flashing - resetKeystrokeTimer(); - } - - // Opaque - if( !handled && mTakesNonScrollClicks) - { - // Check to see if we're over an HTML-style link - if( !mSegments.empty() ) - { - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) - { - if(cur_segment->getStyle()->isLink()) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (over link, inactive)" << LL_ENDL; - getWindow()->setCursor(UI_CURSOR_HAND); - handled = TRUE; - } - else - if(cur_segment->getStyle()->getIsEmbeddedItem()) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (over embedded item, inactive)" << LL_ENDL; - getWindow()->setCursor(UI_CURSOR_HAND); - //getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; - } - mHoverSegment = cur_segment; - } - } - - if( !handled ) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - if (!mScrollbar->getVisible() || x < getRect().getWidth() - SCROLLBAR_SIZE) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - } - handled = TRUE; - } - } - - return handled; + return handled || LLTextEditor::handleHover(x, y, mask); } @@ -946,125 +846,36 @@ BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) BOOL LLViewerTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - if(!handled)handled = LLTextEditor::handleRightMouseDown(x, y, mask); - - // *TODO: Add right click menus for SLURLs -// if(! handled) -// { -// const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); -// if( cur_segment ) -// { -// if(cur_segment->getStyle()->isLink()) -// { -// handled = TRUE; -// mHTML = cur_segment->getStyle()->getLinkHREF(); -// } -// } -// } -// LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); -// if(handled && menu && mParseHTML && mHTML.length() > 0) -// { -// menu->setVisible(TRUE); -// menu->arrange(); -// menu->updateParent(LLMenuGL::sMenuContainer); -// LLMenuGL::showPopup(this, menu, x, y); -// mHTML = ""; -// } -// else -// { -// if(menu && menu->getVisible()) -// { -// menu->setVisible(FALSE); -// } -// } - return handled; + return childrenHandleRightMouseDown(x, y, mask) != NULL || LLTextEditor::handleRightMouseDown(x, y, mask); } BOOL LLViewerTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; - handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; - if (!handled) - { - handled = LLTextEditor::handleMiddleMouseDown(x, y, mask); - } - return handled; + return childrenHandleMiddleMouseDown(x, y, mask) != NULL || LLTextEditor::handleMiddleMouseDown(x, y, mask); } BOOL LLViewerTextEditor::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMiddleMouseUp(x, y, mask) != NULL; - - return handled; + return childrenHandleMiddleMouseUp(x, y, mask) != NULL || LLTextEditor::handleMiddleMouseUp(x, y, mask); } BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; - - // let scrollbar have first dibs - handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; - - if( !handled && mTakesNonScrollClicks) + if (mTakesNonScrollClicks && allowsEmbeddedItems()) { - if( allowsEmbeddedItems() ) + const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->getStyle()->getIsEmbeddedItem()) { - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment && cur_segment->getStyle()->getIsEmbeddedItem() ) + if( openEmbeddedItemAtPos(cur_segment->getStart())) { - if( openEmbeddedItemAtPos( cur_segment->getStart() ) ) - { - deselect(); - setFocus( FALSE ); - return TRUE; - } + deselect(); + setFocus(FALSE); + return TRUE; } } - - - setCursorAtLocalPos( x, y, FALSE ); - deselect(); - - const LLWString &text = getWText(); - - if( isPartOfWord( text[mCursorPos] ) ) - { - // Select word the cursor is over - while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1])) - { - mCursorPos--; - } - startSelection(); - - while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) ) - { - mCursorPos++; - } - - mSelectionEnd = mCursorPos; - } - else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) ) - { - // Select the character the cursor is over - startSelection(); - mCursorPos++; - mSelectionEnd = mCursorPos; - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection here. - mIsSelecting = FALSE; - - // delay cursor flashing - resetKeystrokeTimer(); - - // take selection to 'primary' clipboard - updatePrimary(); - - handled = TRUE; } - return handled; + + return handleDoubleClick(x, y, mask); } @@ -1244,7 +1055,6 @@ void LLViewerTextEditor::setEmbeddedText(const std::string& instr) std::string LLViewerTextEditor::getEmbeddedText() { -#if 1 // New version (Version 2) mEmbeddedItemList->copyUsedCharsToIndexed(); LLWString outtextw; @@ -1260,26 +1070,6 @@ std::string LLViewerTextEditor::getEmbeddedText() } std::string outtext = wstring_to_utf8str(outtextw); return outtext; -#else - // Old version (Version 1) - mEmbeddedItemList->copyUsedCharsToIndexed(); - std::string outtext; - for (S32 i=0; i<(S32)mWText.size(); i++) - { - llwchar wch = mWText[i]; - if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) - { - S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch); - wch = 0x80 | index % 128; - } - else if (wch >= 0x80) - { - wch = LL_UNKNOWN_CHAR; - } - outtext.push_back((U8)wch); - } - return outtext; -#endif } std::string LLViewerTextEditor::appendTime(bool prepend_newline) From 413c24bde85775b935f2331da10e5f4998ca4bec Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 14:23:43 -0500 Subject: [PATCH 054/332] Bug Fix/Feature: Make the spam of radar on open/close and login optional Adds RadarAlertFlood and RadarAlertFloodLeaving RadarAlertFloodLeaving requires RadarAlertFlood Radar uses new announcements for these events. These settings are off by default, Find them in your local debug settings! --- indra/newview/app_settings/settings.xml | 22 +++++++++++++ indra/newview/llfloateravatarlist.cpp | 31 ++++++++++++------- indra/newview/llfloateravatarlist.h | 8 ++--- .../skins/default/xui/en-us/strings.xml | 2 ++ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 15b0a927a..dbaa6406e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7790,6 +7790,28 @@ This should be as low as possible, but too low may break functionality Value 1 + RadarAlertFlood + + Comment + Show the flood of people arriving and (if RadarAlertFloodLeaving) leaving when you login and when you open/close the Radar with RadarKeepOpen off. + Persist + 1 + Type + Boolean + Value + 0 + + RadarAlertFloodLeaving + + Comment + When activated along with RadarAlertFlood shows the flood of people leaving when you close the Radar with RadarKeepOpen off. + Persist + 1 + Type + Boolean + Value + 0 + RadarAlertShowDist Comment diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index 87c742e47..7c1c0e096 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -70,7 +70,7 @@ const S32& radar_namesystem() namespace { - void chat_avatar_status(const std::string& name, const LLUUID& key, ERadarStatType type, bool entering, const F32& dist) + void chat_avatar_status(const std::string& name, const LLUUID& key, ERadarStatType type, bool entering, const F32& dist, bool flood = false) { static LLCachedControl radar_chat_alerts(gSavedSettings, "RadarChatAlerts"); if (!radar_chat_alerts) return; @@ -82,6 +82,8 @@ namespace } // if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMETAGS)) return; // RLVa:LF Don't announce people are around when blind, that cheats the system. + static LLCachedControl radar_alert_flood(gSavedSettings, "RadarAlertFlood"); + if (flood && !radar_alert_flood) return; static LLCachedControl radar_alert_sim(gSavedSettings, "RadarAlertSim"); static LLCachedControl radar_alert_draw(gSavedSettings, "RadarAlertDraw"); static LLCachedControl radar_alert_shout_range(gSavedSettings, "RadarAlertShoutRange"); @@ -100,7 +102,7 @@ namespace default: llassert(type); break; } args["[NAME]"] = name; - args["[ACTION]"] = LLTrans::getString(entering ? "has_entered" : "has_left"); + args["[ACTION]"] = LLTrans::getString((entering ? "has_entered" : "has_left") + (flood ? "_flood" : LLStringUtil::null)); if (args.find("[RANGE]") != args.end()) chat.mText = LLTrans::getString("radar_alert_template", args); else if (chat.mText.empty()) return; @@ -144,7 +146,12 @@ LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string& name, LLAvatarListEntry::~LLAvatarListEntry() { - setPosition(mPosition, F32_MIN, false); // Dead and gone + static LLCachedControl radar_alert_flood_leaving(gSavedSettings, "RadarAlertFloodLeaving"); + bool cleanup = LLFloaterAvatarList::instance().isCleanup(); + if (radar_alert_flood_leaving || !cleanup) + { + setPosition(mPosition, F32_MIN, false, cleanup); // Dead and gone + } LLAvatarPropertiesProcessor::instance().removeObserver(mID, this); } @@ -185,17 +192,17 @@ void LLAvatarListEntry::processProperties(void* data, EAvatarProcessorType type) } } -void LLAvatarListEntry::setPosition(const LLVector3d& position, const F32& dist, bool drawn) +void LLAvatarListEntry::setPosition(const LLVector3d& position, const F32& dist, bool drawn, bool flood) { mPosition = position; bool here(dist != F32_MIN); // F32_MIN only if dead bool this_sim(here && (gAgent.getRegion()->pointInRegionGlobal(position) || !(LLWorld::getInstance()->positionRegionValidGlobal(position)))); - if (this_sim != mStats[STAT_TYPE_SIM]) chat_avatar_status(mName, mID, STAT_TYPE_SIM, mStats[STAT_TYPE_SIM] = this_sim, dist); - if (drawn != mStats[STAT_TYPE_DRAW]) chat_avatar_status(mName, mID, STAT_TYPE_DRAW, mStats[STAT_TYPE_DRAW] = drawn, dist); + if (this_sim != mStats[STAT_TYPE_SIM]) chat_avatar_status(mName, mID, STAT_TYPE_SIM, mStats[STAT_TYPE_SIM] = this_sim, dist, flood); + if (drawn != mStats[STAT_TYPE_DRAW]) chat_avatar_status(mName, mID, STAT_TYPE_DRAW, mStats[STAT_TYPE_DRAW] = drawn, dist, flood); bool shoutrange(here && dist < LFSimFeatureHandler::getInstance()->shoutRange()); - if (shoutrange != mStats[STAT_TYPE_SHOUTRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_SHOUTRANGE, mStats[STAT_TYPE_SHOUTRANGE] = shoutrange, dist); + if (shoutrange != mStats[STAT_TYPE_SHOUTRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_SHOUTRANGE, mStats[STAT_TYPE_SHOUTRANGE] = shoutrange, dist, flood); bool chatrange(here && dist < LFSimFeatureHandler::getInstance()->sayRange()); - if (chatrange != mStats[STAT_TYPE_CHATRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_CHATRANGE, mStats[STAT_TYPE_CHATRANGE] = chatrange, dist); + if (chatrange != mStats[STAT_TYPE_CHATRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_CHATRANGE, mStats[STAT_TYPE_CHATRANGE] = chatrange, dist, flood); } void LLAvatarListEntry::resetName(const bool& hide_tags, const bool& anon_names, const std::string& hidden) @@ -240,6 +247,8 @@ LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")), LLFloaterAvatarList::~LLFloaterAvatarList() { + mCleanup = true; + mAvatars.clear(); } //static @@ -413,7 +422,7 @@ BOOL LLFloaterAvatarList::postBuild() mAvatarList->setSortChangedCallback(boost::bind(&LLFloaterAvatarList::onAvatarSortingChanged,this)); for (LLViewerRegion* region : LLWorld::instance().getRegionList()) { - updateAvatarList(region); + updateAvatarList(region, true); } assessColumns(); @@ -515,7 +524,7 @@ const F32& radar_range_radius() return radius; } -void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region) +void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region, bool first) { // Check whether updates are enabled if (!mUpdate) @@ -561,7 +570,7 @@ void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region) } // Announce position - entry->setPosition(position, (position - mypos).magVec(), avatarp); + entry->setPosition(position, (position - mypos).magVec(), avatarp, first); // Mark as typing if they are typing if (avatarp && avatarp->isTyping()) entry->setActivity(LLAvatarListEntry::ACTIVITY_TYPING); diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h index 10397ffdf..0da776495 100644 --- a/indra/newview/llfloateravatarlist.h +++ b/indra/newview/llfloateravatarlist.h @@ -79,7 +79,7 @@ enum ACTIVITY_TYPE * Update world position. * Affects age. */ - void setPosition(const LLVector3d& position, const F32& dist, bool drawn); + void setPosition(const LLVector3d& position, const F32& dist, bool drawn, bool flood = false); const LLVector3d& getPosition() const { return mPosition; } @@ -202,7 +202,7 @@ public: /** * @brief Updates the internal avatar list with the currently present avatars. */ - void updateAvatarList(const class LLViewerRegion* region); + void updateAvatarList(const class LLViewerRegion* region, bool first = false); /** * @brief Refresh avatar list (display) @@ -318,8 +318,6 @@ public: static bool onConfirmRadarChatKeys(const LLSD& notification, const LLSD& response ); - static void callbackIdle(void *userdata); - void doCommand(avlist_command_t cmd, bool single = false) const; /** @@ -330,6 +328,7 @@ public: */ void expireAvatarList(const std::list& ids); void updateAvatarSorting(); + bool isCleanup() const { return mCleanup; } private: void setFocusAvatarInternal(const LLUUID& id); @@ -340,6 +339,7 @@ private: LLScrollListCtrl* mAvatarList; av_list_t mAvatars; bool mDirtyAvatarSorting; + bool mCleanup = false; /** * @brief true when Updating diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index 66e72db88..b34d3d165 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -3119,7 +3119,9 @@ Where tag = tag string to match. Removes bot's matching the tag. has triggered your avatar age alert has entered + is now detected in has left + is now off the radar for the sim draw distance From 8a089647559074472ac9c6fe538b5358caf74c18 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 14:25:56 -0500 Subject: [PATCH 055/332] Request: Underline links when hovering over them, for distinguishability --- indra/llui/lltexteditor.cpp | 19 +++++++++++++++---- indra/llui/lltexteditor.h | 7 +++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 0062eddc8..9f8d374f1 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1581,6 +1581,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; + auto old_hover = mHoverSegment; mHoverSegment = NULL; if(hasMouseCapture() ) { @@ -1665,6 +1666,14 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) } } + if (old_hover != mHoverSegment) + { + if (old_hover) + old_hover->underlineOnHover(false); + if (mHoverSegment) + mHoverSegment->underlineOnHover(true); + } + if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) { mOnScrollEndCallback(mOnScrollEndData); @@ -4269,7 +4278,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s LLStyleSP link_style(style ? new LLStyle(*style) : new LLStyle); link_style->setColor(link_color); link_style->setLinkHREF(match.getUrl()); - appendAndHighlightText(link, part, link_style); + appendAndHighlightText(link, part, link_style, match.underlineOnHoverOnly()); }; while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3))) @@ -4387,7 +4396,7 @@ void LLTextEditor::appendLineBreakSegment(/*const LLStyle::Params& style_params* insertStringNoUndo(getLength(), LLWString(1, '\n')); } -void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep) +void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover) { if (new_text.empty()) return; @@ -4407,7 +4416,7 @@ void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highl }*/ std::string str = std::string(new_text,start,new_text.length()-start); - appendAndHighlightTextImpl(str,highlight_part, stylep); + appendAndHighlightTextImpl(str,highlight_part, stylep, underline_on_hover); } @@ -4478,7 +4487,7 @@ void LLTextEditor::replaceUrl(const std::string &url, } -void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep) +void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover) { // Save old state BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); @@ -4519,6 +4528,7 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h LLStyleSP sp(new LLStyle(*highlight_params)); LLTextSegmentPtr segmentp; segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_text.size()); + if (underline_on_hover) segmentp->setUnderlineOnHover(true); mSegments.push_back(segmentp); } return; @@ -4537,6 +4547,7 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h S32 segment_start = old_length; S32 segment_end = old_length + wide_text.size(); LLTextSegmentPtr segment = new LLTextSegment(stylep, segment_start, segment_end); + if (underline_on_hover) segment->setUnderlineOnHover(true); mSegments.push_back(segment); } else // If no style, still make a segment, diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 66df57ad0..66dc56775 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -187,7 +187,7 @@ public: void appendLineBreakSegment(); - void appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep); + void appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover = false); void replaceUrl(const std::string& url, const std::string& label, const std::string& icon); @@ -196,7 +196,7 @@ public: const LLColor4 &color, const std::string& font_name = LLStringUtil::null); - void appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep); + void appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover = false); // Removes text from the end of document // Does not change highlight or cursor position. @@ -634,6 +634,8 @@ public: void setStyle(const LLStyleSP &style) { mStyle = style; } void setIsDefault(BOOL b) { mIsDefault = b; } BOOL getIsDefault() const { return mIsDefault; } + void setUnderlineOnHover(bool b) { mUnderlineOnHover = b; } + void underlineOnHover(bool hover) { if (mUnderlineOnHover) mStyle->mUnderline = hover; } void setToken( LLKeywordToken* token ) { mToken = token; } LLKeywordToken* getToken() const { return mToken; } BOOL getToolTip( std::string& msg ) const; @@ -655,6 +657,7 @@ private: S32 mEnd; LLKeywordToken* mToken; BOOL mIsDefault; + bool mUnderlineOnHover = false; std::string mTooltip; }; From 0aa6fa9302851af646aab62016428258b51a9afb Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 14:36:57 -0500 Subject: [PATCH 056/332] Fix scrolling not taking effect on certain sliders --- indra/llui/llslider.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 12d32d2ed..c2a8ffd51 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -247,8 +247,11 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) BOOL LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) { - F32 new_val = getValueF32() - clicks * getIncrement(); + F32 old_val = getValueF32(); + F32 new_val = old_val - clicks * getIncrement(); setValueAndCommit(new_val); + // Singu Note: We have to tell the viewer we're done dragging if it cares + if (mMouseUpSignal && old_val != new_val) (*mMouseUpSignal)(this, new_val); return TRUE; } From 02c06f16547f3eceeab0b097f6a199040db7a2f3 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 15:45:52 -0500 Subject: [PATCH 057/332] Don't leave links underlined if the mouse leaves --- indra/llui/lltexteditor.cpp | 8 ++++++++ indra/llui/lltexteditor.h | 1 + 2 files changed, 9 insertions(+) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 9f8d374f1..331dc846c 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1681,6 +1681,14 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) return handled; } +void LLTextEditor::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if (mHoverSegment) + { + mHoverSegment->underlineOnHover(false); + mHoverSegment = nullptr; + } +} BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 66dc56775..9f55412be 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -92,6 +92,7 @@ public: virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask) override; virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); From 865061faf05bb2beae32d93ef31b58199333ab71 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 20 Jan 2019 15:46:29 -0500 Subject: [PATCH 058/332] Don't split link underlines, since we're not even using the query color --- indra/llui/lltexteditor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 331dc846c..076c6728b 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4323,7 +4323,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s }*/ // output the styled url - append_link(label); + append_link(label + match.getQuery()); bool tooltip_required = !match.getTooltip().empty(); // set the tooltip for the Url label @@ -4332,14 +4332,15 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s setLastSegmentToolTip(match.getTooltip()); } + /* Singu Note: Upstream uses hardcoded Grey here, they have no care for skins, this could be awful! + Also it splits up the link! For now just make it append it to the same segment with its label above. // show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries std::string label = match.getQuery(); if (!label.empty()) { - /* Singu Note: Upstream uses hardcoded Grey here, they have no care for skins, this could be awful! For now just make it a normal link link_params.color = LLColor4::grey; link_params.readonly_color = LLColor4::grey; - appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly());*/ + appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); append_link(label); // set the tooltip for the query part of url @@ -4347,7 +4348,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s { setLastSegmentToolTip(match.getTooltip()); } - } + }*/ } else if (!replace_links) // Still link the link itself { From dd7ab5a0ff4f1bd52f9ef4a797f7597c2dc535e5 Mon Sep 17 00:00:00 2001 From: Inusaito Sayori Date: Mon, 21 Jan 2019 08:48:54 -0500 Subject: [PATCH 059/332] Underline all registered urls on hover --- indra/llui/lltexteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 076c6728b..547db779f 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4286,7 +4286,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s LLStyleSP link_style(style ? new LLStyle(*style) : new LLStyle); link_style->setColor(link_color); link_style->setLinkHREF(match.getUrl()); - appendAndHighlightText(link, part, link_style, match.underlineOnHoverOnly()); + appendAndHighlightText(link, part, link_style, true/*match.underlineOnHoverOnly()*/); }; while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3))) From 95cd6cf775b6040ae59b55b51688080928db6d9c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 21 Jan 2019 09:57:21 -0500 Subject: [PATCH 060/332] Sync up textures.xml a bit --- indra/newview/skins/default/textures/textures.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 3a742f3ce..fcafe9efb 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -29,6 +29,7 @@ with the same filename but different name --> + @@ -67,6 +68,8 @@ with the same filename but different name + + @@ -81,31 +84,41 @@ with the same filename but different name + + + + + + + + + + @@ -396,7 +409,6 @@ with the same filename but different name - From c8d95e58c37b648d69009113aa38153c46824388 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 21 Jan 2019 12:07:04 -0500 Subject: [PATCH 061/332] LLMenuGL Sync --- indra/llui/llmenugl.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 3dd787089..0a927ee4a 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -70,7 +70,7 @@ using namespace LLOldEvents; // static -LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL; +LLMenuHolderGL *LLMenuGL::sMenuContainer = nullptr; S32 MENU_BAR_HEIGHT = 0; S32 MENU_BAR_WIDTH = 0; @@ -258,7 +258,7 @@ BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) // if not, it will be added to the list BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) { - LLKeyBinding *accelerator = NULL; + LLKeyBinding *accelerator = nullptr; if (mAcceleratorKey != KEY_NONE) { @@ -268,19 +268,37 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) accelerator = *list_it; if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))) { + + // *NOTE: get calling code to throw up warning or route + // warning messages back to app-provided output + // std::string warning; + // warning.append("Duplicate key binding <"); + // appendAcceleratorString( warning ); + // warning.append("> for menu items:\n "); + // warning.append(accelerator->mName); + // warning.append("\n "); + // warning.append(mLabel); + + // LL_WARNS() << warning << LL_ENDL; + // LLAlertDialog::modalAlert(warning); return FALSE; } } if (!accelerator) { - accelerator = new LLKeyBinding; - if (accelerator) + try { + accelerator = new LLKeyBinding; accelerator->mKey = mAcceleratorKey; accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS); // accelerator->mName = mLabel; + listp->push_back(accelerator);//addData(accelerator); + } + catch (const std::bad_alloc& e) + { + LL_WARNS() << "Failed to allocate memory for keybinding with exception: " << e.what() << LL_ENDL; + return FALSE; } - listp->push_back(accelerator);//addData(accelerator); } } return TRUE; @@ -681,7 +699,7 @@ class LLMenuItemVerticalSeparatorGL public: LLMenuItemVerticalSeparatorGL( void ); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override { return FALSE; } }; LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) @@ -733,7 +751,7 @@ LLFloater* LLMenuItemTearOffGL::getParentFloater() } } - return NULL; + return nullptr; } void LLMenuItemTearOffGL::onCommit() @@ -4873,7 +4891,7 @@ void LLContextMenu::hide() { mHoverItem->setHighlight( FALSE ); } - mHoverItem = NULL; + mHoverItem = nullptr; } From bde878232830a09b90d9dcb8a991ecb803c597c3 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 21 Jan 2019 12:08:39 -0500 Subject: [PATCH 062/332] Add in LLLoadingIndicator --- indra/llui/CMakeLists.txt | 2 + indra/llui/llloadingindicator.cpp | 104 ++++++++++++++++++ indra/llui/llloadingindicator.h | 104 ++++++++++++++++++ .../skins/default/textures/Progress_1.png | Bin 0 -> 470 bytes .../skins/default/textures/Progress_10.png | Bin 0 -> 461 bytes .../skins/default/textures/Progress_11.png | Bin 0 -> 475 bytes .../skins/default/textures/Progress_12.png | Bin 0 -> 455 bytes .../skins/default/textures/Progress_2.png | Bin 0 -> 453 bytes .../skins/default/textures/Progress_3.png | Bin 0 -> 454 bytes .../skins/default/textures/Progress_4.png | Bin 0 -> 454 bytes .../skins/default/textures/Progress_5.png | Bin 0 -> 468 bytes .../skins/default/textures/Progress_6.png | Bin 0 -> 462 bytes .../skins/default/textures/Progress_7.png | Bin 0 -> 469 bytes .../skins/default/textures/Progress_8.png | Bin 0 -> 454 bytes .../skins/default/textures/Progress_9.png | Bin 0 -> 469 bytes .../skins/default/textures/textures.xml | 13 +++ 16 files changed, 223 insertions(+) create mode 100644 indra/llui/llloadingindicator.cpp create mode 100644 indra/llui/llloadingindicator.h create mode 100644 indra/newview/skins/default/textures/Progress_1.png create mode 100644 indra/newview/skins/default/textures/Progress_10.png create mode 100644 indra/newview/skins/default/textures/Progress_11.png create mode 100644 indra/newview/skins/default/textures/Progress_12.png create mode 100644 indra/newview/skins/default/textures/Progress_2.png create mode 100644 indra/newview/skins/default/textures/Progress_3.png create mode 100644 indra/newview/skins/default/textures/Progress_4.png create mode 100644 indra/newview/skins/default/textures/Progress_5.png create mode 100644 indra/newview/skins/default/textures/Progress_6.png create mode 100644 indra/newview/skins/default/textures/Progress_7.png create mode 100644 indra/newview/skins/default/textures/Progress_8.png create mode 100644 indra/newview/skins/default/textures/Progress_9.png diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 1ee716cb8..b00081882 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -44,6 +44,7 @@ set(llui_SOURCE_FILES llkeywords.cpp lllayoutstack.cpp lllineeditor.cpp + llloadingindicator.cpp lllocalcliprect.cpp llmenugl.cpp llmodaldialog.cpp @@ -122,6 +123,7 @@ set(llui_HEADER_FILES llkeywords.h lllayoutstack.h lllineeditor.h + llloadingindicator.h lllocalcliprect.h llmemberlistener.h llmenugl.h diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp new file mode 100644 index 000000000..915571874 --- /dev/null +++ b/indra/llui/llloadingindicator.cpp @@ -0,0 +1,104 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +/** + * @file llloadingindicator.cpp + * @brief Perpetual loading indicator + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llloadingindicator.h" + +// Linden library includes +#include "llsingleton.h" + +// Project includes +#include "lluictrlfactory.h" +#include "lluiimage.h" + +static LLRegisterWidget r("loading_indicator"); + +/////////////////////////////////////////////////////////////////////////////// +// LLLoadingIndicator class +/////////////////////////////////////////////////////////////////////////////// + +LLLoadingIndicator::LLLoadingIndicator(const Params& p) +: LLUICtrl(p), + mImagesPerSec(p.images_per_sec > 0 ? p.images_per_sec : 1.0f), + mCurImageIdx(0) +{ +} + +void LLLoadingIndicator::initFromParams(const Params& p) +{ + for (LLUIImage* image : p.images().image) + { + mImages.push_back(image); + } + + if (mImages.empty()) // Singu Note: We don't have default widgets yet, so let's just handle this here + for (int i = 1; i <= 12; ++i) + mImages.push_back(LLUI::getUIImage(llformat("Progress_%d", i))); + + // Start timer for switching images. + start(); +} + +void LLLoadingIndicator::draw() +{ + // Time to switch to the next image? + if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired()) + { + // Switch to the next image. + if (!mImages.empty()) + { + mCurImageIdx = (mCurImageIdx + 1) % mImages.size(); + } + + // Restart timer. + start(); + } + + LLUIImagePtr cur_image = mImages.empty() ? LLUIImagePtr(NULL) : mImages[mCurImageIdx]; + + // Draw current image. + if( cur_image.notNull() ) + { + cur_image->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha); + } + + LLUICtrl::draw(); +} + +void LLLoadingIndicator::stop() +{ + mImageSwitchTimer.stop(); +} + +void LLLoadingIndicator::start() +{ + mImageSwitchTimer.start(); + F32 period = 1.0f / (mImages.size() * mImagesPerSec); + mImageSwitchTimer.setTimerExpirySec(period); +} diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h new file mode 100644 index 000000000..b5e86c0ac --- /dev/null +++ b/indra/llui/llloadingindicator.h @@ -0,0 +1,104 @@ +/** + * @file llloadingindicator.h + * @brief Perpetual loading indicator + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLLOADINGINDICATOR_H +#define LL_LLLOADINGINDICATOR_H + +#include "lluictrl.h" +#include "lluiimage.h" + +/////////////////////////////////////////////////////////////////////////////// +// class LLLoadingIndicator +/////////////////////////////////////////////////////////////////////////////// + +/** + * Perpetual loading indicator (a la MacOSX or YouTube) + * + * Number of rotations per second can be overridden + * with the "images_per_sec" parameter. + * + * Can start/stop spinning. + * + * @see start() + * @see stop() + */ +class LLLoadingIndicator +: public LLUICtrl +{ + LOG_CLASS(LLLoadingIndicator); +public: + + struct Images : public LLInitParam::Block + { + Multiple image; + + Images() + : image("image") + {} + }; + + struct Params : public LLInitParam::Block + { + Optional images_per_sec; + Optional > images; + + Params() + : images_per_sec("images_per_sec", 1.0f), + images("images") + {} + }; + + virtual ~LLLoadingIndicator() {} + + // llview overrides + void draw() override; + + /** + * Stop spinning. + */ + void stop(); + + /** + * Start spinning. + */ + void start(); + + void reset() { mCurImageIdx = 0; } + +private: + LLLoadingIndicator(const Params&); + void initFromParams(const Params&); + + friend class LLUICtrlFactory; + + F32 mImagesPerSec; + S8 mCurImageIdx; + LLFrameTimer mImageSwitchTimer; + + std::vector mImages; +}; + +#endif // LL_LLLOADINGINDICATOR_H diff --git a/indra/newview/skins/default/textures/Progress_1.png b/indra/newview/skins/default/textures/Progress_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d6efbfa2ae80add256e73a672d941a79d95e96d GIT binary patch literal 470 zcmV;{0V)28P)Px$kV!;AR7l6|l}k>-P!xv0Yghm#lHf?(flIh|1wul?V+1Qe#wJ2Z7%MB#iBJ-f z))n+F!6{C}shG$Pj+0vhl}FJSC;my(_O$2upTAWi!f)b$U&eoeoya5&N~u(iTALye z08MM7yV#ywnxAxV?as5kM-Is)%ps_zCTPyvQubG;$SqMyaV3TbUItAy!bYsm*(@= z77(jirPVg2{6tloeq{b&`w0NTBUsI_4)$X&ef&y zdi`fNLesgrwBP*}6}GHuJMqqXh{(#Z4PLJwdA)wHPdxSiPyX`+A0r~PEK?J8P5=M^ M07*qoM6N<$g2!Rf7ytkO literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_10.png b/indra/newview/skins/default/textures/Progress_10.png new file mode 100644 index 0000000000000000000000000000000000000000..28203324f1fcbe025c1d7f070ab325b6e27d70d5 GIT binary patch literal 461 zcmV;;0W$uHP)Px$heZFt`$Lzzv*w0ck=4xPlWPTZuqPSR*HZ6%>P^ zH|X5JBZw!E8;qMF!KT#$5@X`OndZ&Qyf^><{;3#aI3`XwGX8#nOk7YzVw0=Y+a-|o z{i&05U{ur0SJjnr>cALqwGIOy3n*Kx1T2Au?@uTDQna$`FlKKU$%%!;jj7{Y*MXJR zp;pRS0wuu9=g+TzA}|8FK;ZjR*Xs`yQ0R8rle83#Ek%JnOaT}68{h_5=W=HwuRqu{ z05C}SYN`TTjJdNxJK*Q?~-mNYxpp|-xte}BXPS19pgaCN!cG|t2G)NlLD7^ll z1T?oo>(0Zyn|e%BqG)LX&_8;E*?e&aP^*+5lZPx$l}SWFR7l6|l|fFzP!NW{F&qHH23O)z-FO4r6G#&h3M)MUuqL6Fgtg%W;HEqb zru78$CgNTqH+XIyNhK{(A|@vONz-KBbmq-J^G`)<%^`8bf${ef6w-_$lIm<(b_f*X zcy4C}-0W##TXxH`Z-Eiu#PK`=ihu>+y3HyO0(VLU<9#VwnD;}<*~5lUDkN=8lN1?R zs(=q%1C?_5bgyaDK7^fGa~2K%6R!%_niz)>^l- zBK!Tp$nOu@+u}*aHm#aDuK+6%InGG%rO95eyK|GnL=%Tv>kq(JPtR)#EWleXm(KG2 zy=5*G0W&tpT_t R0SEv9002ovPDHLkV1l3+%rgK0 literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_12.png b/indra/newview/skins/default/textures/Progress_12.png new file mode 100644 index 0000000000000000000000000000000000000000..089d58b09049192ad9ad8a9ad1356615de711bd6 GIT binary patch literal 455 zcmV;&0XY7NP)Px$fk{L`R7l6|RY7XPKootyxU^tzU~kYP%$%SG1jnrO1ktsiMsQs`LAx>u1Y%E+ zIYJlOVmH+jm>Wzt6G*3y#Zd%~TIaWbU%kii}G;Rt9l6wcE_!xUVrBu%r*`G}B{mHab4oZ`p=OBn;1|S8nX}6l!K@=+hvfXNOe=>d3E##x&Kz{YItLIYP xv)4C#?ANPJMJ9qMPJ<{;PKihV|Kwj!@D4YBy5HZHTZsSw002ovPDHLkV1oK@!Y%*+ literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_2.png b/indra/newview/skins/default/textures/Progress_2.png new file mode 100644 index 0000000000000000000000000000000000000000..94cb73b1f7d3bdf3627fb388258882968c042e69 GIT binary patch literal 453 zcmV;$0XqJPP)Px$e@R3^R7l6|l|fFzKoo|*F&+SSxWk)FPXJ9wC~U+N6xJkQn{=(5K;7VIG?Ww2 z8>kC)3GN|sgSzk$v9tn4;)Xw&WSai(|L47VeN}NBvrX)=W$X-ABaY*5EAg%#f1>nj zy%h#p3`T!yASc(d?~6$zOH^x1Yi-rj4E8f(E`%HcT|j3D7l1QhUc$>HC(FQI2-Hfc zgACpSkORg*GmVgHpp*)M@Tal}Lf|QihUZy?C4=jmyWzYf=JtLp*<@h=xLR99d7dTt zX&KCbXoF5K$RaIn$)H*(+Y8w=B%8-H<$V6I2wWM{J6*d(B3zP7E=WdMH7)@;$Eh6y zO=EiDx?R+tU*dE+?RtLjE*Clgczt;;intB9vhM?G-C1-3Sk+3|o=zsNwBnn5AFk|k zfNM<81}uqL?#SeLQ0nUto*!sn2#h=J){*B2F%WgyEz5CgBPpR}OfOm-6`M@cd3U6J vM9lPx$fJsC_R7l6|l}$>+P!xr~Ck{KPJJ6wjdq`Gb0)?iX*sh@MSZHk_W9PyD^M`=>%gm&U}~xr5j+rHxVgFKzH{zNjEL})*x-lp_X#%B0b@*NvWUdM zlQG6$l9PW2B2trAcp7qm;yNXo>w2~}i2$yyhc#esjIkmT0iK977YM&c09+_hG@i`* z2m8B@#{G2>_$rGxo=ly?gT1Y7Ms0ci$ zp$)w+77B%iwXv_p)>j_WZZC9z7?^k3Cp$q<6~GP#mu?VL?^Qx~Fu1f|`q*g!fYt}k wTB)4ugRyumLl9KsAgGR3sb~KG$-hqU0vxoXTQoK1jQ{`u07*qoM6N<$f*T6Y+5i9m literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_4.png b/indra/newview/skins/default/textures/Progress_4.png new file mode 100644 index 0000000000000000000000000000000000000000..a467098d8232e2b64bafa93e2b9c4b9aa3672b2b GIT binary patch literal 454 zcmV;%0XhDOP)Px$fJsC_R7l6|l|fDdK@de>8xMdtz?<}(Kqet!U?ZFWSeq!2utrY64u*t;@dVS? zz>T<)h>3H9a$%9+$S5F~m{`fAJKfdwyMER0F(Se-?Sv!S-v>BNFBoGookb)D5@U>? zvAp7}ifqz_F~*>r_w<%Yxl}ycreii0c_Y9YRSkf0 zsd&3Dz$c2@oo>9MNV766q4e)NRul(im zxvtN{qw1rs^BhojF7^R0E6Xq2Gzcmdh=F++4$p(2B7h6Sp=Yi4pcHz}#cr?1P6Lpk w*k;Oyoij*V5L99y3B#ekx03&VnZFM32IpPx$j!8s8R7l6|l|f3wP!NW{Cmx{QK#$-JUQS>NLes9=l^{av4Q!$7wkN2oCV@cf z3G$BM4U8*YSnLhPg;OXov9Vf2a3JJm-sGSB|Gb$P5n-Qpz@F{zCpgSIj4`FbVVul? zTC;KP73~+*^HT<4oV3F@kuXktb=3f?V7|Ot`#Tb4dLI?+jdC&7Bm(FUhHW5gHqKob zCkwz2PDHFe-&SQX1=9XtIA1Q86JVwOYoM;{6)+KzIgsi*)fgzq z2=JFLFZSy4;%IZT+29@+i^!|iCDY`^mEXK_QuX?S;j~O&5$UO1g;*l;kwJejOxGtx zW$2V8Fvg6(+y@Hkeyr0zlPCyUH$l*v?uu0?kplZ#n(ZPx$h)G02R7l6|l}}E>P!Ps{&u{=dfpsH^dIw)`00{|&m7D-vlYo%0Mos`L#l)ob z1nm*rsM~r1NbG*xOcPT6E0~!0CCy7albLznH#0RN!Xa_Qf${ef z-rrtlKz)sd5#Wo+VD8=BMA1wgw?#bHNwb|O^1Rxu=ha5LOzuXCG;mQxcC@Pc-d2Ue z*E2|xWN58B1p?q1Ad14^r^~JmxRc3!DM>yX+KL}QBb||Lv2>DCSnFCK1mY+PPpx%B zz(!GMwq16cc9Em!&Av52xgKZ6f7cdXr0D;b`RfV30Mm57WhfwyLI3~&07*qoM6N<$ Ef(10pwEzGB literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_7.png b/indra/newview/skins/default/textures/Progress_7.png new file mode 100644 index 0000000000000000000000000000000000000000..64fa2947714fd2ecacb4e60c8761ea9e6ca118ee GIT binary patch literal 469 zcmV;`0V@89P)Px$k4Z#9R7l6|l}~EIKp4e;Q#^n|T{qp-W!n>UasvZ`iQVW4YS)5R!L^D9(3O8c zFgHk!l5T53Z;)<2Aq|NR1s5%S5CSt_=KJ&Bo2WRBxg@T*Fn)i6LV7_dl^fh247WhB z*X`JOF=Px$fJsC_R7l6|l}%2l z{wlHUK?Jmc>DBek18@h-0T+nf$++VMp$3}$UUylwMGI+BEY37=DenL+AiHQZ=3Wr) z5&&3!NwT0LEocMU_gD4Yd<+b|AlwQsDKM&}A^;&Yw(Wrf+?KR>3efL$Un|R0Yiqqz z%4YLkn3*5}W?m4s=SPt1Toq zfhs2i$3t}v w%3zFnGoR39{(f>?kM>kl{Ve*w(f(e554GdKKn7J>i2wiq07*qoM6N<$f>SibiU0rr literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Progress_9.png b/indra/newview/skins/default/textures/Progress_9.png new file mode 100644 index 0000000000000000000000000000000000000000..f3e9723184fcbaba963de87fe3651b049b1e0b72 GIT binary patch literal 469 zcmV;`0V@89P)Px$k4Z#9R7l6|l}}2;KorJ*BVBe;PheLTt&85k$q8(r(A1SZfp%SJnnKnwCumoi zg3v+SH#f1Kz=GM23#U>;(?m-V!4DpRd2im|Z(fAfntkGcJ>%~OD5VdC5Shk~b2$Sl zQFLcz*`#M$+7{h$F0X(wU`NrN0xEz6@J4pO3pxi;<_C~%0s_vjaDcYhjfRvU>k|Zpb%Mv&OLZiO` zmO$u@{8a(~liVR2*ro-|BzcohPmhNwy#l)4$bU6+SpdC4ECPt*xdN1w*3e|01qHmg zgZ{8EO{KJECaV>~tjou>S_lO9(}%e^TAJ#&R`5whb0H5^BLHzB78S+)^x+1e({9~n zJ+xf|d>L}5-BM|P-`Yi5>xPjVt@Z0!{nR#tu9{c9^Rf%p997QhC)b^J%LXi + + + + + + + + + + + + + From 54cf0a6a9102cf4ca2c7f14f947580529af0dd6a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 21 Jan 2019 09:27:03 -0500 Subject: [PATCH 063/332] Set up LLButton::Params and LLButton::initFromParams --- indra/llui/llbutton.cpp | 302 +++++++++++++++++++++++++--------------- indra/llui/llbutton.h | 92 +++++++++++- 2 files changed, 274 insertions(+), 120 deletions(-) diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index d11334959..60eae831c 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -61,67 +61,92 @@ S32 BTN_HEIGHT = 0; S32 BTN_GRID = 12; S32 BORDER_SIZE = 1; + +LLButton::Params::Params() + : label_selected("label_selected"), // requires is_toggle true + label_shadow("label_shadow", true), + auto_resize("auto_resize", false), + use_ellipses("use_ellipses", false), + image_unselected("image_unselected", LLUI::getUIImage("button_enabled_32x128.tga")), + image_selected("image_selected", LLUI::getUIImage("button_enabled_selected_32x128.tga")), + image_hover_selected("image_hover_selected"), + image_hover_unselected("image_hover_unselected"), + image_disabled_selected("image_disabled_selected", LLUI::getUIImage("button_disabled_32x128.tga")), + image_disabled("image_disabled", LLUI::getUIImage("button_disabled_32x128.tga")), + image_pressed("image_pressed", LLUI::getUIImage("button_enabled_selected_32x128.tga")), + image_pressed_selected("image_pressed_selected", LLUI::getUIImage("button_enabled_32x128.tga")), + image_overlay("image_overlay"), + image_overlay_selected("image_overlay_selected"), + image_overlay_alignment("image_overlay_alignment", std::string("center")), + image_overlay_enable("image_overlay_enable", true), + label_color("label_color", LLUI::sColorsGroup->getColor("ButtonLabelColor")), + label_color_selected("label_color_selected", LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), + label_color_disabled("label_color_disabled", LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), + label_color_disabled_selected("label_color_disabled_selected", LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), + image_color("image_color", LLUI::sColorsGroup->getColor("ButtonImageColor")), // requires is_toggle true + image_color_disabled("image_color_disabled", LLUI::sColorsGroup->getColor("ButtonImageColor")), + image_overlay_color("image_overlay_color", LLColor4::white), + image_overlay_selected_color("image_overlay_selected_color", LLColor4::white), + image_overlay_disabled_color("image_overlay_disabled_color", LLColor4(1.f, 1.f, 1.f, .5f)), + flash_color("flash_color", LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), + pad_right("pad_right", LLUI::sConfigGroup->getS32("ButtonHPad")), + pad_left("pad_left", LLUI::sConfigGroup->getS32("ButtonHPad")), + pad_bottom("pad_bottom", LLUI::sConfigGroup->getS32("ButtonVPad")), + image_top_pad("image_top_pad"), + image_bottom_pad("image_bottom_pad"), + imgoverlay_label_space("imgoverlay_label_space", 1), + click_callback("click_callback"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + mouse_held_callback("mouse_held_callback"), + is_toggle("is_toggle", false), + scale_image("scale_image", true), + commit_on_return("commit_on_return", true), + display_pressed_state("display_pressed_state", true), + hover_glow_amount("hover_glow_amount", 0.25f), + //held_down_delay("held_down_delay"), + use_draw_context_alpha("use_draw_context_alpha", true), + //badge("badge"), + handle_right_mouse("handle_right_mouse", false), + button_flash_enable("button_flash_enable", false), + button_flash_count("button_flash_count", LLUI::sConfigGroup->getS32("ButtonFlashCount")), + button_flash_rate("button_flash_rate", LLUI::sConfigGroup->getF32("ButtonFlashRate")), + fade_when_disabled("fade_when_disabled", false), + help_url("help_url") +{ + addSynonym(is_toggle, "toggle"); + changeDefault(initial_value, LLSD(false)); + changeDefault(font_halign, LLFontGL::HCENTER); +} + LLButton::LLButton( const std::string& name, const LLRect& rect, const std::string& control_name, commit_callback_t commit_callback) : LLUICtrl(name, rect, TRUE, commit_callback), - - mMouseDownFrame( 0 ), + mMouseDownFrame(0), mMouseHeldDownCount(0), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mBorderEnabled(FALSE), + mFlashing(FALSE), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), - mUnselectedLabel(name), - mSelectedLabel(name), - mGLFont( LLFontGL::getFontSansSerif() ), - mHeldDownDelay( 0.5f ), // seconds until held-down callback is called - mHeldDownFrameDelay( 0 ), - mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), - mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), - mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), - mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), mImageFlash(NULL), - mImageHoverSelected( NULL ), - mImageHoverUnselected( NULL ), - mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), - mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), - mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), - mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), - mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), - mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mImageOverlay(NULL), - mImageOverlaySelected(NULL), - mImageOverlayColor(LLColor4::white), - mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), - mImageOverlaySelectedColor(LLColor4::white), - mImageOverlayAlignment(LLFontGL::HCENTER), mImageOverlayTopPad(0), mImageOverlayBottomPad(0), mImgOverlayLabelSpace(1), - mIsToggle( FALSE ), - mScaleImage( TRUE ), - mDropShadowedText( TRUE ), - mAutoResize( FALSE ), - mHAlign( LLFontGL::HCENTER ), - mLeftHPad( LLBUTTON_H_PAD ), - mRightHPad( LLBUTTON_H_PAD ), - mBottomVPad( LLBUTTON_V_PAD ), - mHoverGlowStrength(0.25f), - mCommitOnReturn(TRUE), - mFadeWhenDisabled(FALSE), + mDropShadowedText(TRUE), mForcePressedState(false), - mDisplayPressedState(TRUE), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mHeldDownSignal(NULL), - mHandleRightMouse(false), - mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), - mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), - mAlpha(1.f) + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { - init(control_name); + Params params; + params.label_selected(name) + .label(name) + .control_name(control_name) + .name(name) + .rect(rect); + initFromParams(params); } @@ -134,93 +159,133 @@ LLButton::LLButton(const std::string& name, const LLRect& rect, const std::string& unselected_label, const std::string& selected_label ) : LLUICtrl(name, rect, TRUE, commit_callback), - mMouseDownFrame( 0 ), + mMouseDownFrame(0), mMouseHeldDownCount(0), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mBorderEnabled(FALSE), + mFlashing(FALSE), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), - mUnselectedLabel(unselected_label), - mSelectedLabel(selected_label), - mGLFont( font ? font : LLFontGL::getFontSansSerif() ), - mHeldDownDelay( 0.5f ), // seconds until held-down callback is called - mHeldDownFrameDelay( 0 ), - mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), - mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), - mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), - mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), mImageFlash(NULL), - mImageHoverSelected( NULL ), - mImageHoverUnselected( NULL ), - mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), - mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), - mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), - mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), - mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), - mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mImageOverlay(NULL), - mImageOverlaySelected(NULL), - mImageOverlayColor(LLColor4::white), - mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), - mImageOverlaySelectedColor(LLColor4::white), - mImageOverlayAlignment(LLFontGL::HCENTER), mImageOverlayTopPad(0), mImageOverlayBottomPad(0), mImgOverlayLabelSpace(1), - mIsToggle( FALSE ), - mScaleImage( TRUE ), - mDropShadowedText( TRUE ), - mAutoResize( FALSE ), - mHAlign( LLFontGL::HCENTER ), - mLeftHPad( LLBUTTON_H_PAD ), - mRightHPad( LLBUTTON_H_PAD ), - mBottomVPad( LLBUTTON_V_PAD ), - mHoverGlowStrength(0.25f), - mCommitOnReturn(TRUE), - mFadeWhenDisabled(FALSE), + mDropShadowedText(TRUE), mForcePressedState(false), - mDisplayPressedState(TRUE), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mHeldDownSignal(NULL), - mHandleRightMouse(false), - mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), - mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), - mAlpha(1.f) + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { + Params params; + params.label_selected(selected_label) + .label(unselected_label) + .font(font) + .control_name(control_name) + .name(name) + .rect(rect); - if( unselected_image_name != "" ) + if (!unselected_image_name.empty()) { // user-specified image - don't use fixed borders unless requested - mImageUnselected = LLUI::getUIImage(unselected_image_name); - mImageDisabled = mImageUnselected; + auto image = LLUI::getUIImage(unselected_image_name); + params.image_unselected(image) + .image_disabled(image) + .image_pressed_selected(image) - mFadeWhenDisabled = TRUE; - //mScaleImage = FALSE; - - mImagePressedSelected = mImageUnselected; + .fade_when_disabled(true); + //params.scale_image(false); } - if( selected_image_name != "" ) + if (!selected_image_name.empty()) { // user-specified image - don't use fixed borders unless requested - mImageSelected = LLUI::getUIImage(selected_image_name); - mImageDisabledSelected = mImageSelected; + auto image = LLUI::getUIImage(selected_image_name); + params.image_selected(image) + .image_disabled_selected(image) + .image_pressed(image) - mFadeWhenDisabled = TRUE; - //mScaleImage = FALSE; - - mImagePressed = mImageSelected; + .fade_when_disabled(true); + //params.scale_image(false); } - init(control_name); + initFromParams(params); } -void LLButton::init(const std::string& control_name) +LLButton::LLButton(const Params& params) +: LLUICtrl(params), + mMouseDownFrame(0), + mMouseHeldDownCount(0), + mBorderEnabled(FALSE), + mFlashing(FALSE), + mCurGlowStrength(0.f), + mNeedsHighlight(FALSE), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), + mImageFlash(NULL), + mImageOverlayTopPad(0), + mImageOverlayBottomPad(0), + mImgOverlayLabelSpace(1), + mDropShadowedText(TRUE), + mForcePressedState(false), + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { + initFromParams(params); +} + +void LLButton::initFromParams(const Params& params) +{ + LLUICtrl::initFromParams(params); + mUnselectedLabel = params.label; + mSelectedLabel = params.label_selected; + auto font = params.font(); + mGLFont = font ? font : LLFontGL::getFontSansSerif(); + mHAlign = params.font_halign; + mImageUnselected = params.image_unselected; + mImageSelected = params.image_selected; + mImageDisabled = params.image_disabled; + mImageDisabledSelected = params.image_disabled_selected; + mImagePressed = params.image_pressed; + mImagePressedSelected = params.image_pressed_selected; + mImageHoverSelected = params.image_hover_selected; + mImageHoverUnselected = params.image_hover_unselected; + mUnselectedLabelColor = params.label_color; + mSelectedLabelColor = params.label_color_selected; + mDisabledLabelColor = params.label_color_disabled; + mDisabledSelectedLabelColor = params.label_color_disabled_selected; + mImageColor = params.image_color; + mFlashBgColor = params.flash_color; + mDisabledImageColor = params.image_color_disabled; + mImageOverlay = params.image_overlay; + mImageOverlaySelected = params.image_overlay_selected; + mImageOverlayColor = params.image_overlay_color; + mImageOverlayDisabledColor = params.image_overlay_disabled_color; + mImageOverlaySelectedColor = params.image_overlay_selected_color; + mImageOverlayAlignment = LLFontGL::hAlignFromName(params.image_overlay_alignment); + mIsToggle = params.is_toggle; + mScaleImage = params.scale_image; + mAutoResize = params.auto_resize; + mLeftHPad = params.pad_left; + mRightHPad = params.pad_right; + mBottomVPad = params.pad_bottom; + mHoverGlowStrength = params.hover_glow_amount; + mCommitOnReturn = params.commit_on_return; + mFadeWhenDisabled = params.fade_when_disabled; + mDisplayPressedState = params.display_pressed_state; + if (params.mouse_down_callback.isProvided()) + setMouseDownCallback(params.mouse_down_callback); + if (params.mouse_up_callback.isProvided()) + setMouseUpCallback(params.mouse_up_callback); + if (params.mouse_held_callback.isProvided()) + setHeldDownCallback(params.mouse_held_callback); + mHandleRightMouse = params.handle_right_mouse; + mButtonFlashCount = params.button_flash_count; + mButtonFlashRate = params.button_flash_rate; + // Hack to make sure there is space for at least one character if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) { @@ -231,9 +296,16 @@ void LLButton::init(const std::string& control_name) mMouseDownTimer.stop(); - setControlName(control_name, NULL); + setControlName(params.control_name, NULL); } +//static +const LLButton::Params& LLButton::getDefaultParams() +{ + // Singu Note: We diverge here, not using LLUICtrlFactory::getDefaultParams + static const Params p; + return p; +} // virtual LLButton::~LLButton() diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 86471f1c5..812ea6de1 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -74,6 +74,87 @@ class LLButton : public LLUICtrl { public: + struct Params + : public LLInitParam::Block + { + // text label + Optional label_selected; + Optional label_shadow; + Optional auto_resize; + Optional use_ellipses; + + // images + Optional image_unselected, + image_selected, + image_hover_selected, + image_hover_unselected, + image_disabled_selected, + image_disabled, + image_flash, + image_pressed, + image_pressed_selected, + image_overlay, + image_overlay_selected; + + Optional image_overlay_alignment; + Optional image_overlay_enable; + + // colors + Optional label_color, + label_color_selected, + label_color_disabled, + label_color_disabled_selected, + image_color, + image_color_disabled, + image_overlay_color, + image_overlay_selected_color, + image_overlay_disabled_color, + flash_color; + + // layout + Optional pad_right; + Optional pad_left; + Optional pad_bottom; // under text label + + //image overlay paddings + Optional image_top_pad; + Optional image_bottom_pad; + + /** + * Space between image_overlay and label + */ + Optional imgoverlay_label_space; + + // callbacks + Optional click_callback, // alias -> commit_callback + mouse_down_callback, + mouse_up_callback, + mouse_held_callback; + + // misc + Optional is_toggle, + scale_image, + commit_on_return, + display_pressed_state; + + Optional hover_glow_amount; + //Optional held_down_delay; + + Optional use_draw_context_alpha; + + //Optional badge; + + Optional handle_right_mouse; + + Optional button_flash_enable; + Optional button_flash_count; + Optional button_flash_rate; + Optional fade_when_disabled; + Optional help_url; + + Params(); + }; + // simple button with text label LLButton(const std::string& name, const LLRect &rect = LLRect(), const std::string& control_name = std::string(), commit_callback_t commit_callback = NULL); @@ -86,6 +167,11 @@ public: const LLFontGL* mGLFont = NULL, const std::string& unselected_label = LLStringUtil::null, const std::string& selected_label = LLStringUtil::null ); + + void initFromParams(const Params& p); + static const LLButton::Params& getDefaultParams(); + LLButton(const Params& p = getDefaultParams()); + public: ~LLButton(); @@ -93,7 +179,6 @@ public: typedef boost::function button_callback_t; void addImageAttributeToXML(LLXMLNodePtr node, const LLPointer, const std::string& xmlTagName) const; - void init(const std::string& control_name); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); @@ -161,7 +246,6 @@ public: const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); } const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); } - void setImageColor(const std::string& color_control); void setImageColor(const LLColor4& c); /*virtual*/ void setColor(const LLColor4& c); @@ -205,9 +289,7 @@ public: void setImageDisabled(LLPointer image); void setImageDisabledSelected(LLPointer image); void setImageFlash(LLPointer image); - void setImagePressed(LLPointer image); - - static void onHeldDown(void *userdata); // to be called by gIdleCallbacks + void setHelpURLCallback(const std::string &help_url); const std::string& getHelpURL() const { return mHelpURL; } From d0b7a983d145845aa0ecca9c0201ff168006eff8 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 21 Jan 2019 23:38:06 -0500 Subject: [PATCH 064/332] Fix an edge case where the radar could now crash loop, woops. Thanks to Deltek and Router Gray for reporting and testing! <3 --- indra/newview/llfloateravatarlist.cpp | 2 +- indra/newview/llfloateravatarlist.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index 7c1c0e096..c1cb88026 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -147,7 +147,7 @@ LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string& name, LLAvatarListEntry::~LLAvatarListEntry() { static LLCachedControl radar_alert_flood_leaving(gSavedSettings, "RadarAlertFloodLeaving"); - bool cleanup = LLFloaterAvatarList::instance().isCleanup(); + bool cleanup = LLFloaterAvatarList::isCleanup(); if (radar_alert_flood_leaving || !cleanup) { setPosition(mPosition, F32_MIN, false, cleanup); // Dead and gone diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h index 0da776495..9aa278d83 100644 --- a/indra/newview/llfloateravatarlist.h +++ b/indra/newview/llfloateravatarlist.h @@ -328,7 +328,11 @@ public: */ void expireAvatarList(const std::list& ids); void updateAvatarSorting(); - bool isCleanup() const { return mCleanup; } + static bool isCleanup() + { + const auto& inst = getIfExists(); + return inst && inst->mCleanup; + } private: void setFocusAvatarInternal(const LLUUID& id); From 621c0a02491882f74ae62a5ad181765efd996670 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 22 Jan 2019 00:49:09 -0500 Subject: [PATCH 065/332] Don't double up separators in the context menu --- indra/llui/llmenugl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 0a927ee4a..4b38899d3 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -5001,7 +5001,11 @@ bool LLContextMenu::addChild(LLView* view, S32 tab_group) if (LLContextMenu* context = dynamic_cast(view)) return appendContextSubMenu(context); if (LLMenuItemGL* item = dynamic_cast(view)) + { + if (!mItems.empty() && mItems.back()->getType() == "separator" && item->getType() == "separator") // Singu TODO: Profile this? Does it matter? + item->setVisible(false); return append(item); + } if (LLMenuGL* menu = dynamic_cast(view)) return appendMenu(menu); return false; @@ -5042,7 +5046,7 @@ LLPieMenu::LLPieMenu(const std::string& name, const std::string& label) // Separators on pie menus are invisible bool LLPieMenu::addChild(LLView* view, S32 tab_group) { - if (LLContextMenu::addChild(view, tab_group)) + if (LLContextMenu::addChild(view, tab_group) && view->getVisible()) { LLMenuItemSeparatorGL* sep = dynamic_cast(view); if(sep) From ca5fb91350d95896840623c52fd5832fe0b47a75 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 22 Jan 2019 03:31:07 -0500 Subject: [PATCH 066/332] We C++14 now. --- indra/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 06e06f274..b480c96b3 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -19,6 +19,9 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(Variables) include(BuildVersion) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type. One of: Debug Release RelWithDebInfo" FORCE) From b4cf0b429b784803ef27ddded32f8c246ca46b5a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 23 Jan 2019 05:05:36 -0500 Subject: [PATCH 067/332] Sync LLSelectMgr with upstream Alchemy --- indra/llprimitive/object_flags.h | 1 + indra/newview/llselectmgr.cpp | 470 ++++++++++++++++++++++--------- indra/newview/llselectmgr.h | 62 ++-- 3 files changed, 375 insertions(+), 158 deletions(-) diff --git a/indra/llprimitive/object_flags.h b/indra/llprimitive/object_flags.h index f48b97d58..e97014470 100644 --- a/indra/llprimitive/object_flags.h +++ b/indra/llprimitive/object_flags.h @@ -69,6 +69,7 @@ const U32 FLAGS_TEMPORARY = (1U << 30); //const U32 FLAGS_UNUSED_007 = (1U << 31); // was FLAGS_ZLIB_COMPRESSED const U32 FLAGS_LOCAL = FLAGS_ANIM_SOURCE | FLAGS_CAMERA_SOURCE; +const U32 FLAGS_WORLD = FLAGS_USE_PHYSICS | FLAGS_PHANTOM | FLAGS_TEMPORARY_ON_REZ; typedef enum e_havok_joint_type { diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index e36764d02..f915faa36 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1,3 +1,5 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /** * @file llselectmgr.cpp * @brief A manager for selected objects and faces. @@ -51,6 +53,7 @@ // viewer includes #include "llagent.h" #include "llagentcamera.h" +#include "llattachmentsmgr.h" #include "llviewerwindow.h" #include "lldrawable.h" #include "llfloaterinspect.h" @@ -65,8 +68,8 @@ #include "llmenugl.h" #include "llmeshrepository.h" #include "llmutelist.h" -#include "llparcel.h" #include "llnotificationsutil.h" +#include "llparcel.h" // Rez under Land Group #include "llstatusbar.h" #include "llsurface.h" #include "lltool.h" @@ -82,7 +85,7 @@ #include "llviewermenu.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" +#include "llviewerparcelmgr.h" // Rez under Land Group #include "llviewerregion.h" #include "llviewerstats.h" #include "llvoavatarself.h" @@ -255,7 +258,7 @@ void LLSelectMgr::updateEffects() //keep reference grid objects active struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { LLDrawable* drawable = object->mDrawable; if (drawable) @@ -279,7 +282,7 @@ void LLSelectMgr::overrideObjectUpdates() //override any position updates from simulator on objects being edited struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* selectNode) + bool apply(LLSelectNode* selectNode) override { LLViewerObject* object = selectNode->getObject(); if (object && object->permMove() && !object->isPermanentEnforced()) @@ -524,17 +527,15 @@ BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) { BOOL object_found = FALSE; LLTool *tool = NULL; - if (!gNoRender) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - // It's possible that the tool is editing an object that is not selected - LLViewerObject* tool_editing_object = tool->getEditingObject(); - if( tool_editing_object && tool_editing_object->mID == id) - { - tool->stopEditing(); - object_found = TRUE; - } + tool = LLToolMgr::getInstance()->getCurrentTool(); + + // It's possible that the tool is editing an object that is not selected + LLViewerObject* tool_editing_object = tool->getEditingObject(); + if( tool_editing_object && tool_editing_object->mID == id) + { + tool->stopEditing(); + object_found = TRUE; } // Iterate through selected objects list and kill the object @@ -650,7 +651,7 @@ bool LLSelectMgr::enableLinkObjects() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { LLViewerObject *root_object = (object == NULL) ? NULL : object->getRootEdit(); return object->permModify() && !object->isPermanentEnforced() && @@ -967,6 +968,10 @@ LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 iter != objects.end(); ++iter) { LLViewerObject* cur_objectp = *iter; + if(!cur_objectp || cur_objectp->isDead()) + { + continue; + } LLSelectNode* nodep = new LLSelectNode(cur_objectp, FALSE); nodep->selectTE(face, TRUE); mHoverObjects->addNodeAtEnd(nodep); @@ -1546,7 +1551,7 @@ void LLSelectMgr::cleanup() struct LLSelectMgrSendFunctor : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -1584,8 +1589,12 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) LLViewerInventoryItem* mItem; LLUUID mImageID; f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} - bool apply(LLViewerObject* objectp, S32 te) + bool apply(LLViewerObject* objectp, S32 te) override { + if(objectp && !objectp->permModify()) + { + return false; + } if (mItem) { if (te == -1) // all faces @@ -1616,23 +1625,26 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) } return true; } - } setfunc(item, imageid); + }; + + f setfunc(item, imageid); getSelection()->applyToTEs(&setfunc); struct g : public LLSelectedObjectFunctor { LLViewerInventoryItem* mItem; g(LLViewerInventoryItem* item) : mItem(item) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (!mItem) { object->sendTEUpdate(); - // 1 particle effect per object // if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) { // + // 1 particle effect per object LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE); effectp->setSourceObject(gAgentAvatarp); effectp->setTargetObject(object); @@ -1657,7 +1669,7 @@ void LLSelectMgr::selectionSetColor(const LLColor4 &color) { LLColor4 mColor; f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1681,7 +1693,7 @@ void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color) { LLColor4 mColor; f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1708,7 +1720,7 @@ void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha) { F32 mAlpha; f(const F32& a) : mAlpha(a) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1732,7 +1744,7 @@ void LLSelectMgr::selectionRevertColors() { LLObjectSelectionHandle mSelectedObjects; f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1753,13 +1765,47 @@ void LLSelectMgr::selectionRevertColors() getSelection()->applyToObjects(&sendfunc); } +void LLSelectMgr::selectionRevertShinyColors() +{ + struct f : public LLSelectedTEFunctor + { + LLObjectSelectionHandle mSelectedObjects; + f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} + bool apply(LLViewerObject* object, S32 te) override + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects->findNode(object); + if (nodep && te < (S32)nodep->mSavedShinyColors.size()) + { + LLColor4 color = nodep->mSavedShinyColors[te]; + // update viewer side color in anticipation of update from simulator + LLMaterialPtr old_mat = object->getTE(te)->getMaterialParams(); + if (!old_mat.isNull()) + { + LLMaterialPtr new_mat = gFloaterTools->getPanelFace()->createDefaultMaterial(old_mat); + new_mat->setSpecularLightColor(color); + object->getTE(te)->setMaterialParams(new_mat); + LLMaterialMgr::getInstance()->put(object->getID(), te, *new_mat); + } + } + } + return true; + } + } setfunc(mSelectedObjects); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + BOOL LLSelectMgr::selectionRevertTextures() { struct f : public LLSelectedTEFunctor { LLObjectSelectionHandle mSelectedObjects; f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1799,7 +1845,7 @@ void LLSelectMgr::selectionSetBumpmap(U8 bumpmap) { U8 mBump; f(const U8& b) : mBump(b) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1821,7 +1867,7 @@ void LLSelectMgr::selectionSetTexGen(U8 texgen) { U8 mTexgen; f(const U8& t) : mTexgen(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1844,7 +1890,7 @@ void LLSelectMgr::selectionSetShiny(U8 shiny) { U8 mShiny; f(const U8& t) : mShiny(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1866,7 +1912,7 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) { U8 mFullbright; f(const U8& t) : mFullbright(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1882,7 +1928,8 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) { U8 mFullbright; g(const U8& t) : mFullbright(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -1915,7 +1962,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) U8 mMediaFlags; const LLSD &mMediaData; f(const U8& t, const LLSD& d) : mMediaFlags(t), mMediaData(d) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1931,7 +1978,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) llassert(mMediaData.isMap()); const LLTextureEntry *texture_entry = object->getTE(te); if (!mMediaData.isMap() || - (NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY)) + ((NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY))) { // skip adding/updating media } @@ -1959,7 +2006,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -1987,7 +2034,7 @@ void LLSelectMgr::selectionSetGlow(F32 glow) { F32 mGlow; f1(F32 glow) : mGlow(glow) {}; - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object->permModify()) { @@ -2001,7 +2048,7 @@ void LLSelectMgr::selectionSetGlow(F32 glow) struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2020,7 +2067,7 @@ void LLSelectMgr::selectionSetMaterialParams(LLSelectedTEMaterialFunctor* materi LLMaterialPtr mMaterial; f1(LLSelectedTEMaterialFunctor* material_func) : _material_func(material_func) {} - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object && object->permModify() && _material_func) { @@ -2040,7 +2087,7 @@ void LLSelectMgr::selectionSetMaterialParams(LLSelectedTEMaterialFunctor* materi struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2056,7 +2103,7 @@ void LLSelectMgr::selectionRemoveMaterial() { struct f1 : public LLSelectedTEFunctor { - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object->permModify()) { @@ -2071,7 +2118,7 @@ void LLSelectMgr::selectionRemoveMaterial() struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2112,7 +2159,7 @@ BOOL LLSelectMgr::selectionGetGlow(F32 *glow) F32 lglow = 0.f; struct f1 : public LLSelectedTEGetFunctor { - F32 get(LLViewerObject* object, S32 face) + F32 get(LLViewerObject* object, S32 face) override { return object->getTE(face)->getGlow(); } @@ -2130,7 +2177,8 @@ void LLSelectMgr::selectionSetPhysicsType(U8 type) { U8 mType; f(const U8& t) : mType(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2149,7 +2197,8 @@ void LLSelectMgr::selectionSetFriction(F32 friction) { F32 mFriction; f(const F32& friction) : mFriction(friction) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2168,7 +2217,8 @@ void LLSelectMgr::selectionSetGravity(F32 gravity ) { F32 mGravity; f(const F32& gravity) : mGravity(gravity) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2187,7 +2237,8 @@ void LLSelectMgr::selectionSetDensity(F32 density ) { F32 mDensity; f(const F32& density ) : mDensity(density) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2206,7 +2257,8 @@ void LLSelectMgr::selectionSetRestitution(F32 restitution) { F32 mRestitution; f(const F32& restitution ) : mRestitution(restitution) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2229,7 +2281,8 @@ void LLSelectMgr::selectionSetMaterial(U8 material) { U8 mMaterial; f(const U8& t) : mMaterial(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2251,7 +2304,8 @@ BOOL LLSelectMgr::selectionAllPCode(LLPCode code) { LLPCode mCode; f(const LLPCode& t) : mCode(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->getPCode() != mCode) { @@ -2302,6 +2356,7 @@ void LLSelectMgr::selectionSetIncludeInSearch(bool include_in_search) "ObjectIncludeInSearch", packAgentAndSessionID, packObjectIncludeInSearch, + logNoOp, &include_in_search, SEND_ONLY_ROOTS); } @@ -2321,7 +2376,8 @@ BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action) { U8 mAction; f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if ( mAction != object->getClickAction()) { @@ -2340,7 +2396,8 @@ void LLSelectMgr::selectionSetClickAction(U8 action) { U8 mAction; f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { object->setClickAction(mAction); return true; @@ -2351,6 +2408,7 @@ void LLSelectMgr::selectionSetClickAction(U8 action) sendListToRegions("ObjectClickAction", packAgentAndSessionID, packObjectClickAction, + logNoOp, &action, SEND_INDIVIDUALS); } @@ -2386,7 +2444,7 @@ void LLSelectMgr::sendGodlikeRequest(const std::string& request, const std::stri } else { - sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS); + sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, logNoOp, &data, SEND_ONLY_ROOTS); } } @@ -2414,6 +2472,23 @@ void LLSelectMgr::packGodlikeHead(void* user_data) } } +// static +void LLSelectMgr::logNoOp(LLSelectNode* node, void *) +{ +} + +// static +void LLSelectMgr::logAttachmentRequest(LLSelectNode* node, void *) +{ + LLAttachmentsMgr::instance().onAttachmentRequested(node->mItemID); +} + +// static +void LLSelectMgr::logDetachRequest(LLSelectNode* node, void *) +{ + LLAttachmentsMgr::instance().onDetachRequested(node->mItemID); +} + // static void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *) { @@ -2431,7 +2506,7 @@ void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter) { F32 mRepeatsPerMeter; f(const F32& t) : mRepeatsPerMeter(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) @@ -2681,6 +2756,35 @@ BOOL LLSelectMgr::selectGetRootsModify() return TRUE; } +//----------------------------------------------------------------------------- +// selectGetSameRegion() - return TRUE if all objects are in same region +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetSameRegion() +{ + if (getSelection()->isEmpty()) + { + return TRUE; + } + LLViewerObject* object = getSelection()->getFirstObject(); + if (!object) + { + return FALSE; + } + LLViewerRegion* current_region = object->getRegion(); + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + object = node->getObject(); + if (!node->mValid || !object || current_region != object->getRegion()) + { + return FALSE; + } + } + + return TRUE; +} //----------------------------------------------------------------------------- // selectGetNonPermanentEnforced() - return TRUE if all objects are not @@ -3537,22 +3641,20 @@ bool LLSelectMgr::confirmDelete(const LLSD& notification, const LLSD& response, // attempt to derez into the trash. LLDeRezInfo info(DRD_TRASH, trash_id); LLSelectMgr::getInstance()->sendListToRegions("DeRezObject", - packDeRezHeader, - packObjectLocalID, - (void*) &info, - SEND_ONLY_ROOTS); + packDeRezHeader, + packObjectLocalID, + logNoOp, + (void*) &info, + SEND_ONLY_ROOTS); // VEFFECT: Delete Object - one effect for all deletes - if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) + if (!gSavedSettings.getBOOL("DisablePointAtAndBeam") && (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD)) { - if (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD) - { - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - F32 duration = 0.5f; - duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; - effectp->setDuration(duration); - } + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + F32 duration = 0.5f; + duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; + effectp->setDuration(duration); } gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); @@ -3577,6 +3679,7 @@ void LLSelectMgr::selectForceDelete() "ObjectDelete", packDeleteHeader, packObjectLocalID, + logNoOp, (void*)TRUE, SEND_ONLY_ROOTS); } @@ -3750,7 +3853,7 @@ void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy) data.offset = offset; data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS); if (select_copy) { @@ -3805,7 +3908,7 @@ void LLSelectMgr::repeatDuplicate() data.offset = LLVector3::zero; data.flags = 0x0; - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS); // move current selection based on delta from duplication position and update duplication position for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); @@ -3884,7 +3987,7 @@ void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region, data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); sendListToRegions("ObjectDuplicateOnRay", - packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + packDuplicateOnRayHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS); if (select_copy) { @@ -3902,21 +4005,19 @@ void LLSelectMgr::packDuplicateOnRayHead(void *user_data) msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - LLUUID group_id = gAgent.getGroupID(); - // Apply always rez under owned land group setting for objects - // duplicated by shift-dragging or duplicating - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (gAgent.isInGroup(parcel->getGroupID())) - { - group_id = parcel->getGroupID(); - } - else if (gAgent.isInGroup(parcel->getOwnerID())) - { - group_id = parcel->getOwnerID(); - } + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if(gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } msg->addUUIDFast(_PREHASH_GroupID, group_id); @@ -3951,6 +4052,7 @@ void LLSelectMgr::sendMultipleUpdate(U32 type) "MultipleObjectUpdate", packAgentAndSessionID, packMultipleUpdate, + logNoOp, &type, send_type); } @@ -4014,7 +4116,7 @@ void LLSelectMgr::sendOwner(const LLUUID& owner_id, data.group_id = group_id; data.override = override; - sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS); } // static @@ -4038,7 +4140,7 @@ void LLSelectMgr::packOwnerHead(void *user_data) void LLSelectMgr::sendGroup(const LLUUID& group_id) { LLUUID local_group_id(group_id); - sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS); + sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, logNoOp, &local_group_id, SEND_ONLY_ROOTS); } @@ -4062,7 +4164,7 @@ void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, con LLBuyData buy; buy.mCategoryID = category_id; buy.mSaleInfo = sale_info; - sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS); + sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, logNoOp, &buy, SEND_ONLY_ROOTS); } // static @@ -4106,7 +4208,7 @@ void LLSelectMgr::selectionSetObjectPermissions(U8 field, data.mMask = mask; data.mOverride = override; - sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, logNoOp, &data, SEND_ONLY_ROOTS); } void LLSelectMgr::packPermissionsHead(void* user_data) @@ -4149,6 +4251,7 @@ void LLSelectMgr::deselectAll() "ObjectDeselect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); @@ -4179,6 +4282,7 @@ void LLSelectMgr::deselectAllForStandingUp() "ObjectDeselect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); @@ -4290,19 +4394,21 @@ void LLSelectMgr::selectionSetObjectName(const std::string& name) std::string name_copy(name); // we only work correctly if 1 object is selected. - if(mSelectedObjects->getRootObjectCount() /*== 1*/) // Singu Note: It was requested that the user be able to bulk rename + if(mSelectedObjects->getRootObjectCount() >= 1) // { sendListToRegions("ObjectName", packAgentAndSessionID, packObjectName, + logNoOp, (void*)(&name_copy), SEND_ONLY_ROOTS); } - else if(mSelectedObjects->getObjectCount() /*== 1*/) + else if(mSelectedObjects->getObjectCount() >= 1) // { sendListToRegions("ObjectName", packAgentAndSessionID, packObjectName, + logNoOp, (void*)(&name_copy), SEND_INDIVIDUALS); } @@ -4313,19 +4419,21 @@ void LLSelectMgr::selectionSetObjectDescription(const std::string& desc) std::string desc_copy(desc); // we only work correctly if 1 object is selected. - if (mSelectedObjects->getRootObjectCount() /*== 1*/) // Singu Note: It was requested that the user be able to bulk change description + if(mSelectedObjects->getRootObjectCount() >= 1) // { sendListToRegions("ObjectDescription", packAgentAndSessionID, packObjectDescription, + logNoOp, (void*)(&desc_copy), SEND_ONLY_ROOTS); } - else if (mSelectedObjects->getObjectCount() /*== 1*/) + else if(mSelectedObjects->getObjectCount() >= 1) // { sendListToRegions("ObjectDescription", packAgentAndSessionID, packObjectDescription, + logNoOp, (void*)(&desc_copy), SEND_INDIVIDUALS); } @@ -4339,6 +4447,7 @@ void LLSelectMgr::selectionSetObjectCategory(const LLCategory& category) sendListToRegions("ObjectCategory", packAgentAndSessionID, packObjectCategory, + logNoOp, (void*)(&category), SEND_ONLY_ROOTS); } @@ -4348,6 +4457,7 @@ void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info) sendListToRegions("ObjectSaleInfo", packAgentAndSessionID, packObjectSaleInfo, + logNoOp, (void*)(&sale_info), SEND_ONLY_ROOTS); } @@ -4381,10 +4491,14 @@ void LLSelectMgr::sendAttach(U8 attachment_point, bool replace) "ObjectAttach", packAgentIDAndSessionAndAttachment, packObjectIDAndRotation, + logAttachmentRequest, &attachment_point, SEND_ONLY_ROOTS ); if (!build_mode) { + // After "ObjectAttach" server will unsubscribe us from properties updates + // so either deselect objects or resend selection after attach packet reaches server + // In case of build_mode LLPanelObjectInventory::refresh() will deal with selection deselectAll(); } } @@ -4401,6 +4515,7 @@ void LLSelectMgr::sendDetach() "ObjectDetach", packAgentAndSessionID, packObjectLocalID, + logDetachRequest, NULL, SEND_ONLY_ROOTS ); } @@ -4417,6 +4532,7 @@ void LLSelectMgr::sendDropAttachment() "ObjectDrop", packAgentAndSessionID, packObjectLocalID, + logDetachRequest, NULL, SEND_ONLY_ROOTS); } @@ -4436,6 +4552,7 @@ void LLSelectMgr::sendLink() "ObjectLink", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_ONLY_ROOTS); } @@ -4451,7 +4568,7 @@ void LLSelectMgr::sendDelink() { //on delink, any modifyable object should f() {} - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -4473,6 +4590,7 @@ void LLSelectMgr::sendDelink() "ObjectDelink", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); } @@ -4525,6 +4643,7 @@ void LLSelectMgr::sendSelect() "ObjectSelect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); } @@ -4546,7 +4665,7 @@ void LLSelectMgr::selectionDump() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { object->dump(); return true; @@ -4559,7 +4678,7 @@ void LLSelectMgr::saveSelectedObjectColors() { struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->saveColors(); return true; @@ -4568,12 +4687,25 @@ void LLSelectMgr::saveSelectedObjectColors() getSelection()->applyToNodes(&func); } +void LLSelectMgr::saveSelectedShinyColors() +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) override + { + node->saveShinyColors(); + return true; + } + } func; + getSelection()->applyToNodes(&func); +} + void LLSelectMgr::saveSelectedObjectTextures() { // invalidate current selection so we update saved textures struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->mValid = FALSE; return true; @@ -4601,7 +4733,8 @@ void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type) EActionType mActionType; LLSelectMgr* mManager; f(EActionType a, LLSelectMgr* p) : mActionType(a), mManager(p) {} - virtual bool apply(LLSelectNode* selectNode) + + bool apply(LLSelectNode* selectNode) override { LLViewerObject* object = selectNode->getObject(); if (!object) @@ -4661,13 +4794,21 @@ struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor LLSelectMgrApplyFlags(U32 flags, BOOL state) : mFlags(flags), mState(state) {} U32 mFlags; BOOL mState; - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { - if ( object->permModify() && // preemptive permissions check - object->isRoot()) // don't send for child objects + if ( object->permModify()) { - object->setFlags( mFlags, mState); - } + if (object->isRoot()) // don't send for child objects + { + object->setFlags( mFlags, mState); + } + else if (FLAGS_WORLD & mFlags && ((LLViewerObject*)object->getRoot())->isSelected()) + { + // FLAGS_WORLD are shared by all items in linkset + object->setFlagsWithoutUpdate(FLAGS_WORLD & mFlags, mState); + } + }; return true; } }; @@ -4742,23 +4883,19 @@ void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data) // static void LLSelectMgr::packDuplicateHeader(void* data) { - LLUUID group_id(gAgent.getGroupID()); - - // Apply always rez under owned land group setting for objects - // duplicated by shift-dragging or duplicating - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (gAgent.isInGroup(parcel->getGroupID())) - { - group_id = parcel->getGroupID(); - } - else if (gAgent.isInGroup(parcel->getOwnerID())) - { - group_id = parcel->getOwnerID(); - } + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if(gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } - packAgentAndSessionAndGroupID(&group_id); LLDuplicateData* dup_data = (LLDuplicateData*) data; @@ -4917,10 +5054,12 @@ void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data) void LLSelectMgr::sendListToRegions(const std::string& message_name, void (*pack_header)(void *user_data), void (*pack_body)(LLSelectNode* node, void *user_data), + void (*log_func)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type) { LLSelectNode* node; + LLSelectNode* linkset_root = NULL; LLViewerRegion* last_region; LLViewerRegion* current_region; @@ -4928,10 +5067,12 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, S32 packets_sent = 0; S32 objects_in_this_packet = 0; + bool link_operation = message_name == "ObjectLink"; + //clear update override data (allow next update through) struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->mLastPositionLocal.setVec(0,0,0); node->mLastRotation = LLQuaternion(); @@ -4947,7 +5088,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, { std::queue& nodes_to_send; push_all(std::queue& n) : nodes_to_send(n) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { if (node->getObject()) { @@ -4961,7 +5103,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, std::queue& nodes_to_send; bool mRoots; push_some(std::queue& n, bool roots) : nodes_to_send(n), mRoots(roots) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { if (node->getObject()) { @@ -5036,8 +5179,16 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, && (! gMessageSystem->isSendFull(NULL)) && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) { + if (link_operation && linkset_root == NULL) + { + // linksets over 254 will be split into multiple messages, + // but we need to provide same root for all messages or we will get separate linksets + linkset_root = node; + } // add another instance of the body of the data (*pack_body)(node, user_data); + // do any related logging + (*log_func)(node, user_data); ++objects_sent; ++objects_in_this_packet; @@ -5062,6 +5213,22 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, gMessageSystem->newMessage(message_name.c_str()); (*pack_header)(user_data); + if (linkset_root != NULL) + { + if (current_region != last_region) + { + // root should be in one region with the child, reset it + linkset_root = NULL; + } + else + { + // add root instance into new message + (*pack_body)(linkset_root, user_data); + ++objects_sent; + ++objects_in_this_packet; + } + } + // don't move to the next object, we still need to add the // body data. } @@ -5186,7 +5353,8 @@ void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data { LLUUID mID; f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { return (node->getObject() && node->getObject()->mID == mID); } @@ -5339,7 +5507,8 @@ void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** use { LLUUID mID; f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { return (node->getObject() && node->getObject()->mID == mID); } @@ -5417,7 +5586,7 @@ void LLSelectMgr::updateSilhouettes() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { object->setChanged(LLXform::SILHOUETTE); return true; @@ -5718,9 +5887,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) } LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID(); - // //for (S32 pass = 0; pass < 2; pass++) - // { for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); iter != mSelectedObjects->end(); iter++) @@ -5837,6 +6004,7 @@ LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) { selectAllTEs(FALSE); saveColors(); + saveShinyColors(); } LLSelectNode::LLSelectNode(const LLSelectNode& nodep) @@ -5882,6 +6050,11 @@ LLSelectNode::LLSelectNode(const LLSelectNode& nodep) { mSavedColors.push_back(*color_iter); } + mSavedShinyColors.clear(); + for (color_iter = nodep.mSavedShinyColors.begin(); color_iter != nodep.mSavedShinyColors.end(); ++color_iter) + { + mSavedShinyColors.push_back(*color_iter); + } saveTextures(nodep.mSavedTextures); } @@ -5934,6 +6107,11 @@ S32 LLSelectNode::getLastSelectedTE() return mLastTESelected; } +S32 LLSelectNode::getLastOperatedTE() +{ + return mLastTESelected; +} + LLViewerObject* LLSelectNode::getObject() { if (!mObject) @@ -5965,6 +6143,26 @@ void LLSelectNode::saveColors() } } +void LLSelectNode::saveShinyColors() +{ + if (mObject.notNull()) + { + mSavedShinyColors.clear(); + for (S32 i = 0; i < mObject->getNumTEs(); i++) + { + const LLMaterialPtr mat = mObject->getTE(i)->getMaterialParams(); + if (!mat.isNull()) + { + mSavedShinyColors.push_back(LLColor4(mat->getSpecularLightColor())); + } + else + { + mSavedShinyColors.push_back(LLColor4::white); + } + } + } +} + void LLSelectNode::saveTextures(const uuid_vec_t& textures) { if (mObject.notNull()) @@ -6195,7 +6393,6 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) { gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); - if (shader) { gGL.diffuseColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); @@ -6228,6 +6425,7 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) LLGLEnable offset(GL_POLYGON_OFFSET_LINE); gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.diffuseColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); glPolygonOffset(3.f, 3.f); glLineWidth(3.f); @@ -6343,10 +6541,11 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.begin(LLRender::LINES); { + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + for(S32 i = 0; i < (S32)mSilhouetteVertices.size(); i += 2) { u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); gGL.texCoord2f( u_coord, v_coord ); gGL.vertex3fv( mSilhouetteVertices[i].mV); u_coord += u_divisor * LLSelectMgr::sHighlightUScale; @@ -6433,11 +6632,6 @@ void dialog_refresh_all() // and call into this function. Yuck. LLSelectMgr::getInstance()->mUpdateSignal(); - if (gNoRender) - { - return; - } - // *TODO: Eliminate all calls into outside classes below, make those // objects register with the update signal. @@ -6678,7 +6872,7 @@ void LLSelectMgr::undo() { BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); + sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); } //----------------------------------------------------------------------------- @@ -6696,7 +6890,7 @@ void LLSelectMgr::redo() { BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); + sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); } //----------------------------------------------------------------------------- @@ -6811,7 +7005,7 @@ void LLSelectMgr::validateSelection() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (!LLSelectMgr::getInstance()->canSelectObject(object)) { @@ -7039,7 +7233,7 @@ F32 LLObjectSelection::getSelectedLinksetCost() LLSelectNode* node = *iter; LLViewerObject* object = node->getObject(); - if (object) + if (object && !object->isAttachment()) { LLViewerObject* root = static_cast(object->getRoot()); if (root) @@ -7528,7 +7722,7 @@ LLSelectNode* LLObjectSelection::getFirstMoveableNode(BOOL get_root_first) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permMove() && !obj->isPermanentEnforced(); @@ -7545,7 +7739,7 @@ LLViewerObject* LLObjectSelection::getFirstCopyableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permCopy() && !obj->isAttachment(); @@ -7564,7 +7758,7 @@ LLViewerObject* LLObjectSelection::getFirstDeleteableObject() struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); // you can delete an object if you are the owner @@ -7593,7 +7787,7 @@ LLViewerObject* LLObjectSelection::getFirstEditableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permModify(); @@ -7609,7 +7803,7 @@ LLViewerObject* LLObjectSelection::getFirstMoveableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permMove() && !obj->isPermanentEnforced(); diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 145c25005..13eb4e1db 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -146,6 +146,7 @@ public: void selectTE(S32 te_index, BOOL selected); BOOL isTESelected(S32 te_index); S32 getLastSelectedTE(); + S32 getLastOperatedTE(); S32 getTESelectMask() { return mTESelectMask; } void renderOneWireframe(const LLColor4& color); void renderOneSilhouette(const LLColor4 &color); @@ -155,6 +156,7 @@ public: void setObject(LLViewerObject* object); // *NOTE: invalidate stored textures and colors when # faces change void saveColors(); + void saveShinyColors(); void saveTextures(const uuid_vec_t& textures); void saveTextureScaleRatios(LLRender::eTexIndex index_to_query); @@ -191,6 +193,7 @@ public: std::string mSitName; U64 mCreationDate; std::vector mSavedColors; + std::vector mSavedShinyColors; uuid_vec_t mSavedTextures; std::vector mTextureScaleRatios; std::vector mSilhouetteVertices; // array of vertices to render silhouette of object @@ -206,6 +209,7 @@ protected: class LLObjectSelection : public LLRefCount { friend class LLSelectMgr; + friend class LLSafeHandle; protected: ~LLObjectSelection(); @@ -218,7 +222,7 @@ public: { bool operator()(LLSelectNode* node) { - return (node->getObject() != NULL); + return (node->getObject() != nullptr); } }; typedef boost::filter_iterator iterator; @@ -229,7 +233,7 @@ public: { bool operator()(LLSelectNode* node) { - return (node->getObject() != NULL) && node->mValid; + return (node->getObject() != nullptr) && node->mValid; } }; typedef boost::filter_iterator valid_iterator; @@ -267,8 +271,8 @@ public: BOOL isEmpty() const; - LLSelectNode* getFirstNode(LLSelectedNodeFunctor* func = NULL); - LLSelectNode* getFirstRootNode(LLSelectedNodeFunctor* func = NULL, BOOL non_root_ok = FALSE); + LLSelectNode* getFirstNode(LLSelectedNodeFunctor* func = nullptr); + LLSelectNode* getFirstRootNode(LLSelectedNodeFunctor* func = nullptr, BOOL non_root_ok = FALSE); LLViewerObject* getFirstSelectedObject(LLSelectedNodeFunctor* func, BOOL get_parent = FALSE); LLViewerObject* getFirstObject(); LLViewerObject* getFirstRootObject(BOOL non_root_ok = FALSE); @@ -298,8 +302,8 @@ public: F32 getSelectedLinksetPhysicsCost(); S32 getSelectedObjectRenderCost(); - F32 getSelectedObjectStreamingCost(S32* total_bytes = NULL, S32* visible_bytes = NULL); - U32 getSelectedObjectTriangleCount(S32* vcount = NULL); + F32 getSelectedObjectStreamingCost(S32* total_bytes = nullptr, S32* visible_bytes = nullptr); + U32 getSelectedObjectTriangleCount(S32* vcount = nullptr); S32 getTECount(); S32 getRootObjectCount(); @@ -320,6 +324,15 @@ public: bool applyToRootNodes(LLSelectedNodeFunctor* func, bool firstonly = false); bool applyToNodes(LLSelectedNodeFunctor* func, bool firstonly = false); + /* + * Used to apply (no-copy) textures to the selected object or + * selected face/faces of the object. + * This method moves (no-copy) texture to the object's inventory + * and doesn't make copy of the texture for each face. + * Then this only texture is used for all selected faces. + */ + void applyNoCopyTextureToTEs(LLViewerInventoryItem* item); + ESelectType getSelectType() const { return mSelectType; } private: @@ -330,10 +343,8 @@ private: void deleteAllNodes(); void cleanupNodes(); - -private: list_t mList; - const LLObjectSelection &operator=(const LLObjectSelection &); + const LLObjectSelection& operator=(const LLObjectSelection&) = delete; LLPointer mPrimaryObject; std::map, LLSelectNode*> mSelectNodeMap; @@ -347,6 +358,9 @@ typedef LLSafeHandle LLObjectSelectionHandle; extern template class LLSelectMgr* LLSingleton::getInstance(); #endif +// For use with getFirstTest() +struct LLSelectGetFirstTest; + class LLSelectMgr : public LLEditMenuHandler, public LLSingleton { public: @@ -380,20 +394,19 @@ public: static void cleanupGlobals(); // LLEditMenuHandler interface - virtual BOOL canUndo() const; - virtual void undo(); + BOOL canUndo() const override; + void undo() override; - virtual BOOL canRedo() const; - virtual void redo(); + BOOL canRedo() const override; + void redo() override; - virtual BOOL canDoDelete() const; - virtual void doDelete(); + BOOL canDoDelete() const override; + void doDelete() override; - virtual void selectAll(); - virtual BOOL canSelectAll() const; - - virtual void deselect(); - virtual BOOL canDeselect() const; + void selectAll() override; + BOOL canSelectAll() const override; + void deselect() override; + BOOL canDeselect() const override; virtual void duplicate(); virtual BOOL canDuplicate() const; @@ -513,6 +526,7 @@ public: //////////////////////////////////////////////////////////////// void saveSelectedObjectTransform(EActionType action_type); void saveSelectedObjectColors(); + void saveSelectedShinyColors(); void saveSelectedObjectTextures(); // Sets which texture channel to query for scale and rot of display @@ -541,6 +555,7 @@ public: void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel void selectionRevertColors(); + void selectionRevertShinyColors(); BOOL selectionRevertTextures(); void selectionSetBumpmap( U8 bumpmap ); void selectionSetTexGen( U8 texgen ); @@ -584,6 +599,9 @@ public: BOOL selectGetRootsModify(); BOOL selectGetModify(); + // returns TRUE if all objects are in same region + BOOL selectGetSameRegion(); + // returns TRUE if is all objects are non-permanent-enforced BOOL selectGetRootsNonPermanentEnforced(); BOOL selectGetNonPermanentEnforced(); @@ -723,6 +741,7 @@ private: void sendListToRegions( const std::string& message_name, void (*pack_header)(void *user_data), void (*pack_body)(LLSelectNode* node, void *user_data), + void (*log_func)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type); @@ -758,6 +777,9 @@ private: static void packHingeHead(void *user_data); static void packPermissionsHead(void* user_data); static void packGodlikeHead(void* user_data); + static void logNoOp(LLSelectNode* node, void *user_data); + static void logAttachmentRequest(LLSelectNode* node, void *user_data); + static void logDetachRequest(LLSelectNode* node, void *user_data); static bool confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle); public: From bedb567ebab5189e8820a82c4b6e4963390adc2f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 23 Jan 2019 21:35:49 -0500 Subject: [PATCH 068/332] Don't RenderHiddenSelections, it makes mesh select bad. This change snuck into upstream in 2010 in a commit by Dave Parks titled Fix for bad zipping of meshes and add mesh icon back to repository. --- indra/newview/app_settings/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index dbaa6406e..2af8784f9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13800,7 +13800,7 @@ This should be as low as possible, but too low may break functionality Type Boolean Value - 1 + 0 RenderHideGroupTitle From b97179963877bedd04231b137940244435822bab Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 23 Jan 2019 22:23:15 -0500 Subject: [PATCH 069/332] Woah, woops! Fix double click crash!! --- indra/newview/llviewertexteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 8097f6e13..01d2014d2 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -875,7 +875,7 @@ BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) } } - return handleDoubleClick(x, y, mask); + return LLTextEditor::handleDoubleClick(x, y, mask); } From 546422609acce29ebe13e051590582dbbdf9f2fe Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 23 Jan 2019 23:37:54 -0500 Subject: [PATCH 070/332] Cleanup and simplify Script Count code --- indra/newview/llviewermenu.cpp | 45 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index aee2075ed..a4b1f4164 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3005,17 +3005,22 @@ class LLAvatarFreeze : public view_listener_t } }; +void do_script_count(bool del) +{ + if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) + { + if (ScriptCounter::getInstance(object->getID())) return; + ScriptCounter* sc = new ScriptCounter(del, object); + sc->requestInventories(); + // sc will destroy itself + } +} + class LLScriptCount : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) - { - if (ScriptCounter::getInstance(object->getID())) return true; - ScriptCounter* sc = new ScriptCounter(false, object); - sc->requestInventories(); - // sc will destroy itself - } + do_script_count(false); return true; } }; @@ -3024,13 +3029,7 @@ class LLScriptDelete : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) - { - if (ScriptCounter::getInstance(object->getID())) return true; - ScriptCounter* sc = new ScriptCounter(true, object); - sc->requestInventories(); - // sc will destroy itself - } + do_script_count(true); return true; } }; @@ -3051,20 +3050,20 @@ class LLObjectEnableScriptDelete : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + auto objects = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* object = objects->getPrimaryObject(); bool new_value = (object != NULL); if(new_value) - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + for (LLObjectSelection::root_iterator iter = objects->root_begin(); + iter != objects->root_end(); iter++) { LLSelectNode* selectNode = *iter; LLViewerObject* object = selectNode->getObject(); - if(object) - if(!object->permModify()) - { - new_value=false; - break; - } + if (object && !object->permModify()) + { + new_value=false; + break; + } } gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); From 3bdf6e553db20a1dc312e12a80797f035f93e3c7 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 24 Jan 2019 11:57:44 -0500 Subject: [PATCH 071/332] Add InactiveFloaterTransparency and ActiveFloaterTransparency settings Dear Users, This removes FloaterUnfocusedBackgroundOpaque, to achieve the same effect, modify InactiveFloaterTransparency, 1.5 should do the trick. PS: The settings are in the Vanity tab at the bottom. Now for developer nonsense: Also sync a bunch of the UI code with upstream Alchemy for opts and shtuff. Also let's translate the tooltips on the buttons on the top right of floaters Translators note that not all new strings have been translated yet as not all are upstream. --- indra/llui/llfloater.cpp | 175 ++++++++++++----- indra/llui/llfloater.h | 47 +++-- indra/llui/llfocusmgr.cpp | 63 +++--- indra/llui/llpanel.cpp | 8 +- indra/llui/lluictrl.cpp | 125 ++++++++---- indra/llui/lluictrl.h | 64 +++--- indra/newview/app_settings/settings.xml | 33 ++-- indra/newview/ascentprefsvan.cpp | 6 +- indra/newview/ascentprefsvan.h | 2 +- indra/newview/llappviewer.cpp | 32 ++- .../de/panel_preferences_ascent_vanity.xml | 1 - .../newview/skins/default/xui/de/strings.xml | 27 +++ .../en-us/panel_preferences_ascent_vanity.xml | 3 +- .../skins/default/xui/en-us/strings.xml | 12 ++ .../es/panel_preferences_ascent_vanity.xml | 1 - .../newview/skins/default/xui/es/strings.xml | 27 +++ .../fr/panel_preferences_ascent_vanity.xml | 1 - .../newview/skins/default/xui/it/strings.xml | 170 ++++++++++++++++ .../newview/skins/default/xui/pt/strings.xml | 182 +++++++++++++++++- 19 files changed, 778 insertions(+), 201 deletions(-) diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index dac5a0282..f5fa3ece2 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -35,8 +35,6 @@ #include "linden_common.h" -#include "llmultifloater.h" - #include "llfocusmgr.h" #include "lluictrlfactory.h" @@ -56,10 +54,13 @@ #include "llcontrol.h" #include "lltabcontainer.h" #include "v2math.h" +#include "lltrans.h" +#include "llmultifloater.h" #include "llfasttimer.h" #include "airecursive.h" #include "llnotifications.h" + const S32 MINIMIZED_WIDTH = 160; const S32 CLOSE_BOX_FROM_TOP = 1; // use this to control "jumping" behavior when Ctrl-Tabbing @@ -104,14 +105,14 @@ std::string LLFloater::sButtonNames[BUTTON_COUNT] = std::string LLFloater::sButtonToolTips[BUTTON_COUNT] = { #ifdef LL_DARWIN - "Close (Cmd-W)", //BUTTON_CLOSE + "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE #else - "Close (Ctrl-W)", //BUTTON_CLOSE + "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE #endif - "Restore", //BUTTON_RESTORE - "Minimize", //BUTTON_MINIMIZE - "Tear Off", //BUTTON_TEAR_OFF - "Edit", //BUTTON_EDIT + "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE + "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE + "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF + "BUTTON_EDIT", //"Edit", //BUTTON_EDIT }; @@ -130,6 +131,31 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; +//static +void LLFloater::initClass() +{ + // translate tooltips for floater buttons + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + sButtonToolTips[i] = LLTrans::getString( sButtonToolTips[i] ); + } + + LLControlVariable* ctrl = LLUI::sConfigGroup->getControl("ActiveFloaterTransparency"); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); + updateActiveFloaterTransparency(); + } + + ctrl = LLUI::sConfigGroup->getControl("InactiveFloaterTransparency"); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); + updateInactiveFloaterTransparency(); + } + +} + LLFloater::LLFloater() : //FIXME: we should initialize *all* member variables here LLPanel(), mAutoFocus(TRUE), @@ -355,6 +381,18 @@ void LLFloater::initFloater(const std::string& title, } } +// static +void LLFloater::updateActiveFloaterTransparency() +{ + sActiveControlTransparency = LLUI::sConfigGroup->getF32("ActiveFloaterTransparency"); +} + +// static +void LLFloater::updateInactiveFloaterTransparency() +{ + sInactiveControlTransparency = LLUI::sConfigGroup->getF32("InactiveFloaterTransparency"); +} + void LLFloater::addResizeCtrls() { // Resize bars (sides) @@ -720,11 +758,6 @@ void LLFloater::applyRectControl() void LLFloater::applyTitle() { - if (gNoRender) - { - return; - } - if (!mDragHandle) { return; @@ -753,7 +786,7 @@ void LLFloater::setTitle( const std::string& title ) applyTitle(); } -std::string LLFloater::getTitle() +std::string LLFloater::getTitle() const { if (mTitle.empty()) { @@ -771,7 +804,7 @@ void LLFloater::setShortTitle( const std::string& short_title ) applyTitle(); } -std::string LLFloater::getShortTitle() +std::string LLFloater::getShortTitle() const { if (mShortTitle.empty()) { @@ -783,8 +816,6 @@ std::string LLFloater::getShortTitle() } } - - BOOL LLFloater::canSnapTo(const LLView* other_view) { if (NULL == other_view) @@ -1023,7 +1054,8 @@ void LLFloater::setFocus( BOOL b ) if (b) { // only push focused floaters to front of stack if not in midst of ctrl-tab cycle - if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode()) + LLFloaterView * parent = dynamic_cast(getParent()); + if (!getHost() && parent && !parent->getCycleMode()) { if (!isFrontmost()) { @@ -1040,6 +1072,7 @@ void LLFloater::setFocus( BOOL b ) last_focus->setFocus(TRUE); } } + updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); } // virtual @@ -1081,8 +1114,9 @@ void LLFloater::setForeground(BOOL front) releaseFocus(); } - if (front || !LLUI::sConfigGroup->getBOOL("FloaterUnfocusedBackgroundOpaque")) // Singu Note: This can be removed when InactiveFloaterTransparency is added - setBackgroundOpaque( front ); + setBackgroundOpaque( front ); + // Singu Note: Upstream isn't doing this, I can't see where they were actually going inactive. Maybe setFocus(false) isn't being called, but we have parity there.. + updateTransparency(front || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1191,7 +1225,7 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp) floaterp->mDependeeHandle = LLHandle(); } -BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index) +BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) { if( mButtonsEnabled[index] ) { @@ -1297,7 +1331,14 @@ void LLFloater::setFrontmost(BOOL take_focus) { // there are more than one floater view // so we need to query our parent directly - ((LLFloaterView*)getParent())->bringToFront(this, take_focus); + LLFloaterView * parent = dynamic_cast( getParent() ); + if (parent) + { + parent->bringToFront(this, take_focus); + } + + // Make sure to set the appropriate transparency type (STORM-732). + updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1432,42 +1473,45 @@ void LLFloater::onClickClose() // virtual void LLFloater::draw() { + const F32 alpha = getCurrentTransparency(); + // draw background if( isBackgroundVisible() ) { + drawShadow(this); + S32 left = LLPANEL_BORDER_WIDTH; S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH; S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH; S32 bottom = LLPANEL_BORDER_WIDTH; - static LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow"); - static F32 shadow_offset = (F32)LLUI::sConfigGroup->getS32("DropShadowFloater"); - if (!isBackgroundOpaque()) - { - shadow_offset *= 0.2f; - shadow_color.mV[VALPHA] *= 0.5f; - } - gl_drop_shadow(left, top, right, bottom, - shadow_color, - ll_round(shadow_offset)); - // No transparent windows in simple UI + LLColor4 color; if (isBackgroundOpaque()) { - gl_rect_2d( left, top, right, bottom, getBackgroundColor() ); + color = getBackgroundColor(); } else { - gl_rect_2d( left, top, right, bottom, getTransparentColor() ); + color = getTransparentColor(); } - if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getCurrentTitle().empty()) { + // We're not using images, use old-school flat colors + gl_rect_2d( left, top, right, bottom, color % alpha ); + // draw highlight on title bar to indicate focus. RDW - const LLFontGL* font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF ); - LLRect r = getRect(); - gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, - LLUI::sColorsGroup->getColor("TitleBarFocusColor"), 0, TRUE); + if(gFocusMgr.childHasKeyboardFocus(this) + && !getIsChrome() + && !getCurrentTitle().empty()) + { + static auto titlebar_focus_color = LLUI::sColorsGroup->getColor("TitleBarFocusColor"); + + const LLFontGL* font = LLFontGL::getFontSansSerif(); + LLRect r = getRect(); + gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, + titlebar_focus_color % alpha, 0, TRUE); + } } } @@ -1524,6 +1568,49 @@ void LLFloater::draw() } } +void LLFloater::drawShadow(LLPanel* panel) +{ + S32 left = LLPANEL_BORDER_WIDTH; + S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; + S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; + S32 bottom = LLPANEL_BORDER_WIDTH; + + static LLUICachedControl shadow_offset_S32 ("DropShadowFloater", 0); + static LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow"); + F32 shadow_offset = (F32)shadow_offset_S32; + + if (!panel->isBackgroundOpaque()) + { + shadow_offset *= 0.2f; + shadow_color.mV[VALPHA] *= 0.5f; + } + gl_drop_shadow(left, top, right, bottom, + shadow_color % getCurrentTransparency(), + ll_round(shadow_offset)); +} + +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ + if (view) + { + if (view->isCtrl()) + { + static_cast(view)->setTransparencyType(transparency_type); + } + + for (LLView* pChild : *view->getChildList()) + { + if ((pChild->getChildCount()) || (pChild->isCtrl())) + updateTransparency(pChild, transparency_type); + } + } +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ + updateTransparency(this, transparency_type); +} + void LLFloater::setCanMinimize(BOOL can_minimize) { // if removing minimize/restore button programmatically, @@ -2319,7 +2406,7 @@ LLRect LLFloaterView::getSnapRect() const return snap_rect; } -LLFloater *LLFloaterView::getFocusedFloater() +LLFloater *LLFloaterView::getFocusedFloater() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { @@ -2332,7 +2419,7 @@ LLFloater *LLFloaterView::getFocusedFloater() return NULL; } -LLFloater *LLFloaterView::getFrontmost() +LLFloater *LLFloaterView::getFrontmost() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { @@ -2345,7 +2432,7 @@ LLFloater *LLFloaterView::getFrontmost() return NULL; } -LLFloater *LLFloaterView::getBackmost() +LLFloater *LLFloaterView::getBackmost() const { LLFloater* back_most = NULL; for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) @@ -2380,7 +2467,7 @@ void LLFloaterView::syncFloaterTabOrder() } } -LLFloater* LLFloaterView::getParentFloater(LLView* viewp) +LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const { LLView* parentp = viewp->getParent(); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 0aeb11501..f53e2714f 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -94,16 +94,19 @@ class LLFloater : public LLPanel friend class LLFloaterView; friend class LLMultiFloater; public: - enum EFloaterButtons + enum EFloaterButton { - BUTTON_CLOSE, + BUTTON_CLOSE = 0, BUTTON_RESTORE, BUTTON_MINIMIZE, BUTTON_TEAR_OFF, BUTTON_EDIT, BUTTON_COUNT }; - + + // Load translations for tooltips for standard buttons + static void initClass(); + LLFloater(); LLFloater(const std::string& name); //simple constructor for data-driven initialization LLFloater( const std::string& name, const LLRect& rect, const std::string& title, @@ -164,9 +167,9 @@ public: void applyTitle(); const std::string& getCurrentTitle() const; void setTitle( const std::string& title); - std::string getTitle(); + std::string getTitle() const; void setShortTitle( const std::string& short_title ); - std::string getShortTitle(); + std::string getShortTitle() const; void setTitleVisible(bool visible); virtual void setMinimized(BOOL b); void moveResizeHandlesToFront(); @@ -174,8 +177,8 @@ public: void addDependentFloater(LLHandle dependent_handle, BOOL reposition = TRUE); LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } void removeDependentFloater(LLFloater* dependent); - BOOL isMinimized() { return mMinimized; } - BOOL isFrontmost(); + BOOL isMinimized() const { return mMinimized; } + virtual BOOL isFrontmost(); BOOL isDependent() { return !mDependeeHandle.isDead(); } void setCanMinimize(BOOL can_minimize); void setCanClose(BOOL can_close); @@ -199,6 +202,7 @@ public: virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); virtual void draw(); + virtual void drawShadow(LLPanel* panel); virtual void onOpen() {} @@ -244,6 +248,8 @@ public: static BOOL getEditModeEnabled() { return sEditModeEnabled; } static LLMultiFloater* getFloaterHost() {return sHostp; } + void updateTransparency(ETypeTransparency transparency_type); + void enableResizeCtrls(bool enable, bool width = true, bool height = true); protected: @@ -259,21 +265,29 @@ protected: void destroy() { die(); } // Don't call this directly. You probably want to call close(). JC private: - void setForeground(BOOL b); // called only by floaterview void cleanupHandles(); // remove handles to dead floaters void createMinimizeButton(); void updateButtons(); void buildButtons(); - BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index); + BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); void addResizeCtrls(); void layoutResizeCtrls(); + static void updateActiveFloaterTransparency(); + static void updateInactiveFloaterTransparency(); + void updateTransparency(LLView* view, ETypeTransparency transparency_type); + LLRect mExpandedRect; + +protected: LLDragHandle* mDragHandle; LLResizeBar* mResizeBar[4]; LLResizeHandle* mResizeHandle[4]; LLButton *mMinimizeButton; + + LLButton* mButtons[BUTTON_COUNT]; +private: BOOL mCanTearOff; BOOL mMinimized; BOOL mForeground; @@ -294,10 +308,7 @@ private: handle_set_t mDependents; bool mDragOnLeft; - BOOL mButtonsEnabled[BUTTON_COUNT]; -protected: - LLButton* mButtons[BUTTON_COUNT]; -private: + bool mButtonsEnabled[BUTTON_COUNT]; F32 mButtonScale; BOOL mAutoFocus; LLHandle mSnappedTo; @@ -344,7 +355,7 @@ public: /*virtual*/ void draw(); /*virtual*/ LLRect getSnapRect() const; - void refresh(); + /*virtual*/ void refresh(); void getNewFloaterPosition( S32* left, S32* top ); void resetStartingFloaterPosition(); @@ -373,10 +384,10 @@ public: void minimizeAllChildren(); // - LLFloater* getFrontmost(); - LLFloater* getBackmost(); - LLFloater* getParentFloater(LLView* viewp); - LLFloater* getFocusedFloater(); + LLFloater* getFrontmost() const; + LLFloater* getBackmost() const; + LLFloater* getParentFloater(LLView* viewp) const; + LLFloater* getFocusedFloater() const; void syncFloaterTabOrder(); // Returns z order of child provided. 0 is closest, larger numbers diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 1be8be2b0..60983c9d4 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -33,10 +33,10 @@ const F32 FOCUS_FADE_TIME = 0.3f; LLFocusableElement::LLFocusableElement() -: mFocusLostCallback(NULL), - mFocusReceivedCallback(NULL), - mFocusChangedCallback(NULL), - mTopLostCallback(NULL) +: mFocusLostCallback(nullptr), + mFocusReceivedCallback(nullptr), + mFocusChangedCallback(nullptr), + mTopLostCallback(nullptr) { } @@ -146,14 +146,14 @@ struct LLFocusMgr::Impl LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() -: mLockedView( NULL ), - mMouseCaptor( NULL ), - mKeyboardFocus( NULL ), - mLastKeyboardFocus( NULL ), - mDefaultKeyboardFocus( NULL ), - mLastDefaultKeyboardFocus( NULL ), +: mLockedView(nullptr ), + mMouseCaptor(nullptr ), + mKeyboardFocus(nullptr ), + mLastKeyboardFocus(nullptr ), + mDefaultKeyboardFocus(nullptr ), + mLastDefaultKeyboardFocus(nullptr ), mKeystrokesOnly(FALSE), - mTopCtrl( NULL ), + mTopCtrl(nullptr ), mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true mImpl(new LLFocusMgr::Impl) { @@ -163,22 +163,22 @@ LLFocusMgr::~LLFocusMgr() { mImpl->mFocusHistory.clear(); delete mImpl; - mImpl = NULL; + mImpl = nullptr; } void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) { if( childHasMouseCapture( view ) ) { - setMouseCapture( NULL ); + setMouseCapture(nullptr ); } if( childHasKeyboardFocus( view )) { if (view == mLockedView) { - mLockedView = NULL; - setKeyboardFocus( NULL ); + mLockedView = nullptr; + setKeyboardFocus(nullptr ); } else { @@ -188,7 +188,7 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) if( childIsTopCtrl( view ) ) { - setTopCtrl( NULL ); + setTopCtrl(nullptr); } } @@ -197,7 +197,7 @@ void LLFocusMgr::restoreDefaultKeyboardFocus(LLFocusableElement* current_default if (current_default_focus && mDefaultKeyboardFocus == current_default_focus) { setDefaultKeyboardFocus(mLastDefaultKeyboardFocus); - mLastDefaultKeyboardFocus = NULL; + mLastDefaultKeyboardFocus = nullptr; } } @@ -206,7 +206,7 @@ void LLFocusMgr::restoreKeyboardFocus(LLFocusableElement* current_focus) if (current_focus && mKeyboardFocus == current_focus) { setKeyboardFocus(mLastKeyboardFocus); - mLastKeyboardFocus = NULL; + mLastKeyboardFocus = nullptr; } } @@ -219,7 +219,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL focus_dirty = false; if (mLockedView && - (new_focus == NULL || + (new_focus == nullptr || (new_focus != mLockedView && dynamic_cast(new_focus) && !dynamic_cast(new_focus)->hasAncestor(mLockedView)))) @@ -291,7 +291,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. - if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) + if (mDefaultKeyboardFocus != nullptr && mKeyboardFocus == nullptr) { mDefaultKeyboardFocus->setFocus(TRUE); } @@ -343,7 +343,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const // Returns TRUE is parent or any descedent of parent is the mouse captor. BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { - if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) + if( mMouseCaptor && dynamic_cast(mMouseCaptor) != nullptr ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) @@ -364,23 +364,24 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f // in order to unlock it if (focus == mLockedView) { - mLockedView = NULL; + mLockedView = nullptr; } + if (mKeyboardFocus == focus) { - mKeyboardFocus = NULL; + mKeyboardFocus = nullptr; } if (mLastKeyboardFocus == focus) { - mLastKeyboardFocus = NULL; + mLastKeyboardFocus = nullptr; } if (mDefaultKeyboardFocus == focus) { - mDefaultKeyboardFocus = NULL; + mDefaultKeyboardFocus = nullptr; } if (mLastDefaultKeyboardFocus == focus) { - mLastDefaultKeyboardFocus = NULL; + mLastDefaultKeyboardFocus = nullptr; } } @@ -430,7 +431,7 @@ void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor { if( mMouseCaptor == captor ) { - mMouseCaptor = NULL; + mMouseCaptor = nullptr; } } @@ -470,7 +471,7 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) { if( mTopCtrl == top_view ) { - mTopCtrl = NULL; + mTopCtrl = nullptr; } } @@ -481,7 +482,7 @@ void LLFocusMgr::lockFocus() void LLFocusMgr::unlockFocus() { - mLockedView = NULL; + mLockedView = nullptr; } F32 LLFocusMgr::getFocusFlashAmt() const @@ -516,7 +517,7 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) // release focus from "top ctrl"s, which generally hides them if (!focus && mTopCtrl) { - setTopCtrl(NULL); + setTopCtrl(nullptr); } mAppHasFocus = focus; } @@ -532,7 +533,7 @@ LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const return static_cast(found_it->second.get()); } } - return NULL; + return nullptr; } void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index b587de148..a42853023 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -196,14 +196,16 @@ void LLPanel::draw() // draw background if( mBgVisible ) { - LLRect local_rect = getLocalRect(); + F32 alpha = getCurrentTransparency(); + + LLRect&& local_rect = getLocalRect(); if (mBgOpaque ) { - gl_rect_2d( local_rect, mBgColorOpaque ); + gl_rect_2d( local_rect, mBgColorOpaque % alpha); } else { - gl_rect_2d( local_rect, mBgColorAlpha ); + gl_rect_2d( local_rect, mBgColorAlpha % alpha); } } diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 6ff4260bd..26acc25da 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -38,6 +38,9 @@ static LLRegisterWidget r("ui_ctrl"); +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; + LLUICtrl::CallbackParam::CallbackParam() : name("name"), function_name("function"), @@ -94,25 +97,26 @@ const LLUICtrl::Params& LLUICtrl::getDefaultParams() LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) : LLView(p), + mCommitSignal(nullptr), + mValidateSignal(nullptr), + mMouseEnterSignal(nullptr), + mMouseLeaveSignal(nullptr), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mRightMouseDownSignal(nullptr), + mRightMouseUpSignal(nullptr), + mDoubleClickSignal(nullptr), + mViewModel(viewmodel), + mEnabledControlVariable(nullptr), + mDisabledControlVariable(nullptr), + mMakeVisibleControlVariable(nullptr), + mMakeInvisibleControlVariable(nullptr), mIsChrome(FALSE), mRequestsFront(p.requests_front), mTabStop(TRUE), mTentative(FALSE), - mViewModel(viewmodel), - mEnabledControlVariable(NULL), - mDisabledControlVariable(NULL), - mMakeVisibleControlVariable(NULL), - mMakeInvisibleControlVariable(NULL), - mCommitSignal(NULL), - mValidateSignal(NULL), - mMouseEnterSignal(NULL), - mMouseLeaveSignal(NULL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mRightMouseDownSignal(NULL), - mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL), - mCommitOnReturn(FALSE) + mCommitOnReturn(FALSE), + mTransparencyType(TT_DEFAULT) { } @@ -210,20 +214,21 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect rect, BOOL mouse_opaque mTabStop( TRUE ), mTentative( FALSE ), mViewModel(LLViewModelPtr(new LLViewModel)), - mEnabledControlVariable(NULL), - mDisabledControlVariable(NULL), - mMakeVisibleControlVariable(NULL), - mMakeInvisibleControlVariable(NULL), - mCommitSignal(NULL), - mValidateSignal(NULL), - mMouseEnterSignal(NULL), - mMouseLeaveSignal(NULL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mRightMouseDownSignal(NULL), - mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL), - mCommitOnReturn(FALSE) + mEnabledControlVariable(nullptr), + mDisabledControlVariable(nullptr), + mMakeVisibleControlVariable(nullptr), + mMakeInvisibleControlVariable(nullptr), + mCommitSignal(nullptr), + mValidateSignal(nullptr), + mMouseEnterSignal(nullptr), + mMouseLeaveSignal(nullptr), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mRightMouseDownSignal(nullptr), + mRightMouseUpSignal(nullptr), + mDoubleClickSignal(nullptr), + mCommitOnReturn(FALSE), + mTransparencyType(TT_DEFAULT) { if(commit_callback) setCommitCallback(commit_callback); @@ -338,6 +343,8 @@ BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) { (*mMouseDownSignal)(this,x,y,mask); } + LL_DEBUGS() << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << LL_ENDL; + return handled; } @@ -349,6 +356,7 @@ BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) { (*mMouseUpSignal)(this,x,y,mask); } + return handled; } @@ -466,7 +474,7 @@ void LLUICtrl::setEnabledControlVariable(LLControlVariable* control) if (mEnabledControlVariable) { mEnabledControlConnection.disconnect(); // disconnect current signal - mEnabledControlVariable = NULL; + mEnabledControlVariable = nullptr; } if (control) { @@ -481,7 +489,7 @@ void LLUICtrl::setDisabledControlVariable(LLControlVariable* control) if (mDisabledControlVariable) { mDisabledControlConnection.disconnect(); // disconnect current signal - mDisabledControlVariable = NULL; + mDisabledControlVariable = nullptr; } if (control) { @@ -496,7 +504,7 @@ void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control) if (mMakeVisibleControlVariable) { mMakeVisibleControlConnection.disconnect(); // disconnect current signal - mMakeVisibleControlVariable = NULL; + mMakeVisibleControlVariable = nullptr; } if (control) { @@ -511,7 +519,7 @@ void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control) if (mMakeInvisibleControlVariable) { mMakeInvisibleControlConnection.disconnect(); // disconnect current signal - mMakeInvisibleControlVariable = NULL; + mMakeInvisibleControlVariable = nullptr; } if (control) { @@ -526,7 +534,12 @@ bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle handle, LLUICtrl* ctrl = handle.get(); if (ctrl) { - if (type == "enabled") + if (type == "value") + { + ctrl->setValue(newvalue); + return true; + } + else if (type == "enabled") { ctrl->setEnabled(newvalue.asBoolean()); return true; @@ -565,19 +578,19 @@ BOOL LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text // virtual LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface() { - return NULL; + return nullptr; } // virtual LLCtrlListInterface* LLUICtrl::getListInterface() { - return NULL; + return nullptr; } // virtual LLCtrlScrollInterface* LLUICtrl::getScrollInterface() { - return NULL; + return nullptr; } BOOL LLUICtrl::hasFocus() const @@ -603,7 +616,7 @@ void LLUICtrl::setFocus(BOOL b) { if( gFocusMgr.childHasKeyboardFocus(this)) { - gFocusMgr.setKeyboardFocus( NULL ); + gFocusMgr.setKeyboardFocus(nullptr ); } } } @@ -1008,11 +1021,11 @@ LLUICtrl* LLUICtrl::getParentUICtrl() const } // *TODO: Deprecate; for backwards compatability only: -boost::signals2::connection LLUICtrl::setCommitCallback( boost::function cb, void* data) +boost::signals2::connection LLUICtrl::setCommitCallback( std::function cb, void* data) { return setCommitCallback( boost::bind(cb, _1, data)); } -boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function cb ) +boost::signals2::connection LLUICtrl::setValidateBeforeCommit( std::function cb ) { if (!mValidateSignal) mValidateSignal = new enable_signal_t(); return mValidateSignal->connect(boost::bind(cb, _2)); @@ -1030,7 +1043,6 @@ BOOL LLUICtrl::getTentative() const return mTentative; } - // virtual void LLUICtrl::setColor(const LLColor4& color) { } @@ -1047,6 +1059,37 @@ void LLUICtrl::setMinValue(LLSD min_value) void LLUICtrl::setMaxValue(LLSD max_value) { } +F32 LLUICtrl::getCurrentTransparency() +{ + F32 alpha = 0; + + switch(mTransparencyType) + { + case TT_DEFAULT: + alpha = getDrawContext().mAlpha; + break; + + case TT_ACTIVE: + alpha = sActiveControlTransparency; + break; + + case TT_INACTIVE: + alpha = sInactiveControlTransparency; + break; + + case TT_FADING: + alpha = sInactiveControlTransparency / 2.f; + break; + } + + return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ + mTransparencyType = type; +} + boost::signals2::connection LLUICtrl::setCommitCallback(const CommitCallbackParam& cb) { return setCommitCallback(initCommitCallback(cb)); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index ca0e11d16..ff28f2519 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -34,16 +34,11 @@ #ifndef LL_LLUICTRL_H #define LL_LLUICTRL_H +//#include "llboost.h" #include "llrect.h" #include "llsd.h" #include "llregistry.h" -#ifndef BOOST_FUNCTION_HPP_INCLUDED -#include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif -#include - #include "llinitparam.h" #include "llview.h" #include "llviewmodel.h" // *TODO move dependency to .cpp file @@ -128,6 +123,13 @@ public: Params(); }; + enum ETypeTransparency + { + TT_DEFAULT, + TT_ACTIVE, // focused floater + TT_INACTIVE, // other floaters + TT_FADING, // fading toast + }; /*virtual*/ ~LLUICtrl(); void initFromParams(const Params& p); @@ -147,26 +149,26 @@ public: // We shouldn't ever need to set this directly //virtual void setViewModel(const LLViewModelPtr&); - virtual BOOL postBuild(); + BOOL postBuild() override; public: // LLView interface /*virtual*/ void initFromXML(LLXMLNodePtr node, LLView* parent); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - /*virtual*/ BOOL isCtrl() const; - /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL canFocusChildren() const; - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ) override; + /*virtual*/ BOOL isCtrl() const override; + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL canFocusChildren() const override; + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) override; // From LLFocusableElement - /*virtual*/ void setFocus( BOOL b ); - /*virtual*/ BOOL hasFocus() const; + /*virtual*/ void setFocus( BOOL b ) override; + /*virtual*/ BOOL hasFocus() const override; // New virtuals @@ -207,11 +209,17 @@ public: // Clear any user-provided input (text in a text editor, checked checkbox, // selected radio button, etc.). Defaults to no-op. virtual void clear(); + virtual void setColor(const LLColor4& color); virtual void setAlpha(F32 alpha); virtual void setMinValue(LLSD min_value); virtual void setMaxValue(LLSD max_value); + F32 getCurrentTransparency(); + + void setTransparencyType(ETypeTransparency type); + ETypeTransparency getTransparencyType() const {return mTransparencyType;} + BOOL focusNextItem(BOOL text_entry_only); BOOL focusPrevItem(BOOL text_entry_only); virtual // Singu Note: focusFirstItem is overridden for our old chat ui to prevent focusing on topmost uictrls. @@ -248,8 +256,8 @@ public: boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb ); // *TODO: Deprecate; for backwards compatability only: - boost::signals2::connection setCommitCallback( boost::function cb, void* data); - boost::signals2::connection setValidateBeforeCommit( boost::function cb ); + boost::signals2::connection setCommitCallback( std::function cb, void* data); + boost::signals2::connection setValidateBeforeCommit( std::function cb ); static LLView* fromXML(LLXMLNodePtr node, LLView* parent, class LLUICtrlFactory* factory); @@ -266,9 +274,13 @@ public: template class CallbackRegistry : public LLRegistrySingleton {}; - class CommitCallbackRegistry : public CallbackRegistry{}; + class CommitCallbackRegistry : public CallbackRegistry + { + }; // the enable callback registry is also used for visiblity callbacks - class EnableCallbackRegistry : public CallbackRegistry{}; + class EnableCallbackRegistry : public CallbackRegistry + { + }; protected: @@ -297,6 +309,10 @@ protected: boost::signals2::connection mMakeVisibleControlConnection; LLControlVariable* mMakeInvisibleControlVariable; boost::signals2::connection mMakeInvisibleControlConnection; + + static F32 sActiveControlTransparency; + static F32 sInactiveControlTransparency; + private: BOOL mIsChrome; @@ -304,6 +320,8 @@ private: BOOL mTabStop; BOOL mTentative; + ETypeTransparency mTransparencyType; + bool mCommitOnReturn; class DefaultTabGroupFirstSorter; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2af8784f9..c1e976a93 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1176,17 +1176,6 @@ Found in Advanced->Rendering->Info Displays Value 0 - FloaterUnfocusedBackgroundOpaque - - Comment - Disables floaters going transparent when not in focus, may conflict with some skins, though. - Persist - 1 - Type - Boolean - Value - 0 - FSSynchronizeTextureMaps Comment @@ -2577,6 +2566,17 @@ This should be as low as possible, but too low may break functionality Value 300.0 + ActiveFloaterTransparency + + Comment + Transparency of active floaters (floaters that have focus) + Persist + 1 + Type + F32 + Value + 1 + AdvanceSnapshot Comment @@ -9312,6 +9312,17 @@ This should be as low as possible, but too low may break functionality Value 1 + InactiveFloaterTransparency + + Comment + Transparency of inactive floaters (floaters that have no focus) + Persist + 1 + Type + F32 + Value + 1 + InBandwidth Comment diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp index aaa1130b0..0c6216647 100644 --- a/indra/newview/ascentprefsvan.cpp +++ b/indra/newview/ascentprefsvan.cpp @@ -128,7 +128,8 @@ void LLPrefsAscentVan::refreshValues() mCustomizeAnim = gSavedSettings.getBOOL("LiruCustomizeAnim"); mAnnounceSnapshots = gSavedSettings.getBOOL("AnnounceSnapshots"); mAnnounceStreamMetadata = gSavedSettings.getBOOL("AnnounceStreamMetadata"); - mUnfocusedFloatersOpaque = gSavedSettings.getBOOL("FloaterUnfocusedBackgroundOpaque"); + mInactiveFloaterTransparency = gSavedSettings.getF32("InactiveFloaterTransparency"); + mActiveFloaterTransparency = gSavedSettings.getF32("ActiveFloaterTransparency"); mCompleteNameProfiles = gSavedSettings.getBOOL("SinguCompleteNameProfiles"); mScriptErrorsStealFocus = gSavedSettings.getBOOL("LiruScriptErrorsStealFocus"); mConnectToNeighbors = gSavedSettings.getBOOL("AlchemyConnectToNeighbors"); @@ -202,7 +203,8 @@ void LLPrefsAscentVan::cancel() gSavedSettings.setBOOL("LiruCustomizeAnim", mCustomizeAnim); gSavedSettings.setBOOL("AnnounceSnapshots", mAnnounceSnapshots); gSavedSettings.setBOOL("AnnounceStreamMetadata", mAnnounceStreamMetadata); - gSavedSettings.setBOOL("FloaterUnfocusedBackgroundOpaque", mUnfocusedFloatersOpaque); + gSavedSettings.setF32("InactiveFloaterTransparency", mInactiveFloaterTransparency); + gSavedSettings.setF32("ActiveFloaterTransparency", mActiveFloaterTransparency); gSavedSettings.setBOOL("SinguCompleteNameProfiles", mCompleteNameProfiles); gSavedSettings.setBOOL("LiruScriptErrorsStealFocus", mScriptErrorsStealFocus); gSavedSettings.setBOOL("AlchemyConnectToNeighbors", mConnectToNeighbors); diff --git a/indra/newview/ascentprefsvan.h b/indra/newview/ascentprefsvan.h index 676042c13..d8e27338e 100644 --- a/indra/newview/ascentprefsvan.h +++ b/indra/newview/ascentprefsvan.h @@ -62,7 +62,7 @@ private: bool mCustomizeAnim; bool mAnnounceSnapshots; bool mAnnounceStreamMetadata; - bool mUnfocusedFloatersOpaque; + F32 mInactiveFloaterTransparency, mActiveFloaterTransparency; bool mCompleteNameProfiles; bool mScriptErrorsStealFocus; bool mConnectToNeighbors; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6cdf1fb46..9ae068e77 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -773,7 +773,6 @@ bool LLAppViewer::init() writeSystemInfo(); - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -818,7 +817,14 @@ bool LLAppViewer::init() LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); - + + LL_INFOS("InitInfo") << "UI initialization is done." << LL_ENDL ; + + // Load translations for tooltips + LLFloater::initClass(); + + ///////////////////////////////////////////////// + LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated ///////////////////////////////////////////////// @@ -884,7 +890,7 @@ bool LLAppViewer::init() std::ostringstream msg; msg << LLTrans::getString("MBUnableToAccessFile"); OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); - return 1; + return true; } LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; @@ -923,10 +929,9 @@ bool LLAppViewer::init() msg, LLStringUtil::null, OSMB_OK); - return 0; + return false; } -#if (_M_IX86_FP > 1 || defined(__SSE2__)) // Without SSE2 support we will crash almost immediately, warn here. if (!gSysCPU.hasSSE2()) { @@ -938,23 +943,8 @@ bool LLAppViewer::init() msg, LLStringUtil::null, OSMB_OK); - return 0; + return false; } -#elif (_M_IX86_FP == 1 || defined(__SSE__)) - // Without SSE support we will crash almost immediately, warn here. - if (!gSysCPU.hasSSE()) - { - // can't use an alert here since we're exiting and - // all hell breaks lose. - std::string msg = LNotificationTemplates::instance().getGlobalString("UnsupportedCPUSSE2"); - LLStringUtil::format(msg,LLTrans::getDefaultArgs()); - OSMessageBox( - msg, - LLStringUtil::null, - OSMB_OK); - return 0; - } -#endif // alert the user if they are using unsupported hardware if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware")) diff --git a/indra/newview/skins/default/xui/de/panel_preferences_ascent_vanity.xml b/indra/newview/skins/default/xui/de/panel_preferences_ascent_vanity.xml index 92fec0dc5..c403b9c11 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_ascent_vanity.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_ascent_vanity.xml @@ -11,7 +11,6 @@ - diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 76d75d7c4..8d1ab98ee 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -140,6 +140,33 @@ Bezahlen Teleportangebot an Freundschaftsangebot + + Entfernen von Freunden + + + Schließen (⌘W) + + + Schließen (Strg+W) + + + Schließen + + + Wiederherstellen + + + Minimieren + + + Abnehmen + + + Andocken + + + Hilfe anzeigen + Suchen... diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_vanity.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_vanity.xml index 52827e6b0..72d9b4c51 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_vanity.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_vanity.xml @@ -12,13 +12,14 @@ - Sound played when regions will restart: + + diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index b34d3d165..af87e8956 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -154,6 +154,18 @@ Make sure you entered the correct Login URI. An example of a Login URI is: \"htt Pay Offer Teleport to Friend Request + Friend Removal + + + Close (⌘W) + Close (Ctrl+W) + Close + Restore + Minimize + Tear Off + Dock + Show Help + Edit Searching... diff --git a/indra/newview/skins/default/xui/es/panel_preferences_ascent_vanity.xml b/indra/newview/skins/default/xui/es/panel_preferences_ascent_vanity.xml index 66bd3e005..504a294af 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_ascent_vanity.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_ascent_vanity.xml @@ -12,7 +12,6 @@ - diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index ac9282a11..c2c361fe8 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -148,6 +148,33 @@ Asegurate que has ingresado el URI de inicio de sesión correcto. Un ejemplo de Pagar Ofrecer Teleporte a Ofrecer Amistad + + Eliminación de amigos + + + Cerrar (⌘W) + + + Cerrar (Ctrl+W) + + + Cerrar + + + Maximizar + + + Minimizar + + + Separar la ventana + + + Fijar + + + Ver la Ayuda + Buscando... diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_ascent_vanity.xml b/indra/newview/skins/default/xui/fr/panel_preferences_ascent_vanity.xml index 6bc3cf598..e2e3fcc2d 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_ascent_vanity.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_ascent_vanity.xml @@ -12,7 +12,6 @@ - diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index 4d12001d0..9de8a6d60 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -133,6 +133,176 @@ Solo un singolo oggetto può essere creato qui + + Non puoi indossare una cartella che contiene più di [AMOUNT] elementi. Per modificare questo limite, accedi ad Avanzate > Mostra impostazioni di debug > WearFolderLimit. + + + + Questo link porta a una pagina nel dominio ufficiale SecondLife.com o LindenLab.com. + + + Non puoi rezzare articoli dalla cartella degli annunci di Marketplace + + + Non puoi inserire nella cartella degli annunci in Marketplace gli articoli che indossi + + + La profondità delle caselle nidificate è maggiore di [AMOUNT]. Diminuisci la profondità delle cartelle nidificate; se necessario, raggruppa gli articoli. + + + Il numero di sottocartelle è maggiore di [AMOUNT]. Diminuisci il numero delle cartelle nel tuo annuncio; se necessario, raggruppa gli articoli. + + + Il numero di articoli è maggiore di [AMOUNT]. Per vendere più di [AMOUNT] articoli in un annuncio, devi raggruppare alcuni di essi. + + + Il numero di articoli in magazzino è maggiore di [AMOUNT]. + + + Puoi trascinare elementi o cartelle solo nelle schede TUTTI o NON ASSOCIATO. Seleziona una di quelle schede e sposta nuovamente gli elementi o le cartelle. + + + Almeno uno di questi oggetti non può essere venduto o trasferito + + + Puoi aggiungere a Marketplace solo gli articoli nel tuo inventario + + + Non puoi inserire cartelle o articoli collegati tramite link nel Marketplace + + + Non puoi inserire biglietti da visita in Marketplace + + + non puoi muovere un annuncio attivo + + + Non puoi spostare la cartella principale degli annunci di Marketplace + + + Tutti gli articoli in una cartella di magazzino devono essere dello stesso tipo e con le stesse autorizzazioni + + + Non puoi spostare una cartella nella relativa cartella secondaria + + + Non puoi spostare una cartella in se stessa + + + Clicca per visitare questa pagina web + + + Clicca per avere maggiori informazioni sul luogo + + + Clicca per vedere il profilo di questo residente + + + Ulteriori informazioni su questo Residente + + + Clicca per disattivare l'audio di questo residente + + + Clicca per attivare l'audio del residente + + + Clicca per inviare un IM a questo residente + + + Clicca per pagare il residente + + + Fai clic per inviare un'offerta di teleport al residente + + + Fai clic per inviare una richiesta di amicizia al residente + + + Clicca per vedere la descrizione del gruppo + + + Clicca per vedere la descrizione dell'evento + + + Clicca per vedere questa inserzione + + + Clicca per vedere la descrizione del lotto + + + Clicca per effettuare il teleport a questa destinazione + + + Clicca per vedere la descrizione dell'oggetto + + + Clicca per vedere questo posto sulla mappa + + + Clicca per avviare il comando secondlife:// + + + + Fai clic per comporre un'email + + + Teleportati a + + + Mostra la mappa per + + + Disattiva audio + + + Riattiva audio + + + IM + + + Paga + + + Offri teleport a + + + Richiesta di amicizia + + + Rimozione amico + + + Chiudi (⌘W) + + + Chiudi (Ctrl+W) + + + Chiudi + + + Ripristina + + + Minimizza + + + Distacca + + + Àncora + + + Mostra Aiuto + + + Ricerca in corso... + + + Nessun risultato. + Recupero dati in corso... diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index ee9dc7f60..02ac1ceb5 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -910,11 +910,27 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Controle sua camera + + Teletransportá-lo + + + Forçar o avatar a sentar + (Você) - - Teletransportá-lo + + + Suprimir alertas ao gerenciar listas de acesso ao terreno + + + Substituir suas animações padrão + + + Retornar objetos em seu nome + + + (desconhecido)! Público geral @@ -1204,6 +1220,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Perna direita + + Esqueleto aprimorado + Baixo @@ -1225,6 +1244,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Arraste um marco para adicioná-lo aos seus favoritos. + + Nenhum item correspondente encontrado. Verifique a ortografia de sua cadeia de pesquisa e tente novamente. + Você não possui uma cópia desta textura no seu inventário @@ -4327,6 +4349,15 @@ Denunciar abuso A imagem excede o limite [WIDTH]*[HEIGHT] + + O tamanho máx. do look é [WIDTH]*[HEIGHT]. Redimensione ou use outra imagem + + + O tamanho máx. do look é [WIDTH]*[HEIGHT]. Selecione outra textura + + + Não foi possível confirmar as dimensões da foto. Aguarde até que o tamanho da foto seja exibido no seletor + Aconteceu algo inesperado, apesar de termos tentado impedir isso. @@ -4358,6 +4389,9 @@ Denunciar abuso US$ [AMOUNT] + + Banimento do grupo + Plano @@ -4780,9 +4814,18 @@ Tente colocar o caminho do editor entre aspas. Destinos + + Facebook + + + Flickr + Gestos + + Status da grade + Como @@ -4795,6 +4838,9 @@ Tente colocar o caminho do editor entre aspas. Mercado + + Marketplace + Mini Mapa @@ -4819,6 +4865,9 @@ Tente colocar o caminho do editor entre aspas. Perfil + + Relatar abuso + Buscar @@ -4828,6 +4877,9 @@ Tente colocar o caminho do editor entre aspas. Falar + + Twitter + Controles da câmera @@ -4858,9 +4910,18 @@ Tente colocar o caminho do editor entre aspas. Destinos de interesse + + Publicar no Facebook + + + Carregar no Flickr + Gestos para seu avatar + + Mostrar status da grade atual + Como executar tarefas comuns @@ -4873,6 +4934,9 @@ Tente colocar o caminho do editor entre aspas. Faça compras + + Venda suas criações + Mostrar quem está aqui @@ -4897,6 +4961,9 @@ Tente colocar o caminho do editor entre aspas. Edite ou visualize seu perfil + + Relatar abuso + Encontre lugares, eventos, pessoas @@ -4906,6 +4973,9 @@ Tente colocar o caminho do editor entre aspas. Fale com pessoas próximas usando seu microfone + + Twitter + Alterar o ângulo da câmera @@ -4981,6 +5051,87 @@ Tente colocar o caminho do editor entre aspas. [Usuário] + + Experiência + + + (nenhuma experiência) + + + (experiência sem título) + + + Dentro do terreno + + + Dentro da grade + + + PERMITIDO + + + BLOQUEADO + + + COLABORADOR + + + ADMINISTRADOR + + + RECENTE + + + PRÓPRIAS + + + ([EXPERIENCES], máx. [MAXEXPERIENCES]) + + + assumir seus controles + + + acionar animações no seu avatar + + + anexar ao avatar + + + rastrear sua câmera + + + controlar sua câmera + + + teletransportar você + + + aceitar automaticamente permissões de experiência + + + realizar uma operação desconhecida: [Permission] + + + Assumir o controle + + + Acionar animações + + + Anexar + + + Rastrear câmera + + + Controlar câmera + + + Teletransportar + + + Autorização + As conversas não estão sendo registradas. Para começar a manter um registro, selecione "Salvar: apenas registro" ou "Salvar: registro e transcrições" em Preferências> Bate-papo. @@ -4990,4 +5141,31 @@ Tente colocar o caminho do editor entre aspas. Não há conversas registradas. Depois que você entrar em contato com alguém, ou alguém entrar em contato com você, um registro será exibido aqui. + + Carregando... + + + n/d + + + -Lista vazia- + + + Padrão + + + (nenhum) + + + Sem limite + + + A forma física contém triângulos muito pequenos. Tente simplificar o modelo físico. + + + A forma física contém dados de confirmação ruins. Tente consertar o modelo físico. + + + A forma física não tem a versão correta. Defina a versão correta para o modelo físico. + From 77cdc0fa4c2ad13918704fe6643676002a4c1d80 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 24 Jan 2019 12:30:14 -0500 Subject: [PATCH 072/332] Stop spamming the log whenever preferences opens!! --- indra/llui/llnotifications.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 5c9dfb74a..1f2f4cec4 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -284,7 +284,8 @@ LLNotificationForm::LLNotificationForm(const LLSD& sd) } else { - LL_WARNS() << "Invalid form data " << sd << LL_ENDL; + if (!sd.isUndefined()) + LL_WARNS() << "Invalid form data " << sd << LL_ENDL; mFormData = LLSD::emptyArray(); } } From a30119706db805e229fd7e07eed4aac9e8d8df73 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 24 Jan 2019 17:58:54 -0500 Subject: [PATCH 073/332] Add /setdebug chatbar command. Syntax: /setdebug The setting to change this command is SinguCmdLineSetting This command supports using json for complex data types, like vectors. If there's no new value passed, the debug will be changed true<->false, this conversion will have interesting effects on nonboolean types. Have Fun!! --- .../newview/app_settings/settings_ascent.xml | 11 ++++ indra/newview/chatbar_as_cmdline.cpp | 54 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index 09fba4c59..a39f9b855 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -818,6 +818,17 @@ Value /regionsay + SinguCmdLineSetting + + Comment + Set or toggle debug settings from the chatbar. + Persist + 1 + Type + String + Value + /setdebug + SinguCmdLineURL Comment diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index ee069ecbd..2863cfe92 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -42,6 +42,7 @@ #include "llagentui.h" #include "llavataractions.h" #include "llnotificationsutil.h" +#include "llsdserialize.h" #include "llviewerregion.h" #include "llworld.h" #include "lleventtimer.h" @@ -242,11 +243,11 @@ bool cmd_line_chat(std::string data, EChatType type) static LLCachedControl enableChatCmd(gSavedSettings, "AscentCmdLine", true); if (enableChatCmd) { - data = utf8str_tolower(data); std::istringstream input(data); std::string cmd; if (!(input >> cmd)) return true; + cmd = utf8str_tolower(cmd); static LLCachedControl sDrawDistanceCommand(gSavedSettings, "AscentCmdLineDrawDistance"); static LLCachedControl sHeightCommand(gSavedSettings, "AscentCmdLineHeight"); @@ -266,6 +267,7 @@ bool cmd_line_chat(std::string data, EChatType type) static LLCachedControl sTP2Command(gSavedSettings, "AscentCmdLineTP2"); static LLCachedControl sAwayCommand(gSavedSettings, "SinguCmdLineAway"); static LLCachedControl sURLCommand(gSavedSettings, "SinguCmdLineURL"); + static LLCachedControl sSettingCommand(gSavedSettings, "SinguCmdLineSetting"); if (cmd == utf8str_tolower(sDrawDistanceCommand)) // dd { @@ -396,7 +398,7 @@ bool cmd_line_chat(std::string data, EChatType type) slurl = LLSLURL(slurl.getRegion(), LLVector3(fmod(agentPos.mdV[VX], (F64)REGION_WIDTH_METERS), fmod(agentPos.mdV[VY], (F64)REGION_WIDTH_METERS), agentPos.mdV[VZ])); } - LLUrlAction::teleportToLocation(LLWeb::escapeURL(std::string("secondlife:///app/teleport/")+slurl.getLocationString())); + LLUrlAction::teleportToLocation(LLWeb::escapeURL("secondlife:///app/teleport/"+slurl.getLocationString())); } return false; } @@ -539,6 +541,54 @@ bool cmd_line_chat(std::string data, EChatType type) } return false; } + else if (cmd == utf8str_tolower(sSettingCommand)) + { + std::string control_name; + if (input >> control_name) + { + // Find out if this control even exists. + auto control = gSavedSettings.getControl(control_name); + if (!control) gSavedPerAccountSettings.getControl(control_name); + if (!control) return false; + + LLSD val; + // Toggle if we weren't given a value, + if (input.eof()) val = !control->get(); + else // otherwise use what we were given. + { + switch (control->type()) // Convert to the right type, we can't be a string forever. + { + case TYPE_U32: // Integer is Integer, sucks but whatever + case TYPE_S32: + { + LLSD::Integer i; + input >> i; + val = i; + break; + } + case TYPE_F32: + { + LLSD::Float f; + input >> f; + val = f; + break; + } + case TYPE_STRING: + { + LLSD::String str; + std::getline(input, str, '\0'); // Read the entire rest of the stream into str + LLStringUtil::trim(str); // There's no setting where a space would be necessary here. + val = str; + break; + } + default: // Just parse it like JSON (or accept one of the many boolean value options! + LLSDSerialize::fromNotation(val, input, LLSDSerialize::SIZE_UNLIMITED); + } + } + control->set(val); + } + return false; + } else if (cmd == "typingstop") { std::string text; From 748a952e81aa63a3314371b0b8a4a7fd66ba7c98 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 24 Jan 2019 18:00:36 -0500 Subject: [PATCH 074/332] Fix Script Count not being enabled when AllowSelectAvatar is true --- indra/newview/llviewermenu.cpp | 8 ++++---- indra/newview/skins/default/xui/en-us/menu_pie_self.xml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a4b1f4164..ff590cd74 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3005,9 +3005,9 @@ class LLAvatarFreeze : public view_listener_t } }; -void do_script_count(bool del) +void do_script_count(bool del, LLViewerObject* object = nullptr) { - if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) + if (object || (object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject())) { if (ScriptCounter::getInstance(object->getID())) return; ScriptCounter* sc = new ScriptCounter(del, object); @@ -3020,7 +3020,7 @@ class LLScriptCount : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - do_script_count(false); + do_script_count(false, userdata["data"].asString() == "agent" ? gAgentAvatarp : nullptr); return true; } }; @@ -3038,7 +3038,7 @@ class LLObjectVisibleScriptCount : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + LLViewerObject* object = userdata["data"].asString() == "agent" ? gAgentAvatarp : LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); bool new_value = (object != NULL); gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); diff --git a/indra/newview/skins/default/xui/en-us/menu_pie_self.xml b/indra/newview/skins/default/xui/en-us/menu_pie_self.xml index aeb5f1184..6e1b30e6c 100644 --- a/indra/newview/skins/default/xui/en-us/menu_pie_self.xml +++ b/indra/newview/skins/default/xui/en-us/menu_pie_self.xml @@ -100,8 +100,8 @@ From 0fca6ee425871950fbbf5b946153af2049e17b73 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 25 Jan 2019 19:30:58 -0500 Subject: [PATCH 075/332] More seriously C++14 ...I hope --- indra/cmake/00-Common.cmake | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 4e1e6cb49..fbbf1446f 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -7,7 +7,6 @@ set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES") include(Variables) - # Portable compilation flags. set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -DLL_DEBUG=1") @@ -31,14 +30,11 @@ if(NON_RELEASE_CRASH_REPORTING) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLL_SEND_CRASH_REPORTS=1") endif() - # Don't bother with a MinSizeRel build. - set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING "Supported build types." FORCE) # Platform-specific compilation flags. - if (WINDOWS) # Don't build DLLs. set(BUILD_SHARED_LIBS OFF) @@ -128,21 +124,24 @@ set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math") if (LINUX) set(CMAKE_SKIP_RPATH TRUE) + add_compile_options( + -fvisibility=hidden + -fexceptions + -fno-math-errno + -fno-strict-aliasing + -fsigned-char + -g + -pthread + ) + add_definitions( - -DLL_LINUX=1 - -DAPPID=secondlife - -D_REENTRANT - -fexceptions - -fno-math-errno - -fno-strict-aliasing - -fsigned-char - -fvisibility=hidden - -g - -pthread - ) + -DLL_LINUX=1 + -DAPPID=secondlife + -D_REENTRANT + ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") From 167d336274068c7b1317589468b8cd95a31a8689 Mon Sep 17 00:00:00 2001 From: Inusaito Sayori Date: Fri, 5 Dec 2014 12:19:30 -0500 Subject: [PATCH 076/332] Up to compile phase at https://bitbucket.org/lindenlab/viewer-directdelivery/commits/20fd3e98b4010d4ad87fb53e6fd623aa9e0b1104?at=default --- indra/llinventory/llfoldertype.cpp | 3 + indra/llinventory/llfoldertype.h | 3 + indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 41 +- .../newview/app_settings/settings_ascent.xml | 11 + .../newview/llfloatermarketplacelistings.cpp | 782 ++++++++++ indra/newview/llfloatermarketplacelistings.h | 204 +++ indra/newview/llfloateroutbox.cpp | 10 +- indra/newview/llfloateroutbox.h | 4 +- indra/newview/llfoldervieweventlistener.h | 3 + indra/newview/llfolderviewitem.cpp | 47 +- indra/newview/llfolderviewitem.h | 9 +- indra/newview/llinventoryactions.cpp | 157 +- indra/newview/llinventorybridge.cpp | 1063 +++++++++---- indra/newview/llinventorybridge.h | 29 +- indra/newview/llinventoryfilter.cpp | 51 + indra/newview/llinventoryfilter.h | 11 +- indra/newview/llinventoryfunctions.cpp | 1074 +++++++++++++- indra/newview/llinventoryfunctions.h | 17 +- indra/newview/llinventorymodel.cpp | 17 +- indra/newview/llinventorypanel.cpp | 224 +-- indra/newview/llinventorypanel.h | 1 + indra/newview/llmarketplacefunctions.cpp | 1321 ++++++++++++++++- indra/newview/llmarketplacefunctions.h | 146 ++ indra/newview/llmenucommands.cpp | 2 + indra/newview/llstartup.cpp | 8 + indra/newview/lltooldraganddrop.cpp | 17 +- indra/newview/llviewerfoldertype.cpp | 8 +- indra/newview/llviewerinventory.cpp | 25 + indra/newview/llviewerinventory.h | 3 + indra/newview/llviewermenu.cpp | 62 + indra/newview/llviewermenu.h | 1 + indra/newview/llviewermessage.cpp | 22 +- indra/newview/llviewerregion.cpp | 1 + .../textures/Inv_StockFolderClosed.png | Bin 0 -> 1143 bytes .../default/textures/Inv_StockFolderOpen.png | Bin 0 -> 1319 bytes .../textures/Inv_VersionFolderClosed.png | Bin 0 -> 1132 bytes .../textures/Inv_VersionFolderOpen.png | Bin 0 -> 1521 bytes .../icn_toolbar_marketplace_listings.tga | Bin 0 -> 49 bytes .../skins/default/textures/textures.xml | 5 + .../xui/en-us/floater_associate_listing.xml | 53 + .../en-us/floater_marketplace_listings.xml | 114 ++ .../en-us/floater_marketplace_validation.xml | 46 + .../default/xui/en-us/menu_inventory.xml | 47 + .../xui/en-us/menu_marketplace_view.xml | 16 + .../skins/default/xui/en-us/menu_viewer.xml | 6 + .../skins/default/xui/en-us/notifications.xml | 122 ++ .../xui/en-us/panel_marketplace_listings.xml | 87 ++ .../panel_marketplace_listings_inventory.xml | 24 + .../panel_marketplace_listings_listed.xml | 23 + ...anel_marketplace_listings_unassociated.xml | 22 + .../panel_marketplace_listings_unlisted.xml | 23 + .../skins/default/xui/en-us/panel_toolbar.xml | 5 + .../skins/default/xui/en-us/strings.xml | 56 +- 54 files changed, 5566 insertions(+), 462 deletions(-) create mode 100644 indra/newview/llfloatermarketplacelistings.cpp create mode 100644 indra/newview/llfloatermarketplacelistings.h create mode 100644 indra/newview/skins/default/textures/Inv_StockFolderClosed.png create mode 100644 indra/newview/skins/default/textures/Inv_StockFolderOpen.png create mode 100644 indra/newview/skins/default/textures/Inv_VersionFolderClosed.png create mode 100644 indra/newview/skins/default/textures/Inv_VersionFolderOpen.png create mode 100644 indra/newview/skins/default/textures/icn_toolbar_marketplace_listings.tga create mode 100644 indra/newview/skins/default/xui/en-us/floater_associate_listing.xml create mode 100644 indra/newview/skins/default/xui/en-us/floater_marketplace_listings.xml create mode 100644 indra/newview/skins/default/xui/en-us/floater_marketplace_validation.xml create mode 100644 indra/newview/skins/default/xui/en-us/menu_marketplace_view.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_marketplace_listings.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_marketplace_listings_inventory.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_marketplace_listings_listed.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_marketplace_listings_unassociated.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_marketplace_listings_unlisted.xml diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index bb8365528..5fe513310 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -103,6 +103,9 @@ LLFolderDictionary::LLFolderDictionary() addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", TRUE)); addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE)); + addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new FolderEntry("stock", FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE)); addEntry(LLFolderType::FT_SUITCASE, new FolderEntry("suitcase", TRUE)); addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE)); diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index bb9a3f213..bea5dc379 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -94,6 +94,9 @@ public: FT_BASIC_ROOT = 52, + FT_MARKETPLACE_LISTINGS = 53, + FT_MARKETPLACE_STOCK = 54, + FT_MARKETPLACE_VERSION = 55, // Note: We actually *never* create folders with that type. This is used for icon override only. FT_SUITCASE = 100, FT_COUNT, diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 258d44e95..a1242ff27 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -231,6 +231,7 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlandmark.cpp llfloatermap.cpp + llfloatermarketplacelistings.cpp llfloatermediafilter.cpp llfloatermediasettings.cpp llfloatermemleak.cpp @@ -764,6 +765,7 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlandmark.h llfloatermap.h + llfloatermarketplacelistings.h llfloatermediafilter.h llfloatermediasettings.h llfloatermemleak.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c1e976a93..a704472ee 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5419,6 +5419,17 @@ This should be as low as possible, but too low may break functionality Value 1 + DebugHideEmptySystemFolders + + Comment + Hide empty system folders when on + Persist + 1 + Type + Boolean + Value + 1 + DebugInventoryFilters Comment @@ -9400,12 +9411,12 @@ This should be as low as possible, but too low may break functionality Value 0 - InventoryDisplayOutbox + InventoryOutboxDisplayBoth Comment - Override merchant inventory outbox display + Show the legacy Merchant Outbox UI as well as the Marketplace Listings UI Persist - 0 + 1 Type Boolean Value @@ -9422,6 +9433,17 @@ This should be as low as possible, but too low may break functionality Value 0 + InventoryOutboxMakeVisible + + Comment + Enable making the Merchant Outbox and Inbox visible in the inventory for debug purposes. + Persist + 1 + Type + Boolean + Value + 1 + InventoryOutboxMaxFolderCount Comment @@ -9431,7 +9453,7 @@ This should be as low as possible, but too low may break functionality Type U32 Value - 21 + 20 InventoryOutboxMaxFolderDepth @@ -10307,6 +10329,17 @@ This should be as low as possible, but too low may break functionality Value + MarketplaceListingsLogging + + Comment + Enable debug output associated with the Marketplace Listings (SLM) API. + Persist + 1 + Type + Boolean + Value + 0 + MaxDragDistance Comment diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index a39f9b855..0ee2c1a24 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -1761,6 +1761,17 @@ Changing this setting only affects new text. IsCOA 1 + ToolbarVisibleMarketplaceListings + + Comment + Whether or not the button for marketplace listings is on the toolbar + Persist + 1 + Type + Boolean + Value + 0 + ToolbarVisibleMeanEvents Comment diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp new file mode 100644 index 000000000..25ce9dc86 --- /dev/null +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -0,0 +1,782 @@ +/** + * @file llfloatermarketplacelistings.cpp + * @brief Implementation of the marketplace listings floater and panels + * @author merov@lindenlab.com + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermarketplacelistings.h" + +//#include "llfloaterreg.h" +#include "llfiltereditor.h" +#include "llfolderview.h" +#include "llinventorybridge.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventoryobserver.h" +#include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" +#include "llnotificationhandler.h" +#include "llnotificationmanager.h" +#include "llnotificationsutil.h" +//#include "llsidepaneliteminfo.h" +#include "lltextbox.h" +#include "lltrans.h" + +///---------------------------------------------------------------------------- +/// LLPanelMarketplaceListings +///---------------------------------------------------------------------------- + +static LLPanelInjector t_panel_status("llpanelmarketplacelistings"); // Liru TODO: build this instead. + +LLPanelMarketplaceListings::LLPanelMarketplaceListings() +: mRootFolder(NULL) +, mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME) +, mFilterType(LLInventoryFilter::FILTERTYPE_NONE) +{ + mCommitCallbackRegistrar.add("Marketplace.ViewSort.Action", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("Marketplace.ViewSort.CheckItem", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemCheck, this, _2)); +} + +BOOL LLPanelMarketplaceListings::postBuild() +{ + childSetAction("add_btn", boost::bind(&LLPanelMarketplaceListings::onAddButtonClicked, this)); + childSetAction("audit_btn", boost::bind(&LLPanelMarketplaceListings::onAuditButtonClicked, this)); + + mFilterEditor = getChild("filter_editor"); + mFilterEditor->setCommitCallback(boost::bind(&LLPanelMarketplaceListings::onFilterEdit, this, _2)); + + mAuditBtn = getChild("audit_btn"); + mAuditBtn->setEnabled(FALSE); + + return LLPanel::postBuild(); +} + +void LLPanelMarketplaceListings::buildAllPanels() +{ + // Build the All panel first + LLInventoryPanel* panel_all_items; + panel_all_items = buildInventoryPanel("All Items", "panel_marketplace_listings_inventory.xml"); + panel_all_items->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel_all_items->getFilter().markDefault(); + + // Build the other panels + LLInventoryPanel* panel; + panel = buildInventoryPanel("Active Items", "panel_marketplace_listings_listed.xml"); + panel->getFilter().setFilterMarketplaceActiveFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + panel = buildInventoryPanel("Inactive Items", "panel_marketplace_listings_unlisted.xml"); + panel->getFilter().setFilterMarketplaceInactiveFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + panel = buildInventoryPanel("Unassociated Items", "panel_marketplace_listings_unassociated.xml"); + panel->getFilter().setFilterMarketplaceUnassociatedFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + + // Set the tab panel + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + tabs_panel->setCommitCallback(boost::bind(&LLPanelMarketplaceListings::onTabChange, this)); + tabs_panel->selectTabPanel(panel_all_items); // All panel selected by default + mRootFolder = panel_all_items->getRootFolder(); // Keep the root of the all panel +} + +LLInventoryPanel* LLPanelMarketplaceListings::buildInventoryPanel(const std::string& childname, const std::string& filename) +{ + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + LLInventoryPanel* panel = getChild(childname); + if (panel) + { + tabs_panel->removeTabPanel(panel); + delete panel; + } + panel = LLUICtrlFactory::createFromFile(filename, tabs_panel, LLInventoryPanel::child_registry_t::instance()); + llassert(panel != NULL); + + // Set sort order and callbacks + panel = getChild(childname); + panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); + panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2)); + + return panel; +} + +void LLPanelMarketplaceListings::onFilterEdit(const std::string& search_string) +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + // Save filter string (needed when switching tabs) + mFilterSubString = search_string; + // Set filter string on active panel + panel->setFilterSubString(mFilterSubString); + } +} + +void LLPanelMarketplaceListings::draw() +{ + if (LLMarketplaceData::instance().checkDirtyCount()) + { + update_all_marketplace_count(); + } + + // Get the audit button enabled only after the whole inventory is fetched + if (!mAuditBtn->getEnabled()) + { + mAuditBtn->setEnabled(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()); + } + + LLPanel::draw(); +} + +void LLPanelMarketplaceListings::onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action) +{ + panel->onSelectionChange(items, user_action); +} + +void LLPanelMarketplaceListings::onTabChange() +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + // If the panel doesn't allow drop on root, it doesn't allow the creation of new folder on root either + LLButton* add_btn = getChild("add_btn"); + add_btn->setEnabled(panel->getAllowDropOnRoot()); + + // Set filter string on active panel + panel->setFilterSubString(mFilterSubString); + } +} + +void LLPanelMarketplaceListings::onAddButtonClicked() +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + llassert(marketplacelistings_id.notNull()); + LLFolderType::EType preferred_type = LLFolderType::lookup("category"); + LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null); + gInventory.notifyObservers(); + panel->setSelectionByID(category, TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); + } +} + +void LLPanelMarketplaceListings::onAuditButtonClicked() +{ + LLSD data(LLSD::emptyMap()); + LLFloaterReg::showInstance("marketplace_validation", data); +} + +void LLPanelMarketplaceListings::onViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + // Sort options + if (chosen_item == "sort_by_stock_amount") + { + mSortOrder = (mSortOrder == LLInventoryFilter::SO_FOLDERS_BY_NAME ? LLInventoryFilter::SO_FOLDERS_BY_WEIGHT : LLInventoryFilter::SO_FOLDERS_BY_NAME); + // Set each panel with that sort order + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + LLInventoryPanel* panel = (LLInventoryPanel*)tabs_panel->getPanelByName("All Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Active Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Inactive Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Unassociated Items"); + panel->setSortOrder(mSortOrder); + } +} + +bool LLPanelMarketplaceListings::onViewSortMenuItemCheck(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_by_stock_amount") + return mSortOrder == LLInventoryFilter::SO_FOLDERS_BY_WEIGHT; + return false; +} + +///---------------------------------------------------------------------------- +/// LLMarketplaceListingsAddedObserver helper class +///---------------------------------------------------------------------------- + +class LLMarketplaceListingsAddedObserver : public LLInventoryCategoryAddedObserver +{ +public: + LLMarketplaceListingsAddedObserver(LLFloaterMarketplaceListings * marketplace_listings_floater) + : LLInventoryCategoryAddedObserver() + , mMarketplaceListingsFloater(marketplace_listings_floater) + { + } + + void done() + { + for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it) + { + LLViewerInventoryCategory* added_category = *it; + + LLFolderType::EType added_category_type = added_category->getPreferredType(); + + if (added_category_type == LLFolderType::FT_MARKETPLACE_LISTINGS) + { + mMarketplaceListingsFloater->initializeMarketPlace(); + } + } + } + +private: + LLFloaterMarketplaceListings * mMarketplaceListingsFloater; +}; + +///---------------------------------------------------------------------------- +/// LLFloaterMarketplaceListings +///---------------------------------------------------------------------------- + +LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key) +: LLFloater(key) +, mCategoriesObserver(NULL) +, mCategoryAddedObserver(NULL) +, mRootFolderId(LLUUID::null) +, mInventoryStatus(NULL) +, mInventoryInitializationInProgress(NULL) +, mInventoryPlaceholder(NULL) +, mInventoryText(NULL) +, mInventoryTitle(NULL) +, mPanelListings(NULL) +, mFirstViewListings(true) +{ + //buildFromFile("floater_marketplace_listings.xml"); + LLUICtrlFactory::instance().buildFloater(this, "floater_marketplace_listings.xml"); +} + +LLFloaterMarketplaceListings::~LLFloaterMarketplaceListings() +{ + if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; + + if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) + { + gInventory.removeObserver(mCategoryAddedObserver); + } + delete mCategoryAddedObserver; +} + +BOOL LLFloaterMarketplaceListings::postBuild() +{ + mInventoryStatus = getChild("marketplace_status"); + mInventoryInitializationInProgress = getChild("initialization_progress_indicator"); + mInventoryPlaceholder = getChild("marketplace_listings_inventory_placeholder_panel"); + mInventoryText = mInventoryPlaceholder->getChild("marketplace_listings_inventory_placeholder_text"); + mInventoryTitle = mInventoryPlaceholder->getChild("marketplace_listings_inventory_placeholder_title"); + + mPanelListings = static_cast(getChild("panel_marketplace_listing")); + + LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterMarketplaceListings::onFocusReceived, this)); + + // Observe category creation to catch marketplace listings creation (moot if already existing) + mCategoryAddedObserver = new LLMarketplaceListingsAddedObserver(this); + gInventory.addObserver(mCategoryAddedObserver); + + // Debug : fetch aggressively so we can create test data right onOpen() + fetchContents(); + + return TRUE; +} + +void LLFloaterMarketplaceListings::onClose(bool app_quitting) +{ +} + +bool canAccessMarketplace() +{ + return (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT) || gSavedSettings.getBOOL("InventoryOutboxDisplayBoth"); +} + +void LLFloaterMarketplaceListings::onOpen(const LLSD& key) +{ + if (!canAccessMarketplace()) + { + close(); + return; + } + // + // Initialize the Market Place or go update the marketplace listings + // + if (LLMarketplaceData::instance().getSLMStatus() <= MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE) + { + initializeMarketPlace(); + } + else + { + updateView(); + } +} + +void LLFloaterMarketplaceListings::onFocusReceived() +{ + updateView(); +} + +void LLFloaterMarketplaceListings::fetchContents() +{ + if (mRootFolderId.notNull()) + { + LLInventoryModelBackgroundFetch::instance().start(mRootFolderId); + // Get all the SLM Listings + LLMarketplaceData::instance().getSLMListings(); + } +} + +void LLFloaterMarketplaceListings::setup() +{ + if (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) + { + // If we are *not* a merchant or we have no market place connection established yet, do nothing + return; + } + + // We are a merchant. Get the Marketplace listings folder, create it if needs be. + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); + if (marketplacelistings_id.isNull()) + { + // We should never get there unless the inventory fails badly + LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL; + return; + } + + // No longer need to observe new category creation + if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) + { + gInventory.removeObserver(mCategoryAddedObserver); + delete mCategoryAddedObserver; + mCategoryAddedObserver = NULL; + } + llassert(!mCategoryAddedObserver); + + if (marketplacelistings_id == mRootFolderId) + { + LL_WARNS("SLM") << "Inventory warning: Marketplace listings folder already set" << LL_ENDL; + return; + } + mRootFolderId = marketplacelistings_id; + + // Consolidate Marketplace listings + // We shouldn't have to do that but with a client/server system relying on a "well known folder" convention, + // things get messy and conventions get broken down eventually + gInventory.consolidateForType(marketplacelistings_id, LLFolderType::FT_MARKETPLACE_LISTINGS); + + // Now that we do have a non NULL root, we can build the inventory panels + mPanelListings->buildAllPanels(); + + // Create observer for marketplace listings modifications + if (!mCategoriesObserver && mRootFolderId.notNull()) + { + mCategoriesObserver = new LLInventoryCategoriesObserver(); + llassert(mCategoriesObserver); + gInventory.addObserver(mCategoriesObserver); + mCategoriesObserver->addCategory(mRootFolderId, boost::bind(&LLFloaterMarketplaceListings::onChanged, this)); + } + + // Get the content of the marketplace listings folder + fetchContents(); +} + +void LLFloaterMarketplaceListings::initializeMarketPlace() +{ + LLMarketplaceData::instance().initializeSLM(boost::bind(&LLFloaterMarketplaceListings::updateView, this)); +} + +S32 LLFloaterMarketplaceListings::getFolderCount() +{ + if (mPanelListings && mRootFolderId.notNull()) + { + LLInventoryModel::cat_array_t * cats; + LLInventoryModel::item_array_t * items; + gInventory.getDirectDescendentsOf(mRootFolderId, cats, items); + + return (cats->size() + items->size()); + } + else + { + return 0; + } +} + +void LLFloaterMarketplaceListings::setStatusString(const std::string& statusString) +{ + mInventoryStatus->setText(statusString); +} + +void LLFloaterMarketplaceListings::updateView() +{ + U32 mkt_status = LLMarketplaceData::instance().getSLMStatus(); + + // Get or create the root folder if we are a merchant and it hasn't been done already + if (mRootFolderId.isNull() && (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)) + { + setup(); + } + + // Update the bottom initializing status and progress dial + if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) + { + setStatusString(getString("MarketplaceListingsInitializing")); + mInventoryInitializationInProgress->setVisible(true); + } + else + { + setStatusString(""); + mInventoryInitializationInProgress->setVisible(false); + } + + // Update the middle portion : tabs or messages + if (getFolderCount() > 0) + { + if (mFirstViewListings) + { + // We need to rebuild the tabs cleanly the first time we make them visible + // setup() does it if the root is nixed first + mRootFolderId.setNull(); + setup(); + mFirstViewListings = false; + } + mPanelListings->setVisible(TRUE); + mInventoryPlaceholder->setVisible(FALSE); + } + else + { + mPanelListings->setVisible(FALSE); + mInventoryPlaceholder->setVisible(TRUE); + + std::string text; + std::string title; + std::string tooltip; + + const LLSD& subs = getMarketplaceStringSubstitutions(); + + // Update the top message or flip to the tabs and folders view + // *TODO : check those messages and create better appropriate ones in strings.xml + if (mRootFolderId.notNull()) + { + // "Marketplace listings is empty!" message strings + text = LLTrans::getString("InventoryMarketplaceListingsNoItems", subs); + title = LLTrans::getString("InventoryMarketplaceListingsNoItemsTitle"); + tooltip = LLTrans::getString("InventoryMarketplaceListingsNoItemsTooltip"); + } + else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) + { + // "Initializing!" message strings + text = LLTrans::getString("InventoryOutboxInitializing", subs); + title = LLTrans::getString("InventoryOutboxInitializingTitle"); + tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip"); + } + else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT) + { + // "Not a merchant!" message strings + text = LLTrans::getString("InventoryOutboxNotMerchant", subs); + title = LLTrans::getString("InventoryOutboxNotMerchantTitle"); + tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip"); + } + else + { + // "Errors!" message strings + text = LLTrans::getString("InventoryOutboxError", subs); + title = LLTrans::getString("InventoryOutboxErrorTitle"); + tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); + } + + mInventoryText->setValue(text); + mInventoryTitle->setValue(title); + mInventoryPlaceholder->getParent()->setToolTip(tooltip); + } +} + +bool LLFloaterMarketplaceListings::isAccepted(EAcceptance accept) +{ + return (accept >= ACCEPT_YES_COPY_SINGLE); +} + +BOOL LLFloaterMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // If there's no panel to accept drops or no existing marketplace listings folder, we refuse all drop + if (!mPanelListings || mRootFolderId.isNull()) + { + return FALSE; + } + + // Pass to the children + LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + BOOL handled = (handled_view != NULL); + + // If no one handled it or it was not accepted, we try to accept it at the floater level as if it was dropped on the + // marketplace listings root folder + if (!handled || !isAccepted(*accept)) + { + if (!mPanelListings->getVisible() && mRootFolderId.notNull()) + { + LLFolderView* root_folder = mPanelListings->getRootFolder(); + handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + } + + return handled; +} + +BOOL LLFloaterMarketplaceListings::handleHover(S32 x, S32 y, MASK mask) +{ + return LLFloater::handleHover(x, y, mask); +} + +void LLFloaterMarketplaceListings::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLFloater::onMouseLeave(x, y, mask); +} + +void LLFloaterMarketplaceListings::onChanged() +{ + LLViewerInventoryCategory* category = gInventory.getCategory(mRootFolderId); + if (mRootFolderId.notNull() && category) + { + updateView(); + } + else + { + // Invalidate the marketplace listings data + mRootFolderId.setNull(); + } +} + +//----------------------------------------------------------------------------- +// LLFloaterAssociateListing +//----------------------------------------------------------------------------- + +LLFloaterAssociateListing::LLFloaterAssociateListing(const LLSD& key) +: LLFloater(key) +, mUUID() +{ + //buildFromFile("floater_associate_listing.xml"); + LLUICtrlFactory::instance().buildFloater(this, "floater_associate_listing.xml"); +} + +LLFloaterAssociateListing::~LLFloaterAssociateListing() +{ + gFocusMgr.releaseFocusIfNeeded( this ); +} + +BOOL LLFloaterAssociateListing::postBuild() +{ + getChild("OK")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::apply, this, TRUE)); + getChild("Cancel")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::cancel, this)); + getChild("listing_id")->setPrevalidate(&LLTextValidate::validateNonNegativeS32); + center(); + + return LLFloater::postBuild(); +} + +BOOL LLFloaterAssociateListing::handleKeyHere(KEY key, MASK mask) +{ + if (key == KEY_RETURN && mask == MASK_NONE) + { + apply(); + return TRUE; + } + else if (key == KEY_ESCAPE && mask == MASK_NONE) + { + cancel(); + return TRUE; + } + + return LLFloater::handleKeyHere(key, mask); +} + +// static +void LLFloaterAssociateListing::show(LLFloater* floater, const LLSD& folder_id) +{ + + if (!floater) return; + floater->mUUID = folder_id.asUUID(); + floater->open(); +} + +// Callback for apply if DAMA required... +void LLFloaterAssociateListing::callback_apply(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + apply(FALSE); + } +} + +void LLFloaterAssociateListing::apply(BOOL user_confirm) +{ + if (mUUID.notNull()) + { + S32 id = (S32)getChild("listing_id")->getValue().asInteger(); + if (id > 0) + { + // Check if the id exists in the merchant SLM DB: note that this record might exist in the LLMarketplaceData + // structure even if unseen in the UI, for instance, if its listing_uuid doesn't exist in the merchant inventory + LLUUID listing_uuid = LLMarketplaceData::instance().getListingFolder(id); + if (listing_uuid.notNull() && user_confirm && LLMarketplaceData::instance().getActivationState(listing_uuid)) + { + // Look for user confirmation before unlisting + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFloaterAssociateListing::callback_apply, this, _1, _2)); + return; + } + // Associate the id with the user chosen folder + LLMarketplaceData::instance().associateListing(mUUID,listing_uuid,id); + // Update the folder widgets now that the action is launched + update_marketplace_category(listing_uuid); + update_marketplace_category(mUUID); + } + else + { + LLNotificationsUtil::add("AlertMerchantListingInvalidID"); + } + } + closeFloater(); +} + +void LLFloaterAssociateListing::cancel() +{ + closeFloater(); +} + +//----------------------------------------------------------------------------- +// LLFloaterMarketplaceValidation +//----------------------------------------------------------------------------- + +LLFloaterMarketplaceValidation::LLFloaterMarketplaceValidation(const LLSD& key) +: LLFloater(key), + mEditor(NULL) +{ + //buildFromFile("floater_marketplace_validation.xml"); + LLUICtrlFactory::instance().buildFloater(this, "floater_marketplace_validation.xml"); +} + +BOOL LLFloaterMarketplaceValidation::postBuild() +{ + childSetAction("OK", onOK, this); + + // This widget displays the validation messages + mEditor = getChild("validation_text"); + mEditor->setEnabled(FALSE); + mEditor->setFocus(TRUE); + mEditor->setValue(LLSD()); + + return TRUE; +} + +LLFloaterMarketplaceValidation::~LLFloaterMarketplaceValidation() +{ +} + +// virtual +void LLFloaterMarketplaceValidation::draw() +{ + // draw children + LLFloater::draw(); +} + +void LLFloaterMarketplaceValidation::onOpen(const LLSD& key) +{ + // Clear the text panel + mEditor->setValue(LLSD()); + + // Validates the marketplace + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(marketplacelistings_id); + validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2), false); + } +} + +// static +void LLFloaterMarketplaceValidation::onOK( void* userdata ) +{ + // destroys this object + LLFloaterMarketplaceValidation* self = (LLFloaterMarketplaceValidation*) userdata; + self->closeFloater(); +} + +void LLFloaterMarketplaceValidation::appendMessage(std::string& message, LLError::ELevel log_level) +{ + if (mEditor) + { + // Errors are printed in bold, other messages in normal font + LLStyle::Params style; + LLFontDescriptor new_desc(mEditor->getFont()->getFontDesc()); + new_desc.setStyle(log_level == LLError::LEVEL_ERROR ? LLFontGL::BOLD : LLFontGL::NORMAL); + LLFontGL* new_font = LLFontGL::getFont(new_desc); + style.font = new_font; + mEditor->appendText(message, true, style); + } +} + +//----------------------------------------------------------------------------- +// LLFloaterItemProperties +//----------------------------------------------------------------------------- + +LLFloaterItemProperties::LLFloaterItemProperties(const LLSD& key) +: LLFloater(key) +{ +} + +LLFloaterItemProperties::~LLFloaterItemProperties() +{ +} + +BOOL LLFloaterItemProperties::postBuild() +{ + // On the standalone properties floater, we have no need for a back button... + LLPanel* panel = getChild("item_panel"); + LLButton* back_btn = panel->getChild("back_btn"); + back_btn->setVisible(FALSE); + + return LLFloater::postBuild(); +} + +void LLFloaterItemProperties::onOpen(/*const LLSD& key*/) +{ + // Tell the panel which item it needs to visualize + LLPanel* panel = getChild("item_panel"); + panel->setItemID(key["id"].asUUID()); +} + diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h new file mode 100644 index 000000000..e948b141d --- /dev/null +++ b/indra/newview/llfloatermarketplacelistings.h @@ -0,0 +1,204 @@ +/** + * @file llfloatermarketplacelistings.h + * @brief Implementation of the marketplace listings floater and panels + * @author merov@lindenlab.com + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERMARKETPLACELISTINGS_H +#define LL_LLFLOATERMARKETPLACELISTINGS_H + +#include "llfloater.h" +#include "llinventoryfilter.h" +#include "llinventorypanel.h" +#include "llnotificationptr.h" +#include "llmodaldialog.h" +#include "lltexteditor.h" + +class LLInventoryCategoriesObserver; +class LLInventoryCategoryAddedObserver; +class LLTextBox; +class LLView; +class LLFilterEditor; + +class LLFloaterMarketplaceListings; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLPanelMarketplaceListings +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLPanelMarketplaceListings : public LLPanel +{ +public: + LLPanelMarketplaceListings(); + BOOL postBuild(); + void draw(); + LLFolderView* getRootFolder() { return mRootFolder; } + + void buildAllPanels(); + +private: + LLInventoryPanel* buildInventoryPanel(const std::string& childname, const std::string& filename); + + // UI callbacks + void onViewSortMenuItemClicked(const LLSD& userdata); + bool onViewSortMenuItemCheck(const LLSD& userdata); + void onAddButtonClicked(); + void onAuditButtonClicked(); + void onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action); + void onTabChange(); + void onFilterEdit(const std::string& search_string); + + LLFolderView* mRootFolder; + LLButton* mAuditBtn; + LLFilterEditor* mFilterEditor; + std::string mFilterSubString; + LLInventoryFilter::ESortOrderType mSortOrder; + LLInventoryFilter::EFilterType mFilterType; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFloaterMarketplaceListings +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLFloaterMarketplaceListings : public LLFloater, public LLFloaterSingleton +{ +public: + LLFloaterMarketplaceListings(const LLSD& key); + ~LLFloaterMarketplaceListings(); + + void initializeMarketPlace(); + + // virtuals + BOOL postBuild(); + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + + void showNotification(const LLNotificationPtr& notification); + + BOOL handleHover(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + +protected: + void setup(); + void fetchContents(); + + void setStatusString(const std::string& statusString); + + void onClose(bool app_quitting); + void onOpen(const LLSD& key); + void onFocusReceived(); + void onChanged(); + + bool isAccepted(EAcceptance accept); + + void updateView(); + +private: + S32 getFolderCount(); + + LLInventoryCategoriesObserver * mCategoriesObserver; + LLInventoryCategoryAddedObserver * mCategoryAddedObserver; + + LLTextBox * mInventoryStatus; + LLView * mInventoryInitializationInProgress; + LLView * mInventoryPlaceholder; + LLTextBox * mInventoryText; + LLTextBox * mInventoryTitle; + + LLUUID mRootFolderId; + LLPanelMarketplaceListings * mPanelListings; + bool mFirstViewListings; +}; + +//----------------------------------------------------------------------------- +// LLFloaterAssociateListing +//----------------------------------------------------------------------------- +class LLFloaterAssociateListing : public LLFloater, public LLUISingleton +{ +friend class LLFloaterReg; +public: + virtual BOOL postBuild(); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + static bool visible(LLFloaterAssociateListing* floater, const LLSD& key) { floater && !floater->isMinimized() && floater->isInVisibleChain(); } + static void show(LLFloaterAssociateListing* floater, const LLSD& folder_id); + static void hide(LLFloaterAssociateListing* floater, const LLSD& key) { if (floater) floater->close(); } + +private: + LLFloaterAssociateListing(const LLSD& key); + virtual ~LLFloaterAssociateListing(); + + // UI Callbacks + void apply(BOOL user_confirm = TRUE); + void cancel(); + void callback_apply(const LLSD& notification, const LLSD& response); + + LLUUID mUUID; +}; + +//----------------------------------------------------------------------------- +// LLFloaterMarketplaceValidation +//----------------------------------------------------------------------------- +// Note: For the moment, we just display the validation text. Eventually, we should +// get the validation triggered on the server and display the html report. +// *TODO : morph into an html/text window using the pattern in llfloatertos + +class LLFloaterMarketplaceValidation : public LLFloater +{ +public: + LLFloaterMarketplaceValidation(const LLSD& key); + virtual ~LLFloaterMarketplaceValidation(); + + virtual BOOL postBuild(); + virtual void draw(); + virtual void onOpen(const LLSD& key); + + void appendMessage(std::string& message, LLError::ELevel log_level); + static void onOK( void* userdata ); + +private: + LLTextEditor* mEditor; +}; + +//----------------------------------------------------------------------------- +// LLFloaterItemProperties +//----------------------------------------------------------------------------- + +class LLFloaterItemProperties : public LLFloater +{ +public: + LLFloaterItemProperties(const LLSD& key); + virtual ~LLFloaterItemProperties(); + + BOOL postBuild(); + virtual void onOpen(const LLSD& key); + +private: +}; + +#endif // LL_LLFLOATERMARKETPLACELISTINGS_H + diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 4397eb088..796d6dea2 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -161,6 +161,12 @@ BOOL LLFloaterOutbox::postBuild() mCategoryAddedObserver = new LLOutboxAddedObserver(this); gInventory.addObserver(mCategoryAddedObserver); + // Setup callbacks for importer + LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance(); + importer.setInitializationErrorCallback(boost::bind(&LLFloaterOutbox::initializationReportError, this, _1, _2)); + importer.setStatusChangedCallback(boost::bind(&LLFloaterOutbox::importStatusChanged, this, _1)); + importer.setStatusReportCallback(boost::bind(&LLFloaterOutbox::importReportResults, this, _1, _2)); + return TRUE; } @@ -299,12 +305,8 @@ void LLFloaterOutbox::initializeMarketPlace() // Initialize the marketplace import API // LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance(); - if (!importer.isInitialized()) { - importer.setInitializationErrorCallback(boost::bind(&LLFloaterOutbox::initializationReportError, this, _1, _2)); - importer.setStatusChangedCallback(boost::bind(&LLFloaterOutbox::importStatusChanged, this, _1)); - importer.setStatusReportCallback(boost::bind(&LLFloaterOutbox::importReportResults, this, _1, _2)); importer.initialize(); } } diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h index 61a11c50d..78a175214 100644 --- a/indra/newview/llfloateroutbox.h +++ b/indra/newview/llfloateroutbox.h @@ -1,7 +1,6 @@ /** * @file llfloateroutbox.h - * @brief LLFloaterOutbox - * class definition + * @brief Implementation of the merchant outbox window * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code @@ -30,6 +29,7 @@ #include "llfloater.h" #include "llfoldertype.h" +#include "llinventoryfilter.h" #include "llnotificationptr.h" diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index f6e64a7d4..026dcf63f 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -64,6 +64,9 @@ public: virtual void previewItem( void ) = 0; virtual void selectItem(void) = 0; virtual void showProperties(void) = 0; + + virtual BOOL isItemWearable() const { return FALSE; } + virtual BOOL isItemRenameable() const = 0; virtual BOOL renameItem(const std::string& new_name) = 0; virtual void nameOrDescriptionChanged(void) const { } // Singu note: Currently only used by LLWearableBridge. diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index baa6093b0..7f3f9d71f 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -587,7 +587,8 @@ void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) void LLFolderViewItem::openItem( void ) { - if( mListener ) + if (!mListener) return; + if (mAllowWear || mListener->isItemWearable()) { mListener->openItem(); } @@ -1124,7 +1125,7 @@ LLFolderViewFolder::LLFolderViewFolder( const std::string& name, LLUIImagePtr ic LLUIImagePtr icon_open, LLUIImagePtr icon_link, LLFolderView* root, - LLFolderViewEventListener* listener ): + LLFolderViewEventListener* listener): LLFolderViewItem( name, icon, icon_open, icon_link, 0, root, listener ), // 0 = no create time mIsOpen(FALSE), mExpanderHighlighted(FALSE), @@ -1938,6 +1939,7 @@ void LLFolderViewFolder::destroyView() while (!mFolders.empty()) { LLFolderViewFolder *folderp = mFolders.back(); + mFolders.pop_back(); folderp->destroyView(); // removes entry from mFolders } @@ -2411,9 +2413,16 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, EAcceptance* accept, std::string& tooltip_msg) { + if (!mAllowDrop) + { + *accept = ACCEPT_NO; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot"); + return TRUE; + } + BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data); - - if (accepted) + + if (accepted) { mDragAndDropTarget = TRUE; *accept = ACCEPT_YES_MULTI; @@ -2797,6 +2806,7 @@ bool LLInventorySort::updateSort(U32 order) mByDate = (order & LLInventoryFilter::SO_DATE); mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); + mFoldersByWeight = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); return true; } return false; @@ -2834,9 +2844,7 @@ bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolde // We sort by name if we aren't sorting by date // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); + bool by_name = ((!mByDate || (mFoldersByName && (a->getSortGroup() != SG_ITEM))) && !mFoldersByWeight); if (a->getSortGroup() != b->getSortGroup()) { @@ -2868,6 +2876,31 @@ bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolde return (compare < 0); } } + else if (mFoldersByWeight) + { + S32 weight_a = compute_stock_count(a->getUUID()); + S32 weight_b = compute_stock_count(b->getUUID()); + if (weight_a == weight_b) + { + // Equal weight -> use alphabetical order + return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); + } + else if (weight_a == -1) + { + // No weight -> move a at the end of the list + return false; + } + else if (weight_b == -1) + { + // No weight -> move b at the end of the list + return true; + } + else + { + // Lighter is first (sorted in increasing order of weight) + return (weight_a < weight_b); + } + } else { // BUG: This is very very slow. The getCreationDate() is log n in number diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 56990360a..00c11badb 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -61,7 +61,8 @@ public: : mSortOrder(0), mByDate(false), mSystemToTop(false), - mFoldersByName(false) { } + mFoldersByName(false), + mFoldersByWeight(false) { } // Returns true if order has changed bool updateSort(U32 order); @@ -74,6 +75,7 @@ private: bool mByDate; bool mSystemToTop; bool mFoldersByName; + bool mFoldersByWeight; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -139,6 +141,8 @@ protected: BOOL mIsLoading; LLTimer mTimeSinceRequestStart; bool mShowLoadStatus; + bool mAllowWear; + bool mAllowDrop; std::string mSearchable; U32 mSearchType; @@ -240,6 +244,9 @@ public: void setShowLoadStatus(bool status) { mShowLoadStatus = status; } + void setAllowWear(bool allow) { mAllowWear = allow; } + void setAllowDrop(bool allow) { mAllowDrop = allow; } + // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. //virtual BOOL removeRecursively(BOOL single_item); diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index 6666d8a1a..b2d30af3a 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -62,35 +62,95 @@ using namespace LLOldEvents; namespace LLInventoryAction { - bool doToSelected(LLFolderView* folder, std::string action); + void callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action); + bool doToSelected(LLFolderView* root, std::string action, BOOL user_confirm = TRUE); + + void buildMarketplaceFolders(LLFolderView* root); + void updateMarketplaceFolders(); + std::list sMarketplaceFolders; // Marketplace folders that will need update once the action is completed } typedef LLMemberListener object_inventory_listener_t; typedef LLMemberListener inventory_listener_t; typedef LLMemberListener inventory_panel_listener_t; -bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) +std::list LLInventoryAction::sMarketplaceFolders; + +// Callback for doToSelected if DAMA required... +void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action) { - if (!folder) + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + doToSelected(folder, action, FALSE); + } +} + +bool LLInventoryAction::doToSelected(LLFolderView* root, std::string action, BOOL user_confirm) +{ + if (!root) return true; + + std::set selected_items = root->getSelectionList(); + + // Prompt the user and check for authorization for some marketplace active listing edits + if (user_confirm && (("delete" == action) || ("cut" == action) || ("rename" == action) || ("properties" == action) || ("task_properties" == action) || ("open" == action))) + { + std::set::iterator set_iter = std::find_if(selected_items.begin(), selected_items.end(), boost::bind(&depth_nesting_in_marketplace, _1) >= 0); + if (set_iter != selected_items.end()) + { + if ("open" == action) + { + if (get_can_item_be_worn(*set_iter)) + { + // Wearing an object from any listing, active or not, is verbotten + LLNotificationsUtil::add("AlertMerchantListingCannotWear"); + return; + } + // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... + } + else if (LLMarketplaceData::instance().isInActiveFolder(*set_iter) || + LLMarketplaceData::instance().isListedAndActive(*set_iter)) + { + // If item is in active listing, further confirmation is required + if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(*set_iter) || LLMarketplaceData::instance().isVersionFolder(*set_iter))) + { + // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); + return; + } + // Any other case will simply modify but not unlist a listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); + return; + } + } + } + + // Keep track of the marketplace folders that will need update of their status/name after the operation is performed + buildMarketplaceFolders(root); + LLInventoryModel* model = &gInventory; if ("rename" == action) { - folder->startRenamingSelectedItem(); + root->startRenamingSelectedItem(); + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); return true; } - else if ("delete" == action) + + if ("delete" == action) { - folder->removeSelectedItems(); + root->removeSelectedItems(); + + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); return true; } - else if ("copy" == action || "cut" == action) + if ("copy" == action || "cut" == action) { LLInventoryClipboard::instance().reset(); } - std::set selected_items = folder->getSelectionList(); - LLMultiFloater* multi_floaterp = NULL; if (("task_open" == action || "open" == action) && selected_items.size() > 1) @@ -118,7 +178,7 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - LLFolderViewItem* folder_item = folder->getItemByID(*set_iter); + LLFolderViewItem* folder_item = root->getItemByID(*set_iter); if(!folder_item) continue; LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); if(!bridge) continue; @@ -126,6 +186,9 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) bridge->performAction(model, action); } + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); + LLFloater::setFloaterHost(NULL); if (multi_floaterp) { @@ -135,7 +198,79 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) return true; } -struct LLNewWindow : public inventory_listener_t +void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) +{ + // Make a list of all marketplace folders containing the elements in the selected list + // as well as the elements themselves. + // Once those elements are updated (cut, delete in particular but potentially any action), their + // containing folder will need to be updated as well as their initially containing folder. For + // instance, moving a stock folder from a listed folder to another will require an update of the + // target listing *and* the original listing. So we need to keep track of both. + sMarketplaceFolders.clear(); + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + std::set selected_items = root->getSelectionList(); + for (std::set::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + const LLInventoryObject* obj(gInventory.getObject(*set_iter)); + if (gInventory.isObjectDescendentOf(obj->getParentUUID(), marketplacelistings_id)) + { + sMarketplaceFolders.push_back(obj->getParentUUID()); + sMarketplaceFolders.push_back(obj->getUUID()); + } + } + // Suppress dupes in the list so we wo't update listings twice + sMarketplaceFolders.sort(); + sMarketplaceFolders.unique(); +} + +void LLInventoryAction::updateMarketplaceFolders() +{ + while (!sMarketplaceFolders.empty()) + { + update_marketplace_category(sMarketplaceFolders.back()); + sMarketplaceFolders.pop_back(); + } +} + + + +class LLDoToSelectedPanel : public object_inventory_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLPanelObjectInventory *panel = mPtr; + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLDoToSelectedFloater : public inventory_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLInventoryPanel *panel = mPtr->getPanel(); + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLDoToSelected : public inventory_panel_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLInventoryPanel *panel = mPtr; + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLNewWindow : public inventory_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 537358377..492694938 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -44,6 +44,7 @@ #include "llcallingcard.h" #include "llfirstuse.h" #include "llfloatercustomize.h" +#include "llfloatermarketplacelistings.h" #include "llfloateropenobject.h" #include "llfloaterproperties.h" #include "llfloaterworldmap.h" @@ -72,6 +73,7 @@ #include "llselectmgr.h" #include "lltooldraganddrop.h" #include "lltrans.h" +#include "llurlaction.h" #include "llviewerassettype.h" #include "llviewerfoldertype.h" #include "llviewermenu.h" @@ -92,11 +94,6 @@ #include "rlvlocks.h" // [/RLVa:KB] -// Marketplace outbox current disabled -#define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 -#define ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU 1 -#define BLOCK_WORN_ITEMS_IN_OUTBOX 1 - typedef std::pair two_uuids_t; typedef std::list two_uuids_list_t; @@ -138,11 +135,6 @@ bool isMarketplaceCopyAction(const std::string& action) return (("copy_to_outbox" == action) || ("move_to_outbox" == action)); } -bool isMarketplaceSendAction(const std::string& action) -{ - return ("send_to_marketplace" == action); -} - // Used by LLFolderBridge as callback for directory fetching recursion class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver { @@ -278,8 +270,40 @@ BOOL LLInvFVBridge::isLibraryItem() const BOOL LLInvFVBridge::cutToClipboard() { if(isItemMovable()) + { + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); + if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || + LLMarketplaceData::instance().isListedAndActive(mUUID))) + { + // Prompt the user if cutting from a marketplace active listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInvFVBridge::callback_cutToClipboard, this, _1, _2)); + } + else + { + // Otherwise just perform the cut + return perform_cutToClipboard(); + } + } + return FALSE; +} + +BOOL LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + return perform_cutToClipboard(); + } + return FALSE; +} + +BOOL LLInvFVBridge::perform_cutToClipboard() +{ + if (isItemMovable()) { LLInventoryClipboard::instance().cut(mUUID); + LLFolderView::removeCutItems(); return true; } return false; @@ -296,15 +320,21 @@ BOOL LLInvFVBridge::copyToClipboard() const return FALSE; } -// *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { - show_item_profile(mUUID); - - // Disable old properties floater; this is replaced by the sidepanel. - /* - LLFloaterReg::showInstance("properties", mUUID); - */ + if (isMarketplaceListingsFolder()) + { + show_item_profile(mUUID); + LLFloater* floater_properties = LLFloaterProperties::find(mUUID, LLUUID::null); + if (floater_properties) + { + floater_properties->setVisibleAndFrontmost(); + } + } + else + { + show_item_profile(mUUID); + } } void LLInvFVBridge::removeBatch(std::vector& batch) @@ -751,7 +781,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if (!isInboxFolder()) { items.push_back(std::string("Rename")); - if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) { disabled_items.push_back(std::string("Rename")); } @@ -789,7 +819,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Cut")); } - if (canListOnMarketplace()) + if (canListOnMarketplace() && !isMarketplaceListingsFolder()) { items.push_back(std::string("Marketplace Separator")); @@ -980,7 +1010,7 @@ void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, disabled_items.push_back(std::string("Rename")); } -#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU + // Singu TODO: Remove this block when removing outbox. if (isOutboxFolderDirectParent()) { items.push_back(std::string("Marketplace Separator")); @@ -991,7 +1021,110 @@ void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, disabled_items.push_back(std::string("Marketplace Send")); } } -#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU +} + +void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, + menuentry_vec_t &items, + menuentry_vec_t &disabled_items) +{ + S32 depth = depth_nesting_in_marketplace(mUUID); + if (depth == 1) + { + // Options available at the Listing Folder level + items.push_back(std::string("Marketplace Create Listing")); + items.push_back(std::string("Marketplace Associate Listing")); + items.push_back(std::string("Marketplace Disassociate Listing")); + items.push_back(std::string("Marketplace List")); + items.push_back(std::string("Marketplace Unlist")); + if (LLMarketplaceData::instance().isUpdating(mUUID)) + { + // During SLM update, disable all marketplace related options + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + disabled_items.push_back(std::string("Marketplace Disassociate Listing")); + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + items.push_back(std::string("Marketplace Get Listing")); + } + if (LLMarketplaceData::instance().isListed(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + if (LLMarketplaceData::instance().getVersionFolder(mUUID).isNull()) + { + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace List")); + } + else + { + disabled_items.push_back(std::string("Marketplace Unlist")); + } + } + } + else + { + disabled_items.push_back(std::string("Marketplace Disassociate Listing")); + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + disabled_items.push_back(std::string("Marketplace Get Listing")); + } + } + } + } + if (depth == 2) + { + // Options available at the Version Folder levels and only for folders + LLInventoryCategory* cat = gInventory.getCategory(mUUID); + if (cat && LLMarketplaceData::instance().isListed(cat->getParentUUID())) + { + items.push_back(std::string("Marketplace Activate")); + items.push_back(std::string("Marketplace Deactivate")); + if (LLMarketplaceData::instance().isUpdating(mUUID)) + { + // During SLM update, disable all marketplace related options + disabled_items.push_back(std::string("Marketplace Activate")); + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + else + { + if (LLMarketplaceData::instance().isVersionFolder(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Activate")); + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + else + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + } + } + // Options available at all levels on items and categories + items.push_back(std::string("Marketplace Edit Listing")); + LLUUID listing_folder_id = nested_parent_id(mUUID,depth); + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(listing_folder_id); + if (!LLMarketplaceData::instance().isListed(listing_folder_id) || version_folder_id.isNull()) + { + disabled_items.push_back(std::string("Marketplace Edit Listing")); + } + // Separator + items.push_back(std::string("Marketplace Listings Separator")); } // *TODO: remove this @@ -1109,6 +1242,18 @@ BOOL LLInvFVBridge::isInboxFolder() const return gInventory.isObjectDescendentOf(mUUID, inbox_id); } +BOOL LLInvFVBridge::isMarketplaceListingsFodler() const +{ + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + + if (folder_id.isNull()) + { + return FALSE; + } + + return gInventory.isObjectDescendentOf(mUUID, folder_id); +} + BOOL LLInvFVBridge::isOutboxFolder() const { const LLUUID outbox_id = getOutboxFolder(); @@ -1318,6 +1463,22 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } } +void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid) +{ + // Keep track of the parent + LLInventoryItem* itemp = mode->getItem(uuid); + LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null); + // Remove the object + model->removeObject(uuid); + // Get the parent updated + if (parent_id.notNull()) + { + LLViewerInventoryCategory* parent_cat = model->getCategory(parent_id); + model->updateCategory(parent_cat); + model->notifyObservers(); + } +} + bool LLInvFVBridge::canShare() const { bool can_share = false; @@ -1348,8 +1509,6 @@ bool LLInvFVBridge::canShare() const bool LLInvFVBridge::canListOnMarketplace() const { -#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU - LLInventoryModel * model = getInventoryModel(); const LLViewerInventoryCategory * cat = model->getCategory(mUUID); @@ -1388,16 +1547,10 @@ bool LLInvFVBridge::canListOnMarketplace() const } return true; - -#else - return false; -#endif } bool LLInvFVBridge::canListOnMarketplaceNow() const { -#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU - bool can_list = true; // Do not allow listing while import is in progress @@ -1442,10 +1595,6 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const } return can_list; - -#else - return false; -#endif } @@ -1520,7 +1669,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); return; } else if ("copy" == action) @@ -1573,6 +1721,11 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + LLUrlAction::openURL(url); + } } void LLItemBridge::selectItem() @@ -1981,6 +2134,83 @@ void LLFolderBridge::buildDisplayName() const mDisplayName.assign(getName()); } +std::string LLFolderBridge::getLabelSuffix() const +{ + /* Singu TODO? + static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); + + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + */ + + if (isMarketplaceListingsFolder()) + { + std::string suffix = ""; + // Listing folder case + if (LLMarketplaceData::instance().isListed(getUUID())) + { + suffix = llformat("%d",LLMarketplaceData::instance().getListingID(getUUID())); + if (suffix.empty()) + { + suffix = LLTrans::getString("MarketplaceNoID"); + } + suffix = " (" + suffix + ")"; + if (LLMarketplaceData::instance().getActivationState(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceLive") + ")"; + } + } + // Version folder case + else if (LLMarketplaceData::instance().isVersionFolder(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceActive") + ")"; + } + // Add stock amount + S32 stock_count = compute_stock_count(getUUID()); + if (stock_count == 0) + { + suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")"; + } + else if (stock_count != -1) + { + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + suffix += " (" + LLTrans::getString("MarketplaceStock") + "=" + llformat("%d", stock_count) + ")"; + } + else + { + suffix += " (" + LLTrans::getString("MarketplaceMax") + "=" + llformat("%d", stock_count) + ")"; + } + } + // Add updating suffix + if (LLMarketplaceData::instance().isUpdating(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + return LLInvFVBridge::getLabelSuffix() + suffix; + } + else + { + return LLInvFVBridge::getLabelSuffix(); + } +} + +LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const +{ + if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(getUUID())) + { + return LLFontGL::BOLD; + } + else + { + return LLFontGL::NORMAL; + } +} + + + // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -2018,6 +2248,11 @@ BOOL LLFolderBridge::isItemRemovable() const } } + if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(mUUID)) + { + return FALSE; + } + return TRUE; } @@ -2167,84 +2402,10 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const } -static BOOL can_move_to_outbox(LLInventoryItem* inv_item) -{ - // Collapse links directly to items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - if (linked_item != NULL) - { - inv_item = linked_item; - } - - bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); - if (!allow_transfer) - { - return false; - } - -#if BLOCK_WORN_ITEMS_IN_OUTBOX - bool worn = get_is_item_worn(inv_item->getUUID()); - if (worn) - { - return false; - } -#endif - - bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); - if (calling_card) - { - return false; - } - - return true; -} - - -int get_folder_levels(LLInventoryCategory* inv_cat) -{ - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); - - int max_child_levels = 0; - - for (size_t i=0; i < cats->size(); ++i) - { - LLInventoryCategory* category = cats->at(i); - max_child_levels = llmax(max_child_levels, get_folder_levels(category)); - } - - return 1 + max_child_levels; -} - -int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) -{ - int depth = 0; - - if (ancestor_id == descendant_id) return depth; - - const LLInventoryCategory* category = gInventory.getCategory(descendant_id); - - while(category) - { - LLUUID parent_id = category->getParentUUID(); - - if (parent_id.isNull()) break; - - depth++; - - if (parent_id == ancestor_id) return depth; - - category = gInventory.getCategory(parent_id); - } - - LL_WARNS() << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; - return -1; -} BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, - BOOL drop) + BOOL drop, + BOOL user_confirm) { LLInventoryModel* model = getInventoryModel(); @@ -2257,10 +2418,14 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID from_folder_uuid = inv_cat->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); const BOOL move_is_from_outbox = model->isObjectDescendentOf(cat_id, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); // check to make sure source is agent inventory, and is represented there. LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); @@ -2283,6 +2448,18 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL is_movable = TRUE; + if (is_movable && (marketplacelistings_id == cat_id)) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot"); + } + if (is_movable && move_is_from_marketplacelistings && LLMarketplaceData::instance().getActivationState(cat_id)) + { + // If the incoming folder is listed and active (and is therefore either the listing or the version folder), + // then moving is *not* allowed + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxDragActive"); + } // Can't move a folder into itself if (is_movable && (mUUID == cat_id)) { @@ -2386,90 +2563,18 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = RlvFolderLocks::instance().canMoveFolder(cat_id, mUUID); } // [/RLVa:KB] - if (is_movable && move_is_into_outbox) + if (is_movable && move_is_into_marketplacelistings) { - const int nested_folder_levels = get_folder_path_length(outbox_id, mUUID) + get_folder_levels(inv_cat); - - if (nested_folder_levels > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderDepth")) - { - is_movable = FALSE; - } - else - { - int dragged_folder_count = descendent_categories.size(); - int existing_item_count = 0; - int existing_folder_count = 0; - - const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); - - if (master_folder != NULL) - { - if (model->isObjectDescendentOf(cat_id, master_folder->getUUID())) - { - // Don't use count because we're already inside the same category anyway - dragged_folder_count = 0; - } - else - { - existing_folder_count = 1; // Include the master folder in the count! + // One cannot move a folder into a stock folder + is_movable = (getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK); + } - // If we're in the drop operation as opposed to the drag without drop, we are doing a - // single category at a time so don't block based on the total amount of cargo data items - if (drop) - { - dragged_folder_count += 1; - } - else - { - // NOTE: The cargo id's count is a total of categories AND items but we err on the side of - // prevention rather than letting too many folders into the hierarchy of the outbox, - // when we're dragging the item to a new parent - dragged_folder_count += LLToolDragAndDrop::instance().getCargoIDsCount(); - } - } - - // Tally the total number of categories and items inside the master folder - - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - - model->collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); - - existing_folder_count += existing_categories.size(); - existing_item_count += existing_items.size(); - } - else - { - // Assume a single category is being dragged to the outbox since we evaluate one at a time - // when not putting them under a parent item. - dragged_folder_count += 1; - } - - const int nested_folder_count = existing_folder_count + dragged_folder_count; - const int nested_item_count = existing_item_count + descendent_items.size(); - - if (nested_folder_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) - { - is_movable = FALSE; - } - else if (nested_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - is_movable = FALSE; - } - - if (is_movable == TRUE) - { - for (size_t i=0; i < descendent_items.size(); ++i) - { - LLInventoryItem* item = descendent_items[i]; - if (!can_move_to_outbox(item)) - { - is_movable = FALSE; - break; - } - } - } - } + if (is_movable && (move_is_into_outbox || move_is_into_marketplacelistings)) + { + const LLViewerInventoryCategory* master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + LLViewerInventoryCategory * dest_folder = getCategory(); + S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); + is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); } // @@ -2479,6 +2584,37 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (accept && drop) { + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if (move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(cat_id) || + LLMarketplaceData::instance().isListedAndActive(cat_id))) + { + if (LLMarketplaceData::instance().isListed(cat_id) || LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Move the active version folder or listing folder itself outside marketplace listings will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + else + { + // Any other case will simply modify but not unlist an active listed listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + return true; + } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Moving the version folder from its location will deactivate it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantClearVersion", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID)) + { + // Moving something in an active listed listing will modify it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + } // Look for any gestures and deactivate them if (move_is_into_trash) { @@ -2523,6 +2659,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { copy_folder_to_outbox(inv_cat, mUUID, cat_id, LLToolDragAndDrop::getOperationId()); } + else if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(inv_cat, mUUID); + } else { if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) @@ -2538,11 +2678,31 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, mUUID, move_is_into_trash); } + if (move_is_from_marketplacelistings) + { + // If we move from an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + if (!validate_marketplacelistings(cat, NULL)) + { + LLMarketplaceData::instance().activateListing(version_folder_id,false); + } + } + // Update the listing we moved from anyway + update_marketplace_category(from_folder_uuid); + // Clear the folder from the marketplace in case it is a listing folder + if (LLMarketplaceData::instance().isListed(cat_id)) + { + LLMarketplaceData::instance().clearListing(cat_id); + } + } } } else if (LLToolDragAndDrop::SOURCE_WORLD == source) { - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { accept = FALSE; } @@ -2553,7 +2713,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) { - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { accept = FALSE; } @@ -2923,7 +3083,6 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); return; } else if ("copy" == action) @@ -2955,6 +3114,82 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) restoreItem(); return; } + else if ("marketplace_list" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + mMessage = ""; + if (!validate_marketplacelistings(cat, boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2))) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().activateListing(mUUID,true); + } + } + return; + } + else if ("marketplace_active" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); + } + return; + } + else if ("marketplace_unlist" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLMarketplaceData::instance().activateListing(mUUID, false); + } + return; + } + else if ("marketplace_deactivate" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null); + } + return; + } + else if ("marketplace_create_listing" == action) + { + LLMarketplaceData::instance().createListing(mUUID); + return; + } + else if ("marketplace_disassociate_listing" == action) + { + LLMarketplaceData::instance().clearListing(mUUID); + return; + } + else if ("marketplace_get_listing" == action) + { + // This is used only to exercise the SLM API but won't be shown to end users + LLMarketplaceData::instance().getListing(mUUID); + return; + } + else if ("marketplace_associate_listing" == action) + { + LLFloaterAssociateListing::show(mUUID); + return; + } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + if (!url.empty()) + { + LLUrlAction::openURL(url); + } + return; + } // Move displaced inventory to lost and found else if ("move_to_lost_and_found" == action) { @@ -2979,18 +3214,30 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); } -#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU - else if (isMarketplaceSendAction(action)) + // Singu TODO: Remove this block when removing outbox. + else if ("send_to_marketplace" == action) { LL_INFOS() << "Send to marketplace action!" << LL_ENDL; - - /*LLInventoryCategory * cat = gInventory.getCategory(mUUID); - if (!cat) return; - - send_to_marketplace(cat);*/ LLMarketplaceInventoryImporter::instance().triggerImport(); } -#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU +} + +void LLFolderBridge::gatherMessage(std::string& message, LLError::ELevel log_level) +{ + if (log_level >= LLError::LEVEL_ERROR) + { + if (!mMessage.empty()) + { + // Currently, we do not gather all messages as it creates very long alerts + // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button + //mMessage += "\n"; + return; + } + // Take the leading spaces out... + std::string::size_type start = message.find_first_not_of(" "); + // Append the message + mMessage += message.substr(start, message.length() - start); + } } void LLFolderBridge::openItem() @@ -3060,27 +3307,38 @@ LLFolderType::EType LLFolderBridge::getPreferredType() const // Icons for folders are based on the preferred type LLUIImagePtr LLFolderBridge::getIcon() const { - LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - preferred_type = cat->getPreferredType(); - } - return getIcon(preferred_type); + return getFolderIcon(FALSE); } -// static +LLUIImagePtr LLFolderBridge::getOpenIcon() const +{ + return getFolderIcon(TRUE); +} + +LLUIImagePtr LLFolderBridge::getFolderIcon(BOOL is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + S32 depth = depth_nesting_in_marketplace(mUUID); + if ((preferred_type == LLFolderType::FT_NONE) && (depth == 2)) + { + // We override the type when in the marketplace listings folder and only for version folder + preferred_type = LLFolderType::FT_MARKETPLACE_VERSION; + } + else if ((preferred_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth == -1)) + { + // We override the type when a stock folder is outside of the marketplace listings root + // as we don't want to export that notion outside of marketplace + preferred_type = LLFolderType::FT_NONE; + } + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); +} + +// static : use by LLLinkFolderBridge to get the closed type icons LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); } -LLUIImagePtr LLFolderBridge::getIconOpen() const -{ - return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE)); - -} - LLUIImagePtr LLFolderBridge::getIconOverlay() const { if (getInventoryObject() && getInventoryObject()->getIsLinkType()) @@ -3160,57 +3418,80 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re void LLFolderBridge::pasteFromClipboard(bool only_copies) { LLInventoryModel* model = getInventoryModel(); - if(model && isClipboardPasteable()) + if (model && isClipboardPasteable()) + { + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const BOOL paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + + if (paste_into_marketplacelistings && !LLMarketplaceData::instance().isListed(mUUID) && LLMarketplaceData::instance().isInActiveFolder(mUUID)) + { + // Prompt the user if pasting in a marketplace active version listing (note that pasting right under the listing folder root doesn't need a prompt) + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2)); + } + else + { + // Otherwise just do the paste + perform_pasteFromClipboard(); + } + } +} + +// Callback for pasteFromClipboard if DAMA required... +void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + perform_pasteFromClipboard(); + } +} + +void LLFolderBridge::perform_pasteFromClipboard() +{ + LLInventoryModel* model = getInventoryModel(); + if (model && isClipboardPasteable()) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); std::vector objects; LLInventoryClipboard::instance().retrieve(objects); - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); - - if (outbox_itemp) + std::string error_msg; + const LLViewerInventoryCategory* master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + LLViewerInventoryCategory* dest_folder = getCategory(); + for (LLDynamicArray::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { - //LLToolDragAndDrop::instance().setCargoCount(objects.size()); + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); - BOOL can_list = TRUE; - - for (std::vector::const_iterator iter = objects.begin(); - (iter != objects.end()) && (can_list == TRUE); - ++iter) + if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size(), true)) { - const LLUUID& item_id = (*iter); - LLInventoryItem *item = model->getItem(item_id); - - if (item) - { - MASK mask = 0x0; - BOOL drop = FALSE; - EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(item->getActualType()); - void * cargo_data = (void *) item; - //std::string tooltip_msg; - - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data/*, tooltip_msg*/); - } + break; } - - //LLToolDragAndDrop::instance().resetCargoCount(); - - if (can_list == FALSE) + if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size(), true, true)) { - // Notify user of failure somehow -- play error sound? modal dialog? - return; + break; } } + if (!error_msg.empty()) + { + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } } const LLUUID parent_id(mUUID); @@ -3242,7 +3523,20 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) llassert(vicat); if (vicat) { - changeCategoryParent(model, vicat, parent_id, FALSE); + // Clear the cut folder from the marketplace if it is a listing folder + if (LLMarketplaceData::instance().isListed(item_id)) + { + LLMarketplaceData::instance().clearListing(item_id); + } + if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(vicat, parent_id); + } + else + { + //changeCategoryParent() implicitly calls dirtyFilter + changeCategoryParent(model, vicat, parent_id, FALSE); + } } } else @@ -3251,7 +3545,19 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) llassert(viitem); if (viitem) { - changeItemParent(model, viitem, parent_id, FALSE); + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + } + else + { + //changeItemParent() implicitly calls dirtyFilter + changeItemParent(model, viitem, parent_id, FALSE); + } } } } @@ -3269,11 +3575,18 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) llassert(vicat); if (vicat) { - copy_inventory_category(model, vicat, parent_id); + if (move_is_into_marketplacelistings) + { + move_folder_into_marketplacelistings(vicat, parent_id, true); + } + else + { + copy_inventory_category(model, vicat, parent_id); + } } } // [SL:KB] - Patch: Inventory-Links | Checked: 2010-04-12 (Catznip-2.2.0a) | Added: Catznip-2.0.0a - else if (!only_copies && LLAssetType::lookupIsLinkType(item->getActualType())) + else if (!move_is_into_marketplacelistings && !only_copies && LLAssetType::lookupIsLinkType(item->getActualType())) { link_inventory_object( parent_id, @@ -3281,15 +3594,29 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) NULL); } // [/SL:KB] + else + { + LLViewerInventoryItem* viitem = dynamic_cast(item); + llassert(viitem); + if (viitem) + { + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id, true)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + } else { copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), - item->getUUID(), - parent_id, - std::string(), - LLPointer(NULL)); + item->getUUID(), + parent_id, + std::string(), + LLPointer(NULL)); } } } @@ -3307,13 +3634,15 @@ void LLFolderBridge::pasteLinkFromClipboard() const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { // Notify user of failure somehow -- play error sound? modal dialog? return; @@ -3377,8 +3706,9 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID& lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + const LLUUID& marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); if (lost_and_found_id == mUUID) @@ -3393,6 +3723,30 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("New Clothes")); disabled_items.push_back(std::string("New Body Parts")); } + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, mItems, mDisabledItems); + if (LLMarketplaceData::instance().isUpdating(mUUID)) + { + mDisabledItems.push_back(std::string("New Folder")); + mDisabledItems.push_back(std::string("Rename")); + mDisabledItems.push_back(std::string("Cut")); + mDisabledItems.push_back(std::string("Copy")); + mDisabledItems.push_back(std::string("Paste")); + mDisabledItems.push_back(std::string("Delete")); + } + else if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + mDisabledItems.push_back(std::string("New Folder")); + } + } + if (marketplace_listings_id == mUUID) + { + mDisabledItems.push_back(std::string("New Folder")); + mDisabledItems.push_back(std::string("Rename")); + mDisabledItems.push_back(std::string("Cut")); + mDisabledItems.push_back(std::string("Delete")); + } if(trash_id == mUUID) { // This is the trash. @@ -3420,12 +3774,18 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if(panel && !panel->getFilterWorn()) if (!isInboxFolder() && !isOutboxFolder()) // don't allow creation in inbox or outbox { - items.push_back(std::string("New Folder")); - items.push_back(std::string("New Script")); - items.push_back(std::string("New Note")); - items.push_back(std::string("New Gesture")); - items.push_back(std::string("New Clothes")); - items.push_back(std::string("New Body Parts")); + { + items.push_back(std::string("New Folder")); + } + if (!isMarketplaceListingsFolder()) + { + items.push_back(std::string("New Folder")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); + } } getClipboardEntries(false, items, disabled_items, flags); } @@ -3482,7 +3842,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("Delete System Folder")); } - if (!isOutboxFolder() && !isItemInTrash()) // + if (!isOutboxFolder() && !isMarketplaceListingsFolder() && !isItemInTrash()) // { items.push_back(std::string("Share")); if (!canShare()) @@ -3549,6 +3909,12 @@ void build_context_menu_folder_options(LLInventoryModel* model, const LLUUID& mU if (trash_id == mUUID) return; if (model->isObjectDescendentOf(mUUID, trash_id)) return; + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Delete")); + } + if (isMarketplaceListingsFolder()) return; + items.push_back(std::string("Open Folder In New Window")); LLFolderType::EType type = category->getPreferredType(); @@ -3962,11 +4328,34 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c } } +// Callback for drop item if DAMA required... +void LLFolderBridge::callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + //std::string tooltip_msg; + dragItemIntoFolder(inv_item, TRUE, /*tooltip_msg,*/ FALSE); + } +} + +// Callback for drop category if DAMA required... +void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + //std::string tooltip_msg; + dragCategoryIntoFolder(inv_category, TRUE, /*tooltip_msg,*/ FALSE); + } +} + // This is used both for testing whether an item can be dropped // into the folder, as well as performing the actual drop, depending // if drop == TRUE. BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop) + BOOL drop, + BOOL user_confirm) { LLInventoryModel* model = getInventoryModel(); @@ -3982,6 +4371,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID from_folder_uuid = inv_item->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_favorites = (mUUID == favorites_id); @@ -3990,6 +4381,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); BOOL accept = FALSE; @@ -4079,31 +4472,11 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = can_move_to_landmarks(inv_item); } - else if (move_is_into_outbox) + else if (move_is_into_outbox || move_is_into_marketplacelistings) { - accept = can_move_to_outbox(inv_item); - - if (accept) - { - const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); - - int existing_item_count = LLToolDragAndDrop::instance().getCargoIDsCount(); - - if (master_folder != NULL) - { - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - - gInventory.collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); - - existing_item_count += existing_items.size(); - } - - if (existing_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - accept = FALSE; - } - } + const LLViewerInventoryCategory* master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + LLViewerInventoryCategory* dest_folder = getCategory(); + accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, 1/*LLToolDragAndDrop::instance().getCargoCount()*/); } LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); @@ -4130,6 +4503,17 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { active_panel->unSelectAll(); } + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if ((move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(inv_item->getUUID()) + || LLMarketplaceData::instance().isListedAndActive(inv_item->getUUID()))) || + (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID))) + { + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } + } //-------------------------------------------------------------------------------- // Destination folder logic @@ -4161,6 +4545,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { dropToOutfit(inv_item, move_is_into_current_outfit); } + // MERCHANT OUTBOX folder + // Move the item else if (move_is_into_outbox) { if (move_is_from_outbox) @@ -4172,6 +4558,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, copy_item_to_outbox(inv_item, mUUID, LLUUID::null, LLToolDragAndDrop::getOperationId()); } } + // MARKETPLACE LISTINGS folder + // Move the item + else if (move_is_into_marketplacelistings) + { + move_item_to_marketplacelistings(inv_item, mUUID); + } // NORMAL or TRASH folder // (move the item, restamp if into trash) else @@ -4189,7 +4581,23 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, move_is_into_trash); } - // + if (move_is_from_marketplacelistings) + { + // If we move from an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + if (!validate_marketplacelistings(cat, NULL)) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + } + // Update the listing we moved from anyway + update_marketplace_category(from_folder_uuid); + } + + // //-------------------------------------------------------------------------------- } } @@ -4238,7 +4646,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = FALSE; } - else if (move_is_into_outbox) + else if (move_is_into_outbox || move_is_into_marketplacelistings) { accept = FALSE; } @@ -4268,7 +4676,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) { - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { accept = FALSE; } @@ -4294,7 +4702,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = TRUE; - if (move_is_into_outbox) + if (move_is_into_outbox || move_is_into_marketplacelistings) { accept = FALSE; } @@ -4437,6 +4845,12 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addOutboxContextMenuOptions(flags, items, disabled_items); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { items.push_back(std::string("Share")); @@ -4528,6 +4942,12 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addOutboxContextMenuOptions(flags, items, disabled_items); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { if (isItemInTrash()) @@ -4603,6 +5023,12 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addOutboxContextMenuOptions(flags, items, disabled_items); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { if(isItemInTrash()) @@ -5053,6 +5479,25 @@ void LLNotecardBridge::openItem() } } +void LLNotecardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLNotecardBridge::buildContextMenu()" << LL_ENDL; + + if (isMarketplaceListingsFolder()) + { + menuentry_vec_t items; + menuentry_vec_t disabled_items; + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + hide_context_entries(menu, items, disabled_items); + } + else + { + LLItemBridge::buildContextMenu(menu, flags); + } +} + // +=================================================+ // | LLGestureBridge | // +=================================================+ @@ -5193,6 +5638,12 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Delete")); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { items.push_back(std::string("Share")); @@ -5247,6 +5698,12 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Delete")); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { if(isItemInTrash()) @@ -5580,6 +6037,12 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Delete")); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { items.push_back(std::string("Share")); @@ -5864,6 +6327,12 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Delete")); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere BOOL can_open = ((flags & SUPPRESS_OPEN_ITEM) != SUPPRESS_OPEN_ITEM); @@ -6264,6 +6733,12 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addOutboxContextMenuOptions(flags, items, disabled_items); } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } else { items.push_back(std::string("Properties")); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 0249458a3..76b10f03a 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -141,6 +141,9 @@ protected: virtual void addOutboxContextMenuOptions(U32 flags, menuentry_vec_t &items, menuentry_vec_t &disabled_items); + virtual void addMarketplaceContextMenuOptions(U32 flags, + menuentry_vec_t &items, + menuentry_vec_t &disabled_items); protected: LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); @@ -154,6 +157,7 @@ protected: BOOL isCOFFolder() const; // true if COF or descendant of BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox BOOL isOutboxFolderDirectParent() const; + BOOL isMarketplaceListingsFolder() const; // true if descendant of Marketplace listings folder const LLUUID getOutboxFolder() const; virtual BOOL isItemPermissive() const; @@ -167,7 +171,10 @@ protected: BOOL restamp); void removeBatchNoCheck(std::vector& batch); public: - BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox + + BOOL callback_cutToClipboard(const LLSD& notification, const LLSD& response); + BOOL perform_cutToClipboard(); + protected: LLHandle mInventoryPanel; LLFolderView* mRoot; @@ -178,6 +185,7 @@ protected: void purgeItem(LLInventoryModel *model, const LLUUID &uuid); virtual void buildDisplayName() const {} + void removeObject(LLInventoryModel* model, const LLUUID& uuid); }; class AIFilePicker; @@ -247,8 +255,10 @@ public: mWearables(FALSE) {} - BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); + BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, BOOL user_confirm = TRUE); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, BOOL user_confirm = TRUE); + void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item); + void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_item); virtual void buildDisplayName() const; @@ -266,6 +276,9 @@ public: static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); + virtual std::string getLabelSuffix() const; + virtual LLFontGL::StyleFlags getLabelStyle() const; + virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); @@ -334,9 +347,14 @@ public: static void staticFolderOptionsMenu(); private: + void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response); + void perform_pasteFromClipboard(); + void gatherMessage(std::string& message, LLError::ELevel log_level); + LLUIImagePtr getFolderIcon(BOOL is_open) const; bool mCallingCards; bool mWearables; + std::string mMessage; LLRootHandle mHandle; }; @@ -422,6 +440,7 @@ public: const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} virtual void openItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); }; class LLGestureBridge : public LLItemBridge @@ -468,7 +487,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); - virtual BOOL isItemWearable() const { return TRUE; } + virtual BOOL isItemWearable() const { return TRUE; } virtual std::string getLabelSuffix() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL renameItem(const std::string& new_name); @@ -502,7 +521,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); - virtual BOOL isItemWearable() const { return TRUE; } + virtual BOOL isItemWearable() const { return TRUE; } virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual std::string getLabelSuffix() const; virtual BOOL renameItem(const std::string& new_name); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index e3e88df18..38b83fcc5 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -33,6 +33,8 @@ #include "llfolderviewitem.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" +#include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" #include "llviewercontrol.h" #include "llfolderview.h" #include "llinventorybridge.h" @@ -153,6 +155,40 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { LLInventoryModelBackgroundFetch::instance().start(folder_id); } + + // Marketplace folder filtering + const U32 filterTypes = mFilterOps.mFilterTypes; + const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | FILTERTYPE_MARKETPLACE_UNASSOCIATED; + if (filterTypes & marketplace_filter) + { + S32 depth = depth_nesting_in_marketplace(folder_id); + if (depth > 0) + { + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + if (filterTypes & FILTERTYPE_MARKETPLACE_ACTIVE) + { + if (!LLMarketplaceData::instance().getActivationState(listing_uuid)) + { + return false; + } + } + else if (filterTypes & FILTERTYPE_MARKETPLACE_INACTIVE) + { + if (!LLMarketplaceData::instance().isListed(listing_uuid) || LLMarketplaceData::instance().getActivationState(listing_uuid)) + { + return false; + } + } + else if (filterTypes & FILTER_MARKETPLACE_UNASSOCIATED) + { + if (LLMarketplaceData::instance().isListed(listing_uuid)) + { + return false; + } + } + } + } + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) { // Can only filter categories for items in your inventory @@ -443,6 +479,21 @@ void LLInventoryFilter::setFilterEmptySystemFolders() mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; } +void LLInventoryFilter::setFilterMarketplaceActiveFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_ACTIVE; +} + +void LLInventoryFilter::setFilterMarketplaceInactiveFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_INACTIVE; +} + +void LLInventoryFilter::setFilterMarketplaceUnassociatedFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_UNASSOCIATED; +} + void LLInventoryFilter::setFilterUUID(const LLUUID& object_id) { if (mFilterOps.mFilterUUID == object_id) diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 6fd95ba0d..352d0fae7 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -58,7 +58,10 @@ public: FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it FILTERTYPE_DATE = 0x1 << 3, // search by date range FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type - FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5, // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_MARKETPLACE_ACTIVE = 0x1 << 6, // pass if folder is a marketplace active folder + FILTERTYPE_MARKETPLACE_INACTIVE = 0x1 << 7, // pass if folder is a marketplace inactive folder + FILTERTYPE_MARKETPLACE_UNASSOCIATED = 0x1 << 8 // pass if folder is a marketplace non associated (no market ID) folder }; enum EFilterLink @@ -73,7 +76,8 @@ public: SO_NAME = 0, // Sort inventory by name SO_DATE = 0x1, // Sort inventory by date SO_FOLDERS_BY_NAME = 0x1 << 1, // Force folder sort by name - SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top + SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2, // Force system folders to be on top + SO_FOLDERS_BY_WEIGHT = 0x1 << 3, // Force folder sort by weight, usually, amount of some elements in their descendents }; struct FilterOps @@ -111,6 +115,9 @@ struct FilterOps void setFilterUUID(const LLUUID &object_id); void setFilterWearableTypes(U64 types); void setFilterEmptySystemFolders(); + void setFilterMarketplaceActiveFolders(); + void setFilterMarketplaceInactiveFolders(); + void setFilterMarketplaceUnassociatedFolders(); void updateFilterTypes(U64 types, U64& current_types); void setFilterSubString(const std::string& string); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 203dd7ec8..ccc466bab 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -57,6 +57,7 @@ #include "llinventorypanel.h" #include "lllineeditor.h" #include "llmarketplacenotifications.h" +#include "llmarketplacefunctions.h" #include "llmenugl.h" #include "llnotificationsutil.h" #include "llpanelmaininventory.h" @@ -89,6 +90,44 @@ BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; +// Helper function : callback to update a folder after inventory action happened in the background +void update_folder_cb(const LLUUID& dest_folder) +{ + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + gInventory.updateCategory(dest_cat); + gInventory.notifyObservers(); +} + +// Helper function : Count only the copyable items, i.e. skip the stock items (which are no copy) +S32 count_copyable_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + +// Helper function : Count the number of stock folders +S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) +{ + S32 count = 0; + for (LLInventoryModel::cat_array_t::const_iterator it = categories.begin(); it != categories.end(); ++it) + { + LLInventoryCategory* cat = *it; + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + count++; + } + } + return count; +} + // Generates a string containing the path to the item specified by // item_id. void append_path(const LLUUID& id, std::string& path) @@ -110,6 +149,130 @@ void append_path(const LLUUID& id, std::string& path) path.append(temp); } +void update_marketplace_folder_hierarchy(const LLUUID cat_id) +{ + // When changing the marketplace status of a folder, the only thing that needs to happen is + // for all observers of the folder to, possibly, change the display label of the folder + // so that's the only thing we change on the update mask. + gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id); + + // Update all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + update_marketplace_folder_hierarchy(category->getUUID()); + } +} + +void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement) +{ + // When changing the marketplace status of an item, we usually have to change the status of all + // folders in the same listing. This is because the display of each folder is affected by the + // overall status of the whole listing. + // Consequently, the only way to correctly update an item anywhere in the marketplace is to + // update the whole listing from its listing root. + // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth + // is limited to 4. + // We also take care of degenerated cases so we don't update all folders in the inventory by mistake. + + if (cur_uuid.isNull()) + { + return; + } + + // Grab marketplace listing data for this item + S32 depth = depth_nesting_in_marketplace(cur_uuid); + if (depth > 0) + { + // Retrieve the listing uuid this object is in + LLUUID listing_uuid = nested_parent_id(cur_uuid, depth); + + // Verify marketplace data consistency for this listing + if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(listing_uuid)) + { + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + S32 version_depth = depth_nesting_in_marketplace(version_folder_uuid); + if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) + { + LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; + LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null); + } + } + + // Check if the count on hand needs to be updated on SLM + if (!LLMarketplaceData::instance().isUpdating(listing_uuid) && + (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) + { + LLMarketplaceData::instance().updateCountOnHand(listing_uuid); + } + // Update all descendents starting from the listing root + update_marketplace_folder_hierarchy(listing_uuid); + } + else if (depth == 0) + { + // If this is the marketplace listings root itself, update all descendents + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); + } + } + else + { + // If the folder is outside the marketplace listings root, clear its SLM data if needs be + if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(cur_uuid)) + { + LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; + LLMarketplaceData::instance().clearListing(cur_uuid); + } + // Update all descendents if this is a category + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); + } + } +} + +// Iterate through the marketplace and flag for label change all categories that countain a stock folder (i.e. stock folders and embedding folders up the hierarchy) +void update_all_marketplace_count(const LLUUID& cat_id) +{ + // Get all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array); + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Listing containing stock folders needs to be updated but not others + // Note: we take advantage of the fact that stock folder *do not* contain sub folders to avoid a recursive call here + update_marketplace_category(category->getUUID()); + } + else + { + // Explore the contained folders recursively + update_all_marketplace_count(category->getUUID()); + } + } +} + +void update_all_marketplace_count() +{ + // Get the marketplace root and launch the recursive exploration + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (!marketplace_listings_uuid.isNull()) + { + update_all_marketplace_count(marketplace_listings_uuid); + } +} + // Path should already end on '/' if it is not empty. void append_path_short(LLUUID const& id, std::string& path) { @@ -180,13 +343,14 @@ void copy_inventory_category(LLInventoryModel* model, for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) { LLInventoryItem* item = *iter; + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), new_cat_uuid, std::string(), - LLPointer(NULL)); + cb); } // Copy all the folders @@ -508,7 +672,9 @@ void open_outbox() LLFloaterOutbox::showInstance(); } -LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& destFolderId, S32 operation_id) +// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder +// Note: this is used locally in various situation where we need to wrap an item into a special folder +LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId) { llassert(item); llassert(destFolderId.notNull()); @@ -516,6 +682,7 @@ LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& des LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName()); gInventory.notifyObservers(); + // *TODO : Create different notifications for the various cases LLNotificationsUtil::add("OutboxFolderCreated"); return created_folder_id; @@ -531,8 +698,7 @@ void move_to_outbox_cb_action(const LLSD& payload) // when moving item directly into outbox create folder with that name if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) { - S32 operation_id = payload["operation_id"].asInteger(); - dest_folder_id = create_folder_in_outbox_for_item(viitem, dest_folder_id, operation_id); + dest_folder_id = create_folder_for_item(viitem, dest_folder_id); } LLUUID parent = viitem->getParentUUID(); @@ -603,7 +769,7 @@ void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LL // when moving item directly into outbox create folder with that name if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) { - dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); + dest_folder = create_folder_for_item(inv_item, dest_folder); } copy_inventory_item(gAgent.getID(), @@ -633,7 +799,7 @@ void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 // when moving item directly into outbox create folder with that name if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) { - dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); + dest_folder = create_folder_for_item(inv_item, dest_folder); } LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; @@ -673,6 +839,902 @@ void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_fold open_outbox(); } +///---------------------------------------------------------------------------- +// Marketplace functions +// +// Handles Copy and Move to or within the Marketplace listings folder. +// Handles creation of stock folders, nesting of listings and version folders, +// permission checking and listings validation. +///---------------------------------------------------------------------------- + +S32 depth_nesting_in_marketplace(LLUUID cur_uuid) +{ + // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplace_listings_uuid.isNull()) + { + return -1; + } + // If not a descendent of the marketplace listings root, then the nesting depth is -1 by definition + if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) + { + return -1; + } + + // Iterate through the parents till we hit the marketplace listings root + // Note that the marketplace listings root itself will return 0 + S32 depth = 0; + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (cur_uuid != marketplace_listings_uuid) + { + depth++; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return depth; +} + +// Returns the UUID of the marketplace listing this object is in +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) +{ + if (depth < 1) + { + // For objects outside the marketplace listings root (or root itself), we return a NULL UUID + return LLUUID::null; + } + else if (depth == 1) + { + // Just under the root, we return the passed UUID itself if it's a folder, NULL otherwise (not a listing) + LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid); + return (cat ? cur_uuid : LLUUID::null); + } + + // depth > 1 + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (depth > 1) + { + depth--; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return cur_uuid; +} + +S32 compute_stock_count(LLUUID cat_uuid) +{ + // Handle the case of the folder being a stock folder immediately + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); + if (!cat) + { + // Not a category so no stock count to speak of + return -1; + } + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Note: stock folders are *not* supposed to have nested subfolder so we stop recursion here but we count only items (subfolders will be ignored) + // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + return item_array->size(); + } + + // Grab marketplace data for this folder + S32 depth = depth_nesting_in_marketplace(cat_uuid); + LLUUID listing_uuid = nested_parent_id(cat_uuid, depth); + if (!LLMarketplaceData::instance().isListed(listing_uuid)) + { + // If not listed, the notion of stock is meaningless so it won't be computed for any level + return -1; + } + + LLUUID version_folder_uuid = LLMarketplaceData::instace().getVersionFolder(listing_uuid); + // Handle the case of the first 2 levels : listing and version folders + if (depth == 1) + { + if (version_folder_uuid.notNull()) + { + // If there is a version folder, the stock value for the listing is the version folder stock + return compute_stock_count(version_folder_uuid); + } + else + { + // If there's no version folder associated, the notion of stock count has no meaning + return -1; + } + } + else if (depth == 2) + { + if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid)) + { + // If there is a version folder but we're not it, our stock count is meaningless + return -1; + } + } + + // In all other cases, the stock count is the min of stock folders count found in the descendents + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + + // "-1" denotes a folder that doesn't contain any stock folders in its descendents + S32 curr_count = -1; + + // Note: marketplace listings have a maximum depth nesting of 4 + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + S32 count = compute_stock_count(category->getUUID()); + if ((curr_count == -1) || ((count != -1) && (count < curr_count))) + { + curr_count = count; + } + } + + return curr_count; +} + +// local helper +bool can_move_to_marketplace(LLInventoryItem* inv_item, std::string& tooltip_msg, bool resolve_links) +{ + // Collapse links directly to items/folders + LLViewerInventoryItem* viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryItem* linked_item = viewer_inv_item->getLinkedItem(); + LLViewerInventoryCategory* linked_category = viewer_inv_item->getLinkedCategory(); + + // Linked items and folders cannot be put for sale + if (linked_category || linked_item) + { + tooltip_msg = LLTrans::getString("TooltipOutboxLinked"); + return false; + } + + // A category is always considered as passing... + if (linked_category != NULL) + { + return true; + } + + // Take the linked item if necessary + if (linked_item != NULL) + { + inv_item = linked_item; + } + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + if (!allow_transfer) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); + return false; + } + + // Check library status: library items cannot be put on the marketplace + if (!gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.getRootFolderID())) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + return false; + } + + // Check type: for the moment, calling cards cannot be put on the marketplace + bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); + if (calling_card) + { + tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); + return false; + } + + return true; +} + +// local helper +// Returns the max tree length (in folder nodes) down from the argument folder +int get_folder_levels(LLInventoryCategory* inv_cat) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + int max_child_levels = 0; + for (S32 i = 0; i < cats->size(); ++i) + { + LLInventoryCategory* category = cats->at(i); + max_child_levels = llmax(max_child_levels, get_folder_levels(category)); + } + + return 1 + max_child_levels; +} + +// local helper +// Returns the distance (in folder nodes) between the ancestor and its descendant. Returns -1 if not related. +int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) +{ + int depth = 0; + if (ancestor_id == descendant_id) return depth; + + const LLInventoryCategory* category = gInventory.getCategory(descendant_id); + while (category) + { + LLUUID parent_id = category->getParentUUID(); + if (parent_id.isNull()) break; + + depth++; + if (parent_id == ancestor_id) return depth; + + category = gInventory.getCategory(parent_id); + } + + LL_WARNS("SLM") << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; + return -1; +} + +// local helper +// Returns true if all items within the argument folder are fit for sale, false otherwise +bool has_correct_permissions_for_sale(LLInventoryCategory* cat, std::string& error_msg) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + LLInventoryModel::item_array_t item_array_copy = *item_array; + + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + if (!can_move_to_marketplace(item, error_msg, false)) + { + return false; + } + } + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + if (!has_correct_permissions_for_sale(category, error_msg)) + { + return false; + } + } + return true; +} + +// Returns true if inv_item can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size, bool from_paste) +{ + // Check stock folder type matches item type in marketplace listings or merchant outbox (even if of no use there for the moment) + LLViewerInventoryCategory* view_folder = dynamic_cast(dest_folder); + bool move_in_stock = (view_folder && (view_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)); + bool accept = (view_folder && view_folder->acceptItem(inv_item)); + if (!accept) + { + tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock"); + } + + // Check that the item has the right type and permissions to be sold on the marketplace + if (accept) + { + accept = can_move_to_marketplace(inv_item, tooltip_msg, true); + } + + // Check that the total amount of items won't violate the max limit on the marketplace + if (accept) + { + // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) + int existing_item_count = (move_in_stock ? 0 : bundle_size); + + // Get the version folder: that's where the counts start from + const LLViewerInventoryCategory* version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_item->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + existing_item_count = 0; + } + + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + } + + if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + } + + return accept; +} + +// Returns true if inv_cat can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size, bool check_items, bool from_paste) +{ + bool accept = true; + + // Compute the nested folders level we'll add into with that incoming folder + int incoming_folder_depth = get_folder_levels(inv_cat); + // Compute the nested folders level we're inserting ourselves in + // Note: add 1 when inserting under a listing folder as we need to take the root listing folder in the count + int insertion_point_folder_depth = (root_folder ? get_folder_path_length(root_folder->getUUID(), dest_folder->getUUID()) + 1 : 0); + + // Get the version folder: that's where the folders and items counts start from + const LLViewerInventoryCategory* version_folder = (insertion_point_folder_depth >= 2 ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + // Compare the whole with the nested folders depth limit + // Note: substract 2 as we leave root and version folder out of the count threshold + if ((incoming_folder_depth + insertion_point_folder_depth - 2) > (S32)(gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"))) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args); + accept = false; + } + + if (accept) + { + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, FALSE); + + int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... + int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); + int existing_item_count = 0; + int existing_folder-count = 0; + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_cat->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + dragged_folder_count = 0; + dragged_item_count = 0; + } + + // Tally the total number of categories and items inside the root folder + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + + existing_folder_count += existing_categories.size(); + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + } + + const int total_folder_count = existing_folder_count + dragged_folder_count; + const int total_item_count = existing_item_count + dragged_item_count; + + if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } + else if (total_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + + // Now check that each item in the folder can be moved in the marketplace + if (accept && check_items) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (!can_move_to_marketplace(item, tooltip_msg, false)) + { + accept = false; + break; + } + } + } + } + + return accept; +} + +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) +{ + // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace + S32 depth = depth_nesting_in_marketplace(dest_folder); + if (depth < 0) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Merchant"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // We will collapse links into items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + + if (linked_category != NULL) + { + // Move the linked folder directly + return move_folder_to_marketplacelistings(linked_category, dest_folder, copy); + } + else + { + // Grab the linked item if any + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + std::string error_msg; + if (can_move_to_marketplace(inv_item, error_msg, true)) + { + // When moving an isolated item, we might need to create the folder structure to support it + if (depth == 0) + { + // We need a listing folder + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); + depth++; + } + if (depth == 1) + { + // We need a version folder + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); + depth++; + } + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && + (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // We need a stock folder + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); + dest_cat = gInventory.getCategory(dest_folder); + depth++; + } + + // Verify we can have this item in that destination category + if (!dest_cat->acceptItem(viewer_inv_item)) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // Get the parent folder of the moved item : we may have to update it + LLUUID src_folder = viewer_inv_item->getParentUUID(); + + if (copy) + { + // Copy the item + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); + copy_inventory_item( + gAgent.getID(), + LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); + viewer_inv_item->getUUID(), + dest_folder, + std::string(), + cb); + } + else + { + // Reparent the item + gInventory.changeItemParent(viewer_inv_item, dest_folder, true); + } + + // Update the modified folders + update_marketplace_category(src_folder); + update_marketplace_category(dest_folder); + gInventory.notifyObservers(); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + } + return true; +} + +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy) +{ + // Check that we have adequate permission on all items being moved. Proceed if we do. + std::string error_msg; + if (has_correct_permissions_for_sale(inv_cat, error_msg)) + { + // Get the destination folder + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + + // Check it's not a stock folder + if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // Get the parent folder of the moved item : we may have to update it + LLUUID src_folder = inv_cat->getParentUUID(); + + LLViewerInventoryCategory* viewer_inv_cat = (LLViewerInventoryCategory*) inv_cat; + if (copy) + { + // Copy the folder + copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder); + } + else + { + // Reparent the folder + gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); + } + + // Check the destination folder recursively for no copy items and promote the including folders if any + validate_marketplacelistings(dest_cat); + + // Update the modified folders + update_marketplace_category(src_folder); + update_marketplace_category(dest_folder); + gInventory.notifyObservers(); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + return true; +} + +// Make all relevant business logic checks on the marketplace listings starting with the folder as argument. +// This function does no deletion of listings but a mere audit and raises issues to the user (through the +// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. +// The only inventory changes that are done is to move ad sort folders containing no-copy items to stock folders. +bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) +{ + return cat1->getName().compare(cat2->getName()) < 0; +} + +bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy) +{ + // Folder is valid unless an issues is raised + bool result = true; + + // Get the type and the depth of the folder + LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (cat); + const LLFolderType::EType folder_type = cat->getPreferredType(); + S32 depth = depth_nesting_in_marketplace(cat->getUUID()); + if (depth < 0) + { + // If the folder is not under the marketplace listings root, validation should not be applied + return result; + } + + // Set the indentation for print output (typically, audit button in marketplace folder floater) + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + + // Check out that version folders are marketplace ready + if (depth == 2) + { + std::string message; + // Note: if we fix the hierarchy, we want to check the items individually, hence the last argument here + if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) + { + result = false; + if (cb) + { + message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; + cb(message,LLError::LEVEL_ERROR); + } + } + } + + // Check out that stock folders are at the right level + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2)) + { + if (fix_hierarchy) + { + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb(message, LLError::LEVEL_WARN); + } + // Nest the stock folder one level deeper in a normal folder ad restart from there + LLUUID parent_uuid = cat->getParentUUID(); + LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); + LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); + gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); + result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy); + return result; + } + else + { + result = false; + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb(message, LLError::LEVEL_ERROR); + } + } + } + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + // Stock items : sorting and moving the various stock items is complicated as the set of constraints is high + // For each folder, we need to: + // * separate non stock items, stock items per types in different folders + // * have stock items nested at depth 2 at least + // * never ever move the non-stock items + + std::vector > items_vector; + items_vector.resize(LLInventoryType::IT_COUNT+1); + + // Parse the items and create vectors of items to sort copyable items and stock items of various types + bool has_bad_items = false; + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + + // Test but skip items that shouldn't be there to start with, raise an error message for those + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + has_bad_items = true; + if (cb && fix_hierarchy) + { + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb(message, LLError::LEVEL_ERROR); + } + continue; + } + // Update the appropriate vector item for that type + LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // Get the item type for stock items + type = viewer_inv_item->getInventoryType(); + } + items_vector[type].push_back(viewer_inv_item); + } + + // How many types of folders? Which type is it if only one? + S32 count = 0; + LLInventoryType::EType type = LLInventoryType::IT_COUNT; + for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++) + { + if (!items_vector[i].empty()) + { + count++; + type = (LLInventoryType::EType)(i); + } + } + + // If we have no items in there (only folders or empty), analyze a bit further + if ((count == 0) && !has_bad_items) + { + if (cat_array->size() == 0) + { + if (depth == 2) + { + // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); + cb(message, LLError::LEVEL_WARN); + } + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) + { + // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); + cb(message, LLError::LEVEL_WARN); + } + } + else if (cb) + { + // We warn if there's nothing in a regular folder (maybe it's an under construction listing) + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); + cb(message, LLError::LEVEL_WARN); + } + } + else + { + // Done with that folder : PRint out the folder name unless we alreaady found an error here + if (cb && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, LLError::LEVEL_INFO); + } + } + } + // If we have a single type of items of the right type in the right place, we're done + else if ((count == 1) && !has_bad_items && (((type == LLInventoryType::IT_COUNT) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) + { + // Done with that folder : PRint out the folder name unless we alreaady found an error here + if (cb && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, LLError::LEVEL_INFO); + } + } + else + { + if (fix_hierarchy && !has_bad_items) + { + // Create one folder per vector at the right depth and of the right type + for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++) + { + if (!items_vectory[i].empty()) + { + // Create a new folder + LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); + std::string folder_name = (depth > 1 ? viewer_cat->getName() : viewer_inv_item->getName()); + LLFolderType::EType new_folder_type = (i == LLInventoryType::IT_COUNT ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); + if (cb) + { + std::string message = ""; + if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Stock"); + } + else + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); + } + cb(message, LLError::LEVEL_WARN); + } + LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); + // Move each item to the new folder + while (!items_vector[i].empty()) + { + LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); + if (cb) + { + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); + cb(message, LLError::LEVEL_WARN); + } + gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); + items_vector[i].pop_back(); + } + update_marketplace_category(folder_uuid); + gInventory.notifyObservers(); + } + } + // Stock folder should have no sub folder so reparent those up + if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLUUID parent_uuid = cat->getParentUUID(); + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (*iter); + gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); + // Note : those reparented folders will be recursively visited and validated at the end of this function + } + } + } + else if (cb) + { + // We are not fixing the hierarchy but reporting problems, report everything we can find + // Print the folder name + if (result && (depth >= 1)) + { + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (count >= 2)) + { + // Report if a stock folder contains a mix of items + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); + cb(message, LLError::LEVEL_ERROR); + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) + { + // Report if a stock folder contains subfolders + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); + cb(message, LLError::LEVEL_ERROR); + } + else + { + // Simply print the folder name + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, LLError::LEVEL_INFO); + + } + } + // Scan each item and report if there's a problem + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem* viewer_inv_item = (LLViewerInventoryItem*) item; + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + // Report items that shouldn't be there to start with + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb(message, LLError::LEVEL_ERROR); + } + else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // Report stock items that are misplaced + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); + cb(message, LLError::LEVEL_ERROR); + } + else if (depth == 1) + { + // Report items not wrapped in version folder + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); + cb(message, LLError::LEVEL_WARN); + } + } + } + + // Clean up + if (viewer_cat->getDescendentCount() == 0) + { + // Remove the current folder if it ends up empty + if (cb) + { + std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb(message, LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + gInventory.notifyObservers(); + return result && !has_bad_items; + } + else + { + // Update the current folder + update_marketplace_category(cat->getUUID()); + gInventory.notifyObservers(); + } + } + + // Recursion : Perform the same validation on each nested folder + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + // Sort the folders in alphabetical order first + std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); + + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + result &= validate_marketplacelistings(category, cb, fix_hierarchy); + } + + return result && !has_bad_items; +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index e986eabda..230980960 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -55,6 +55,11 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) void show_item_profile(const LLUUID& item_uuid); +// Nudge the listing categories in the inventory to signal that their marketplace status changed +void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_enforcement = true); +// Nudge all listing categories to signal that their marketplace status changed +void update_all_marketplace_count(); + void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null); @@ -67,9 +72,19 @@ void append_path_short(const LLUUID& id, std::string& path); void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id); void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id); - void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id); +typedef boost::function validation_callback_t; + +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false); +bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true); +S32 depth_nesting_in_marketplace(LLUUID cur_uuid); +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); +S32 computer_stock_count(LLUUID cat_uuid); + /** Miscellaneous global functions ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 25bd0321e..b8b3d6c8d 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1132,7 +1132,8 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 LLPointer old_cat = getCategory(cat->getUUID()); if(old_cat) { - // We already have an old category, modify it's values + // We already have an old category, modify its values + U32 mask = LLInventoryObserver::NONE; LLUUID old_parent_id = old_cat->getParentUUID(); LLUUID new_parent_id = cat->getParentUUID(); if(old_parent_id != new_parent_id) @@ -1156,6 +1157,12 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 { mask |= LLInventoryObserver::LABEL; } + // Under marketplace, category labels are quite complex and need extra upate + const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id)) + { + mask |= LLInventoryObeserver::LABEL; + } old_cat->copyViewerCategory(cat); addChangedMask(mask, cat->getUUID()); } @@ -1509,6 +1516,11 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo LLPointer cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); vector_replace_with_last(*cat_list, cat); } + + // Note : We need to tell the inventory observers that those things are going to be deleted *before* the tree is cleared or they won't know what to delete (in views and view models) + addChangedMask(LLInventoryObserver::REMOVE, id); + gInventory.notifyObservers(); + item_list = getUnlockedItemArray(id); if(item_list) { @@ -1654,9 +1666,10 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) } mModifyMask |= mask; - if (referent.notNull()) + if (referent.notNull() && (mChangedItemIDs.find(referent) == mChangedItemIDs.end())) { mChangedItemIDs.insert(referent); + update_marketplace_category(referent, false); if (mask & LLInventoryObserver::ADD) { diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 9e3e0af73..d6ccd56e2 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -155,6 +155,18 @@ LLInventoryPanel::LLInventoryPanel(const std::string& name, setBackgroundOpaque(TRUE); } +LLUUID getStartFolder(const std::string& start_folder) +{ + if ("LIBRARY" == start_folder) + return gInventory.getLibraryRootFolderID(); + const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(start_folder); + + return (preferred_type != LLFolderType::FT_NONE) + ? gInventory.findCategoryUUIDForType(preferred_type, false, false) + : gInventory.getCategory(static_cast(start_folder)) ? static_cast(mStartFolder) // Singu Note: if start folder is an id of a folder, use it + : LLUUID::null; +} + void LLInventoryPanel::buildFolderView() { // Determine the root folder in case specified, and @@ -162,21 +174,7 @@ void LLInventoryPanel::buildFolderView() //std::string start_folder_name(params.start_folder()); - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolder); - - LLUUID root_id; - - if ("LIBRARY" == mStartFolder) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE) - ? gInventory.findCategoryUUIDForType(preferred_type, false) - : gInventory.getCategory(static_cast(mStartFolder)) ? static_cast(mStartFolder) // Singu Note: if start folder is an id of a folder, use it - : LLUUID::null; - } + LLUUID root_id = getStartFolder(mStartFolder); if ((root_id == LLUUID::null) && !mStartFolder.empty()) { @@ -235,7 +233,26 @@ BOOL LLInventoryPanel::postBuild() { setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); } - getFilter()->setFilterEmptySystemFolders(); + + // hide inbox + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) + { + //getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); // Singu Nope! + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); + } + // hide marketplace listing box, unless we want to show it (note: hacky as show_root_folder is only used for marketplace...) + // Singu Note: Even more hacky! String compare for merchant StartFolder to create the same effect /*&& !mParams.show_root_folder*/ + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && mStartFolder != "merchant") + { + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); + } + + // set the filter for the empty folder if the debug setting is on + if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) + { + getFilter()->setFilterEmptySystemFolders(); + } + return LLPanel::postBuild(); } @@ -671,7 +688,21 @@ void LLInventoryPanel::onIdle(void *userdata) const LLUUID& LLInventoryPanel::getRootFolderID() const { - return mFolderRoot.get()->getListener()->getUUID(); + LLUUID root_id; + if (mFolderRoot.get() && mFolderRoot.get()->getListener()) + { + root_id = mFolderRoot.get()->getListener()->getUUID(); + } + else + { + root_id = getStartFolder(mStartFolder); + if (root_id.isNull()) + { + llwarns << "Could not find folder of type " << preferred_type << LL_ENDL; + root_id.generateNewID(); + } + } + return root_id; } void LLInventoryPanel::initializeViews() @@ -732,7 +763,7 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u return ret; } -LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) +LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge* bridge) { return new LLFolderViewFolder( bridge->getDisplayName(), @@ -758,71 +789,98 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { LLInventoryObject const* objectp = gInventory.getObject(id); - LLUUID root_id = mFolderRoot.get()->getListener()->getUUID(); - LLFolderViewFolder* parent_folder = NULL; - LLFolderViewItem* itemp = NULL; + + if (!objectp) + { + return NULL; + } + + LLFolderViewItem* folder_view_item = mFolderRoot.get()->getItemByID(id); + + const LLUUID& parent_id = objectp->getParentUUID(); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)mFolderRoot.get()->getItemByID(parent_id); + + // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") + bool allow_drop = true; + bool allow_wear = true; // Singu TODO: Remove this when we have params here. + bool create_root = false; + if (mStartFolder == "merchant") // Singu Note: Only done for "merchant" + { + LLUUID root_id = getRootFolderID(); + if (root_id == id) + { + // We insert an extra level that's seen by the UI but has no influence on the model + parent_folder = dynamic_cast(folder_view_item); + folder_view_item = NULL; + allow_drop = false; // merchant floaters upstream say false in xui + allow_wear = false; // merchant floaters upstream say false in xui + create_root = true; + } + } + else if (mStartFolder == "outbox") + { + allow_wear = false; // outbox floater upstream says false in xui + } + + if (!folder_view_item && parent_folder) + { + if (objectp->getType() <= LLAssetType::AT_NONE || + objectp->getType() >= LLAssetType::AT_COUNT) + { + llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << llendl; + return NULL; + } - if (id == root_id) - { - parent_folder = mFolderRoot.get(); - } - else if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - parent_folder = (LLFolderViewFolder*)mFolderRoot.get()->getItemByID(parent_id); - - if (parent_folder) - { - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { - LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " - << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() - << LL_ENDL; - return NULL; - } - - if ((objectp->getType() == LLAssetType::AT_CATEGORY) && - (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), + objectp->getType(), + LLInventoryType::IT_CATEGORY, + this, + mFolderRoot.get(), + objectp->getUUID()); + if (new_listener) { - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - mFolderRoot.get(), - objectp->getUUID()); - if (new_listener) - { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot.get()->getSortOrder()); - } - itemp = folderp; - } - } - else - { - // Build new view for item. - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - mFolderRoot.get(), - item->getUUID(), - item->getFlags()); - - if (new_listener) - { - itemp = createFolderViewItem(new_listener); - } - } - - if (itemp) - { - itemp->addToFolder(parent_folder, mFolderRoot.get()); - } + LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); + if (folderp) + { + folderp->setItemSortOrder(mFolderRoot.get()->getSortOrder()); + folderp->setAllowDrop(allow_drop); + folderp->setAllowWear(allow_wear); + } + folder_view_item = folderp; + } + } + else + { + // Build new view for item. + LLInventoryItem* item = (LLInventoryItem*)objectp; + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), + item->getActualType(), + item->getInventoryType(), + this, + mFolderRoot.get(), + item->getUUID(), + item->getFlags()); + + if (new_listener) + { + folder_view_item = createFolderViewItem(new_listener); + } + } + + if (folder_view_item) + { + llassert(parent_folder != NULL); + folder_view_item->addToFolder(parent_folder, mFolderRoot.get()); + // In the case of the root folder been shown, open that folder by default once the widget is created + if (create_root) + { + folder_view_item->setOpen(TRUE); + } } } @@ -860,7 +918,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) mInventory->unlockDirectDescendentArrays(id); } - return itemp; + return folder_view_item; } // bit of a hack to make sure the inventory is open. @@ -965,7 +1023,7 @@ BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // If folder view is empty the (x, y) point won't be in its rect // so the handler must be called explicitly. // but only if was not handled before. See EXT-6746. - if (!handled && !mFolderRoot.get()->hasVisibleChildren()) + if (!handled && /*mParams.allow_drop_on_root*/mStartFolder != "merchant" && !mFolderRoot.get()->hasVisibleChildren()) { handled = mFolderRoot.get()->handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index b0819d867..63893c4f3 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -128,6 +128,7 @@ public: void modelChanged(U32 mask); LLFolderView* getRootFolder() { return mFolderRoot.get(); } LLScrollContainer* getScrollableContainer() { return mScroller; } + bool getAllowDropOnRoot() const { return mStartFolder != "merchant"; } void onSelectionChange(const std::deque &items, BOOL user_action); diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index d8c921b9b..93dd2fbe4 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -29,14 +29,23 @@ #include "llmarketplacefunctions.h" #include "llagent.h" +#include "llbufferstream.h" #include "llhttpclient.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llnotificationsutil.h" #include "lltimer.h" #include "lltrans.h" #include "llviewercontrol.h" +#include "llviewerinventory.h" #include "llviewermedia.h" #include "llviewernetwork.h" +#include "llviewerregion.h" #include "hippogridmanager.h" +#include "reader.h" // JSON +#include "writer.h" // JSON + // // Helpers // @@ -120,6 +129,535 @@ LLSD getMarketplaceStringSubstitutions() return marketplace_sub_map; } +/////////////////////////////////////////////////////////////////////////////// +// SLM Responders +void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string description) +{ + LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << description << LL_ENDL; + // Prompt the user with the warning (so they know why things are failing) + LLSD subs; + subs["[ERROR_REASON]"] = reason; + // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. + subs["[ERROR_DESCRIPTION]"] = (description.length() <= 512 ? description : ""); + LLNotificationsUtil::add("MerchantTransactionFailed", subs); +} +void log_SLM_infos(const std::string& request, U32 status, const std::string& body) +{ + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL; + } +} + +// Merov: This is a temporary hack used by dev while secondlife-staging is down... +// *TODO : Suppress that before shipping! +static bool sBypassMerchant = false; + +class LLSLMGetMerchantResponder : public LLHTTPClient::ResponderWithResult +{ + LOG_CLASS(LLSLMGetMerchantResponder); +public: + + LLSLMGetMerchantResponder() {} + +protected: + virtual void httpFailure() + { + if (sBypassMerchant) + { + // *TODO : Suppress that before shipping! + log_SLM_infos("Get /merchant", getStatus(), "SLM Connection error bypassed (debug only)"); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); + } + else if (HTTP_NOT_FOUND == getStatus()) + { + log_SLM_infos("Get /merchant", getStatus(), "User is not a merchant"); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); + } + else if (HTTP_SERVICE_UNAVAILABLE == getStatus()) + { + log_SLM_infos("Get /merchant", getStatus(), "Merchant is not migrated"); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); + } + else + { + log_SLM_warning("Get /merchant", getStatus(), getReason(), getContent().get("error_code"), getContent().get("error_description")); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + } + } + + virtual void httpSuccess() + { + log_SLM_infos("Get /merchant", getStatus(), "User is a merchant"); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); + } + + virtual char const* getName() const { return "LLSLMGetMerchantResponder"; } +}; + +class LLSLMGetListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMGetListingsResponder); +public: + + LLSLMGetListingsResponder(const LLUUID& folder_id) + { + mExpectedFolderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std:string body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Get /listings", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Get /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listings", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + bool is_listed = listing["is_listed"].asBool(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInt(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + if (folder_id.notNull()) + { + LLMarketplaceData::instance().deleteListing(folder_id, false); + LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); + LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); + LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + it++; + } + + // Update all folders under the root + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + } + + virtual char const* getName() const { return "LLSLMGetListingsResponder"; } +private: + LLUUID mExpectedFolderId; +}; + +class LLSLMCreateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMCreateListingsResponder); +public: + + LLSLMCreateListingsResponder(const LLUUID& folder_id) + { + mExpectedFolderId = folder_id + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std::String body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Post /listings", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Post /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Post /listings", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + bool is_listed = listing["is_listed"].asBool(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInt(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); + LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); + LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + it++; + } + } + + virtual char const* getName() const { return "LLSLMCreateListingsResponder"; } +private: + LLUUID mExpectedFolderId; +}; + +class LLSLMGetListingResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMGetListingResponder); +public: + + LLSLMGetListingResponder(const LLUUID& folder_id) + { + mExpectedFolderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std:string body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Get /listing", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Get /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listing", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + bool is_listed = listing["is_listed"].asBool(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInt(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + + // Update that listing + LLMarketplaceData::instance().setListingID(folder_id, listing_id, false); + LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id, false); + LLMarketplaceData::instance().setActivationState(folder_id, is_listed, false); + LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); + LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + + it++; + } + } + + virtual char const* getName() const { return "LLSLMGetListingResponder"; } +private: + LLUUID mExpectedFolderId; +}; + +class LLSLMUpdateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMUpdateListingsResponder); +public: + + LLSLMUpdateListingsResponder(const LLUUID& folder_id, bool expected_listed_state, const LLUUID& expected_version_id) + { + mExpectedFolderId = folder_id; + mExpectedListedState = expected_listed_state; + mExpectedVersionUUID = expected_version_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std:string body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Put /listing", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Put /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /listing", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + bool is_listed = listing["is_listed"].asBool(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInt(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + + // Update that listing + LLMarketplaceData::instance().setListingID(folder_id, listing_id, false); + LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id, false); + LLMarketplaceData::instance().setActivationState(folder_id, is_listed, false); + LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); + LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + + // Show a notification alert if what we got is not what we expected + // (this actually doesn't result in an error status from the SLM API protocol) + if ((mExpectedListedState != is_listed) || (mExpectedVersionUUID != version_id)) + { + LLSD subs; + subs["[URL]"] = edit_url; + LLNotificationsUtil::add("AlertMerchantListingNotUpdated", subs); + } + + it++; + } + } + + virtual char const* getName() const { return "LLSLMUpdateListingsResponder"; } +private: + LLUUID mExpectedFolderId; + bool mExpectedListedState; + LLUUID mExpectedVersionUUID; +}; + +class LLSLMAssociateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMAssociateListingsResponder); +public: + + LLSLMAssociateListingsResponder(const LLUUID& folder_id, const LLUUID& source_folder_id) + { + mExpectedFolderId = folder_id; + mSourceFolderId = source_folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + LLMarketplaceData::instance().setUpdating(mSourceFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std:string body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Put /associate_inventory", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + update_marketplace_category(mSourceFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Put /associate_inventory", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + update_marketplace_category(mSourceFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /associate_inventory", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + bool is_listed = listing["is_listed"].asBool(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInt(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + + // Check that the listing ID is not already associated to some other record + LLUUID old_listing = LLMarketplaceData::instance().getListingFolder(listing_id); + if (old_listing.notNull()) + { + // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID) + LLMarketplaceData::instance().deleteListing(old_listing); + } + + // Add the new association + LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); + LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); + LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + + // Alert with DAMA informing the user that a version folder must be designated + LLNotificationsUtil::add("AlertMerchantAssociateNeedsVersion"); + it++; + } + + // Always update the source folder so its widget updates + update_marketplace_category(mSourceFolderId, false); + } + + virtual char const* getName() const { return "LLSLMAssociateListingsResponder"; } +private: + LLUUID mExpectedFolderId; // This is the folder now associated with the id. + LLUUID mSourceFolderId; // This is the folder initially associated with the id. Can be LLUUID::null +}; + +class LLSLMDeleteListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMDeleteListingsResponder); +public: + + LLSLMDeleteListingsResponder(const LLUUID& folder_id) + { + mExpectedFolderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(mExpectedFolderId, false); + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std:string body = strstrm.str(); + + if (!isGoodStatus()) + { + log_SLM_warning("Delete /listing", getStatus(), getReason(), "", body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + log_SLM_warning("Delete /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + update_marketplace_category(mExpectedFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Delete /listing", getStatus(), body); + + // Extract the info from the Json string + Json::ValueIterator it = root["listings"].begin(); + + while (it != root["listings"].end()) + { + Json::Value listing = *it; + + int listing_id = listing["id"].asInt(); + LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); + LLMarketplaceData::instance().deleteListing(folder_id); + + it++; + } + } + virtual char const* getName() const { return "LLSLMDeleteListingsResponder"; } +private: + LLUUID mExpectedFolderId; +}; + +// SLM Responders End +/////////////////////////////////////////////////////////////////////////////// namespace LLMarketplaceImport { @@ -509,45 +1047,44 @@ void LLMarketplaceInventoryImporter::updateImport() // If we are no longer in progress if (!mImportInProgress) { + // Look for results success + mInitialized = LLMarketplaceImport::hasSessionCookie(); + + // Report results + if (mStatusReportSignal) + { + (*mStatusReportSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + } + if (mInitialized) { - // Report results - if (mStatusReportSignal) + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT; + // Follow up with auto trigger of import + if (mAutoTriggerImport) { - (*mStatusReportSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + mAutoTriggerImport = false; + mImportInProgress = triggerImport(); } } else { - // Look for results success - mInitialized = LLMarketplaceImport::hasSessionCookie(); - - if (mInitialized) + U32 status = LLMarketplaceImport::getResultStatus(); + if ((status == MarketplaceErrorCodes::IMPORT_FORBIDDEN) || + (status == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR)) { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT; - // Follow up with auto trigger of import - if (mAutoTriggerImport) - { - mAutoTriggerImport = false; - mImportInProgress = triggerImport(); - } + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT; + } + else if (status == MarketplaceErrorCodes::IMPORT_SERVER_API_DISABLED) + { + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT; } else { - U32 status = LLMarketplaceImport::getResultStatus(); - if ((status == MarketplaceErrorCodes::IMPORT_FORBIDDEN) || - (status == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR)) - { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT; - } - else - { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE; - } - if (mErrorInitSignal && (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)) - { - (*mErrorInitSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); - } + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE; + } + if (mErrorInitSignal && (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)) + { + (*mErrorInitSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); } } } @@ -560,3 +1097,731 @@ void LLMarketplaceInventoryImporter::updateImport() } } +// +// Direct Delivery : Marketplace tuples and data +// +class LLMarketplaceInventoryObserver : public LLInventoryObserver +{ +public: + LLMarketplaceInventoryObserver() {} + virtual ~LLMarketplaceInventoryObserver() {} + virtual void changed(U32 mask); +}; + +void LLMarketplaceInventoryObserver::changed(U32 mask) +{ + // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder: + // * stock counts changing : no copy items coming in and out will change the stock count on folders + // * version and listing folders : moving those might invalidate the marketplace data itself + // Since we should cannot raise inventory change while the observer is called (the list will be cleared + // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied. + + // That's the only changes that really do make sense for marketplace to worry about + if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) + { + const std::set& changed_items = gInventory.getChangedIDs(); + + std::set::const_iterator id_it = changed_items.begin(); + std::set::const_iterator id_end = changed_items.end(); + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj) + { + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + // If it's a folder known to the marketplace, let's check it's in proper shape + if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) + { + LLInventoryCategory* cat = (LLInventoryCategory*)(obj); + validate_marketplacelistings(cat); + } + } + else + { + // If it's not a category, it's an item... + LLInventoryItem* item = (LLInventoryItem*)(obj); + // If it's a no copy item, we may need to update the label count of marketplace listing + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + LLMarketplaceData::instance().setDirtyCount(); + } + } + } + } + } +} + +// Tuple == Item +LLMarketplaceTuple::LLMarketplaceTuple() : + mListingFolderId(), + mListingId(0), + mVersionFolderId(), + mIsActive(false), + mEditURL("") +{ +} + +LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id) : + mListingFolderId(folder_id), + mListingId(0), + mVersionFolderId(), + mIsActive(false), + mEditURL("") +{ +} + +LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed) : + mListingFolderId(folder_id), + mListingId(listing_id), + mVersionFolderId(version_id), + mIsActive(is_listed), + mEditURL("") +{ +} + + +// Data map +LLMarketplaceData::LLMarketplaceData() : + mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED), + mStatusUpdatedSignal(NULL), + mDirtyCount(false) +{ + mInventoryObserver = new LLMarketplaceInventoryObserver; + gInventory.addObserver(mInventoryObserver); +} + +LLMarketplaceData::~LLMarketplaceData() +{ + gInventory.removeObserver(mInventoryObserver); +} + +void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb) +{ + if (mStatusUpdatedSignal == NULL) + { + mStatusUpdatedSignal = new status_updated_signal_t(); + } + mStatusUpdatedSignal->connect(cb); + + if (mMarketPlaceStatus != MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) + { + // If already initialized, just confirm the status so the callback gets called + setSLMStatus(mMarketPlaceStatus); + } + else + { + // Initiate SLM connection and set responder + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; + std::string url = getSLMConnectURL("/merchant"); + log_SLM_infos("LLHTTPClient::get", url, ""); + LLHTTPClient::get(url, new LLSLMGetMerchantResponder(), LLSD()); + } +} + +// Get/Post/Put requests to the SLM Server using the SLM API +void LLMarketplaceData::getSLMListings() +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listings"); + log_SLM_infos("LLHTTPClient::get", url, ""); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + setUpdating(marketplacelistings_id, true); + LLHTTPClient::get(url, new LLSLMGetListingsResponder(marketplacelistings_id), headers); +} + +void LLMarketplaceData::getSLMListing(S32 listing_id) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listing_id); + log_SLM_infos("LLHTTPClient::get", url, ""); + const LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); + setUpdating(folder_id, true); + LLHTTPClient::get(url, new LLSLMGetListingResponder(folder_id), headers); +} + +void LLMarketplaceData::createSLMListing(const LLUUID& folder_id) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + LLViewerInventoryCategory* category = gInventory.getCategory(folder_id); + + Json::Value root; + Json::FastWriter writer; + + root["listing"]["name"] = category->getName(); + root["listing"]["inventory_info"]["listing_folder_id"] = category->getUUID().asString(); + + std::string json_str = writer.write(root); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.size(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + // Send request + std::string url = getSLMConnectURL("/listings"); + log_SLM_infos("LLHTTPClient::postRaw", url, json_str); + setUpdating(folder_id, true); + LLHTTPClient::postRaw(url, data, size, new LLSLMCreateListingsResponder(folder_id), headers); +} + +void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + Json::Value root; + Json::FastWriter writer; + + // Note : we're assuming that sending unchanged info won't break anything server side... + root["listing"]["id"] = listing_id; + root["listing"]["is_listed"] = is_listed; + root["listing"]["inventory_info"]["listing_folder_id"] = folder_id; + root["listing"]["inventory_info"]["version_folder_id"] = version_id; + root["listing"]["inventory_info"]["count_on_hand"] = count; + + std::string json_str = writer.write(root); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.size(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + // Send request + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listing_id); + log_SLM_infos("LLHTTPClient::putRaw", url, json_str); + setUpdating(folder_id, true); + LLHTTPClient::putRaw(url, data, size, new LLSLMCreateListingsResponder(folder_id, is_listed, version_id), headers); +} + +void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& source_folder_id) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + Json::Value root; + Json::FastWriter writer; + + // Note : we're assuming that sending unchanged info won't break anything server side... + root["listing"]["id"] = listing_id; + root["listing"]["is_listed"] = false; + root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); + root["listing"]["inventory_info"]["version_folder_id"] = LLUUID::null.asString(); + root["listing"]["inventory_info"]["count_on_hand"] = -1; + + std::string json_str = writer.write(root); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.size(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + // Send request + std::string url = getSLMConnectURL("/associate_inventory/") + llformat("%d", listing_id); + log_SLM_infos("LLHTTPClient::putRaw", url, json_str); + setUpdating(folder_id, true); + setUpdating(source_folder_id, true); + LLHTTPClient::putRaw(url, data, size, new LLSLMAssociateListingsResponder(folder_id, source_folder_id), headers); +} + +void LLMarketplaceData::deleteSLMListing(S32 listing_id) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listing_id); + log_SLM_infos("LLHTTPClient::del", url, ""); + LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); + setUpdating(folder_id, true); + LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folder_id, is_listed, version_id), headers); +} + +std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) +{ + std::string url(""); + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + // Get DirectDelivery cap + url = regionp->getCapability("DirectDelivery"); + // *TODO : Take this DirectDelivery cap coping mechanism hack out + if (url == "") + { + url = "https://marketplace.secondlife-staging.com/api/1/viewer/" + gAgentID.asString(); + } + else + { + llinfos << "Merov : DD cap = " << url << ", agent = " << gAgentID.asString() << llendl; + } + url += route; + } + return url; +} + +void LLMarketplaceData::setSLMStatus(U32 status) +{ + mMarketPlaceStatus = status; + if (mStatusUpdatedSignal) + { + (*mStatusUpdatedSigal)(); + } +} + +// Creation / Deletion / Update +// Methods publicly called +bool LLMarketplaceData::createListing(const LLUUID& folder_id) +{ + if (isListed(folder_id)) + { + // Listing already exists -> exit with error + return false; + } + + // Post the listing creation request to SLM + createSLMListing(folder_id); + + return true; +} + +bool LLMarketplaceData::clearListing(const LLUUID& folder_id) +{ + if (folder_id.isNull()) + { + // Folder doesn't exist -> exit with error + return false; + } + + // Folder id can be the root of the listing of not so we need to retrieve the root first + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); + S32 listing_id = getListingID(listing_uuid); + + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Update the SLM Server so that this listing is deleted (actually, archived...) + deleteSLMListing(listing_id); + + return true; +} + +bool LLMarketplaceData::getListing(const LLUUID& folder_id) +{ + if (folder_id.isNull()) + { + // Folder doesn't exist -> exit with error + return false; + } + + // Folder id can be the root of the listing or not so we need to retrieve the root first + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); + S32 listing_id = getListingID(listing_uuid); + + if (listing_id == 0) + { + // Listing doesn't exist ->exit with error + return false; + } + + // Get listing data from SLM + getSLMListing(listing_id); + + return true; +} + +bool LLMarketplaceData::getListing(S32 listing_id) +{ + if (listing_id == 0) + { + return false; + } + + // Get listing data from SLM + getSLMListing(listing_id); + return true; +} + +bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate) +{ + // Folder id can be the root of the listing or not so we need to retrieve the root first + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + LLUUID version_uuid = getVersionFolder(listing_uuid); + + // Also update the count on hand + S32 count = compute_stock_count(folder_id); + + // Post the listing update request to SLM + updateSLMListing(listing_uuid, listing_id, version_uuid, activate, count); + + return true; +} + +bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id) +{ + // Folder id can be the root of the listing or not so we need to retrieve the root first + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Note: if the version_id is cleared, we need to unlist the listing, otherwise, state unchanged + bool is_listed = (version_id.isNull() ? false : getActivationState(listing_uuid)); + + // Also update the count on hand + S32 count = compute_stock_count(version_id); + + // Post the listing update requesst to SLM + updateSLMListing(listing_uuid, listing_id, version_id, is_listed, count); + + return true; +} + +bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id) +{ + // Folder id can be the root of the listing or not so we need to retrieve the root first + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Get the unchanged values + bool is_listed = getActivationState(lissting_uuid); + LLUUID version_uuid = getVersionFolder(listing_uuid); + + // Compute the new count on hand + S32 count = compute_stock_count(folder_id); + + // Post the listing update request to SLM + updateSLMListing(listing_uuid, listing_id, version_uuid, is_listed, count); + + return true; +} + +bool LLMarketplaceData::associateListing(const LLUUID& folder_id, const LLUUID& source_folder_id, S32 listing_id) +{ + if (isListed(folder_id)) + { + // Listing already exists -> exit with error + return false; + } + + // Post the listing update request to SLM + associateSLMListing(folder_id, listing_id, source_folder_id); + + return true; +} + +// Methods privately called or called by SLM responders to perform changes +bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, bool update) +{ + if (isListed(folder_id)) + { + // Listing already exists -> exit with error + return false; + } + mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id, version_id, is_listed); + + if (update) + { + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update) +{ + if (!isListed(folder_id)) + { + // Listing doesn't exist -> exit with error + return false; + } + mMarketplaceItems.erase(folder_id); + + if (update) + { + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::deleteListing(S32 listing_id, bool update) +{ + if (listing_id == 0) + { + return false; + } + + LLUUID folder_id = getListingFolder(listing_id); + return deleteListing(folder_id, update); +} + +// Accessors +bool LLMarketplaceData::getActivationState(const LLUUID& folder_id) +{ + // Listing folder case + if (isListed(folder_id)) + { + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it->second).mIsActive; + } + // We need to iterate through the list to check it's not a version folder + marketplace_items_list_t::iterator it = mMarketplaceItems.begin(); + while (it != mMarketplaceItems.end()) + { + if ((it->second).mVersionFolderId == folder_id) + { + return (it->second).mIsActive; + } + it++; + } + return false; +} + +S32 LLMarketplaceData::getListingID(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? 0 : (it->second).mListingId); +} + +S32 LLMarketplaceData::getCountOnHand(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? -1 : (it->second).mCountOnHand); +} + +LLUUID LLMarketplaceData::getVersionFolder(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? LLUUID::null : (it->second).mVersionFolderId); +} + +// Reverse lookup : find the listing folder id from the listing id +LLUUID LLMarketplaceData::getListingFolder(S32 listing_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.begin(); + while (it != mMarketplaceItems.end()) + { + if ((it->second).mListingId == listing_id) + { + return (it->second).mListingFolderId; + } + it++; + } +} + +std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id) +{ + S32 depth = depth_nesting_in_marketplace(folder_id); + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? "" : (it->second).mEditURL); +} + +bool LLMarketplaceData::isListed(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it != mMarketplaceItems.end()); +} + +bool LLMarketplaceData:isListedAndActive(const LLUUID& folder_id) +{ + return (isListed(folder_id) && getActivationState(folder_id)); +} + +bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.begin(): + while (it != mMarketplaceItems.end()) + { + if ((it->second).mVersionFolderId == folder_id) + { + return true; + } + it++; + } + return false; +} + +bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id) +{ + S32 depth = depth_nesting_in_marketplace(obj_id); + LLUUID listing_uuid = nested_parent_id(obj_id, depth); + bool active = getActivationState(listing_uuid); + LLUUID version_uuid = getVersionFolder(listing_uuid); + return (active && ((obj_id == version_uuid) || gInventory.isObjectDescendentOf(obj_id, version_uuid))); +} + +LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id) +{ + S32 depth = depth_nesting_in_marketplace(obj_id); + LLUUID listing_uuid = nested_parent_id(obj_id, depth); + return (getActivationState(listing_uuid) ? getVersionFolder(listing_uuid) : LLUUID::null); +} + +bool LLMarketplaceData::isUpdating(const LLUUID& folder_id) +{ + S32 depth = depth_nesting_in_marketplace(folder_id); + if ((depth <= 0) || (depth > 2)) + { + // Only listing and version folders though are concerned by that status + return false; + } + else + { + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + std::set::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid); + if (it != mPendingUpdateSet.end()) + { + // If we're waiting for data for the marketplace listings root, we are in the updating process for all + return true; + } + else + { + // Check if the listing folder is waiting for data + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + it = mPendingUpdateSet.find(listing_uuid); + return (it != mPendingUpdateSet.end()); + } + } +} + +void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool isUpdating) +{ + std::set::iterator it = mPendingUpdateSet.find(folder_id); + if (it != mPendingUpdateSet.end()) + { + mPendingUpdateSet.erase(it); + } + if (isUpdating) + { + mPendingUpdateSet.insert(folder_id); + } +} + +// Private Modifiers +bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mListingId = listing_id; + + if (update) + { + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::setCountOnHand(const LLUUID& folder_id, S32 count, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mCountOnHand = count; + + return true; +} + +bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + LLUUID old_version_id = (it->second).mVersionFolderId; + if (old_version_id == version_id) + { + return false; + } + + (it->second).mVersionFolderId = version_id; + + if (update) + { + update_marketplace_category(old_version_id, false); + update_marketplace_category(version_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::setActivationState(const LLUUID& folder_id, bool activate, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mIsActive = activate; + + if (update) + { + update_marketplace_category((it->second).mListingFolderId, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData:setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mEditURL = edit_url; + return true; +} + + + diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index d658775b9..0ce9083eb 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -73,6 +73,8 @@ namespace MarketplaceStatusCodes MARKET_PLACE_CONNECTION_FAILURE = 2, MARKET_PLACE_MERCHANT = 3, MARKET_PLACE_NOT_MERCHANT = 4, + MARKET_PLACE_NOT_MIGRATED_MERCHANT = 5, + MARKET_PLACE_MIGRATED_MERCHANT = 6 }; } @@ -114,6 +116,150 @@ private: }; +// Classes handling the data coming from and going to the Marketplace SLM Server DB: +// * implement the Marketplace API +// * cache the current Marketplace data (tuples) +// * provide methods to get Marketplace data on any inventory item +// * set Marketplace data +// * signal Marketplace updates to inventory +namespace SLMErrorCodes +{ + enum eCode + { + SLM_SUCCESS = 200, + SLM_RECORD_CREATED = 201, + SLM_MALFORMED_PAYLOAD = 400, + SLM_NOT_FOUND = 404, + }; +} + +class LLMarketplaceData; +class LLInventoryObserver; + +// A Marketplace item is known by its tuple +class LLMarketplaceTuple +{ +public: + friend class LLMarketplaceData; + + LLMarketplaceTuple(); + LLMarketplaceTuple(const LLUUID& folder_id); + LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed = false); + +private: + // Representation of a marketplace item in the Marketplace DB (well, what we know of it...) + LLUUID mListingFolderId; + S32 mListingId; + LLUUID mVersionFolderId; + bool mIsActive; + S32 mCountOnHand; + std::string mEditURL; +}; +// Notes: +// * The mListingFolderId is used as a key to this map. It could therefore be taken off the LLMarketplaceTuple object themselves. +// * The SLM DB however uses the mListingId as its primary key and it shows in its API. In the viewer though, the mListingFolderId is what we use to grab an inventory record. +typedef std::map marketplace_items_list_t; + +// Session cache of all Marketplace tuples +// Notes: +// * There's one and only one possible set of Marketplace dataset per agent and per session thus making it an LLSingleton +// * Some of those records might correspond to folders that do not exist in the inventory anymore. We do not clear them out though. They just won't show up in the UI. + +class LLSLMGetMerchantResponder; +class LLSLMGetListingsResponder; +class LLSLMCreateListingsResponder; +class LLSLMGetListingResponder; +class LLSLMUpdateListingsResponder; +class LLSLMAssociateListingsResponder; +class LLSLMDeleteListingsResponder; + +class LLMarketplaceData + : public LLSingleton +{ +public: + friend class LLSLMGetMerchantResponder; + friend class LLSLMGetListingsResponder; + friend class LLSLMCreateListingsResponder; + friend class LLSLMGetListingResponder; + friend class LLSLMUpdateListingsResponder; + friend class LLSLMAssociateListingsResponder; + friend class LLSLMDeleteListingsResponder; + + LLMarketplaceData(); + virtual ~LLMarketplaceData(); + + // Public SLM API : Initialization and status + typedef boost::signals2::signal status_updated_signal_t; + void initializeSLM(const status_updated_signal_t::slot_type& cb); + U32 getSLMStatus() const { return mMarketPlaceStatus; } + void getSLMListings(); + bool isEmpty() const { return (mMarketplaceItems.size() == 0); } + + // High level create/delete/set Marketplace data: each method returns true if the function succeeds, false if error + bool createListing(const LLUUID& folder_id); + bool activateListing(const LLUUID& folder_id, bool activate); + bool clearListing(const LLUUID& folder_id); + bool setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id); + bool associateListing(const LLUUID& folder_id, const LLUUID& source_folder_id, S32 listing_id); + bool updateCountOnHand(const LLUUID& folder_id); + bool getListing(const LLUUID& folder_id); + bool getListing(S32 listing_id); + bool deleteListing(S32 listing_id, bool update = true); + + // Probe the Marketplace data set to identify folders + bool isListed(const LLUUID& folder_id); // returns true if folder_id is a Listing folder + bool isListedAndActive(const LLUUID& folder_id); // returns true if folder_id is an active (listed) Listing folder + bool isVersionFolder(const LLUUID& folder_id); // returns true if folder_id is a Version folder + bool isInActiveFolder(const LLUUID& obj_id); // returns true if the obj_id is buried in an active version folder + LLUUID getActiveFolder(const LLUUID& obj_id); // returns the UUID of the active version folder obj_id is in + bool isUpdating(const LLUUID& folder_id); // returns true if we're waiting from SLM incoming data for folder_id + + // Access MArketplace data set : each method returns a default value if the argument can't be found + bool getActivationState(const LLUUID& folder_id); + S32 getListingID(const LLUUID& folder_id); + LLUUID getVersionFolder(const LLUUID& folder_id); + std::string getListingURL(const LLUUID& folder_id); + LLUUID getListingFolder(S32 listing_id); + S32 getCountOnHand(const LLUUID& folder_id); + + // Used to flag if stock count values for Marketplace have to updated + bool checkDirtyCount() { if (mDirtyCount) { mDirtyCount = false; return true; } else { return false; } } + void setDirtyCount() { mDirtyCount = true; } + void setUpdating(const LLUUID& folder_id, bool isUpdating); + +private: + // Modify Marketplace data set : each method returns true if the function succeeds, false if error + // Used internally only by SLM Responders when data are received from the SLM Server + bool addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, bool update = true); + bool deleteListing(const LLUUID& folder_id, bool update = true); + bool setListingID(const LLUUID& folder_id, S32 listing_id, bool update = true); + bool setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id, bool update = true); + bool setActivationState(const LLUUID& folder_id, bool activate, bool update = true); + bool setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update = true); + bool setCountOnHand(const LLUUID& folder_id, S32 count, bool update = true); + + // Private SLM API : package data and get/post/put requests to the SLM Server through the SLM API + void setSLMStatus(U32 status); + void createSLMListing(const LLUUID& folder_id); + void getSLMListing(S32 listing_id); + void updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count); + void associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id); + void deleteSLMListing(S32 listing_id); + std::string getSLMConnectURL(const std::string& route); + + // Handling Marketplace connection and inventory connection + U32 mMarketPlaceStatus + status_updated_signal_t* mStatusUpdatedSignal; + LLInventoryObserver* mInventoryObserver; + bool mDirtyCount; // If true, stock count value need to be updated at the next check + + // Update data + std::set mPendingUpdateSet; + + // The cache of SLM data (at last...) + marketplace_items_list_t mMarketplaceItems; +}; + #endif // LL_LLMARKETPLACEFUNCTIONS_H diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index 56ae324ea..09453a269 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -76,6 +76,7 @@ #include "llfloaterland.h" #include "llfloaterlandholdings.h" #include "llfloatermap.h" +#include "llfloatermarketplacelistings.h" #include "llfloatermediafilter.h" #include "llfloatermemleak.h" #include "llfloatermessagelog.h" @@ -230,6 +231,7 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("joystick"); registerFloater ("media filter"); registerFloater ("mini map"); + registerFloater ("marketplace_listings"); registerFloater ("movement controls"); registerFloater ("mute list"); registerFloater ("notifications console"); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 88da7fc15..5ff19b8d1 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2339,6 +2339,14 @@ bool idle_startup() } display_startup(); + + // Init SLM Marketplace connection so we know which UI should be used for the user as a merchant + // Note: Eventually, all merchant will be migrated to the new SLM system and there will be no reason to show the old UI at all. + // *TODO : Suppress the Merchant Outbox UI completely + check_merchant_status(); + + display_startup(); + // We're successfully logged in. gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index e26d65d93..00c4e3460 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -802,6 +802,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if ( !handled ) { + // *TODO: Suppress the "outbox" case once "marketplace" is used everywhere for everyone // Disallow drag and drop to 3D from the outbox const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (outbox_id.notNull()) @@ -816,7 +817,21 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, } } } - + // Disallow drag and drop to 3D from the marketplace + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.notNull()) + { + for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) + { + if (gInventory.isObjectDescendentOf(mCargoIDs[item_index], outbox_id)) + { + *acceptance = ACCEPT_NO; + mToolTipMsg = LLTrans::getString("TooltipOutboxDragToWorld"); + return; + } + } + } + dragOrDrop3D( x, y, mask, drop, acceptance ); } } diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index fbabfa540..3e2acd2d5 100644 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp @@ -131,12 +131,16 @@ LLViewerFolderDictionary::LLViewerFolderDictionary() addEntry(LLFolderType::FT_OUTFIT, new ViewerFolderEntry("New Outfit", "inv_folder_outfit.tga", "inv_folder_outfit.tga", TRUE, false)); addEntry(LLFolderType::FT_MY_OUTFITS, new ViewerFolderEntry("My Outfits", "inv_folder_outfit.tga", "inv_folder_outfit.tga", TRUE, false)); addEntry(LLFolderType::FT_MESH, new ViewerFolderEntry("Meshes", "inv_folder_mesh.tga", "inv_folder_mesh.tga", FALSE, false)); - + + bool boxes_invisible = !gSavedSettings.getBOOL("InventoryOutboxMakeVisible"); addEntry(LLFolderType::FT_INBOX, new ViewerFolderEntry("Received Items", "inv_folder_inbox.tga", "inv_folder_inbox.tga", FALSE, false)); - addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "inv_folder_outbox.tga", "inv_folder_outbox.tga", FALSE, false)); + addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "inv_folder_outbox.tga", "inv_folder_outbox.tga", FALSE, boxes_invisible)); addEntry(LLFolderType::FT_BASIC_ROOT, new ViewerFolderEntry("Basic Root", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false)); + addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new ViewerFolderEntry("Marketplace Listings", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, boxes_invisible)); + addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new ViewerFolderEntry("New Stock", "Inv_StockFolderOpen", "Inv_StockFolderClosed", FALSE, false, "default")); + addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new ViewerFolderEntry("New Version", "Inv_VersionFolderOpen", "Inv_VersionFolderClosed", FALSE, false, "default")); addEntry(LLFolderType::FT_SUITCASE, new ViewerFolderEntry("My Suitcase", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false)); addEntry(LLFolderType::FT_NONE, new ViewerFolderEntry("New Folder", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false, "default")); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index e07ccebeb..6e91e7a99 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -809,6 +809,31 @@ bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const return true; } +bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item) +{ + bool accept = true; + // Only stock folders have limitation on which item they will accept + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // If the item is copyable (i.e. non stock) do not accept the drop in a stock folder + if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + accept = false; + } + else + { + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(getUUID(),cat_array,item_array); + // Destination stock folder must be empty OR types of incoming and existing items must be identical and have the same permissions + accept = (!item_array->size() || + ((item_array->at(0)->getInventoryType() == inv_item->getInventoryType()) && + (item_array->at(0)->getPermissions().getMaskNextOwner() == inv_item->getPermissions().getMaskNextOwner()))); + } + } + return accept; +} + void LLViewerInventoryCategory::determineFolderType() { /* Do NOT uncomment this code. This is for future 2.1 support of ensembles. diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 7b9db7234..a9eea12bf 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -232,6 +232,9 @@ public: virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); virtual BOOL unpackMessage(const LLSD& category); + // returns true if the category object will accept the incoming item + bool acceptItem(LLInventoryItem* inv_item); + private: friend class LLInventoryModel; void localizeName(); // intended to be called from the LLInventoryModel diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index ff590cd74..26aa675c0 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -94,6 +94,7 @@ #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" #include "llmimetypes.h" #include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmutelist.h" @@ -103,6 +104,7 @@ #include "llselectmgr.h" #include "llstatusbar.h" #include "lltextureview.h" +#include "lltoolbar.h" #include "lltoolcomp.h" #include "lltoolgrab.h" #include "lltoolmgr.h" @@ -585,6 +587,55 @@ void rebuild_context_menus() gAgentAvatarp->buildContextMenus(); } +void set_merchant_SLM_menu() +{ + // DD-170 : SLM Alpha and Beta program : for the moment, we always show the SLM menu and + // tools so that all merchants can try out the UI, even if not migrated. + // *TODO : Keep SLM UI hidden for non migrated merchant in released viewer + + //if (LLMarketplaceData::instance().getSLMStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT) + //{ + // Merchant not migrated: show only the old Merchant Outbox menu + // gMenuHolder->getChild("MerchantOutbox")->setVisible(TRUE); + //} + //else + //{ + // All other cases (new merchant, not merchant, migrated merchant): show the new Marketplace Listings menu and enable the tool + gMenuHolder->getChild("MarketplaceListings")->setVisible(TRUE); + gToolBar->getChild("marketplace_listings_btn")->setEnabled(false); + //} +} + +void set_merchant_outbox_menu(U32 status, const LLSD& content) +{ + // If the merchant is fully migrated, the API is disabled (503) and we won't show the old menu item. + // In all other cases, we show it. + if (status != MarketplaceErrorCodes::IMPORT_SERVER_API_DISABLED) + { + gMenuHolder->getChild("MerchantOutbox")->setVisible(TRUE); + } +} + +void check_merchant_status() +{ + if (!gSavedSettings.getBOOL("InventoryOutboxDisplayBoth")) + { + // Hide both merchant related menu items + gMenuHolder->getChild("MerchantOutbox")->setVisible(FALSE); + gMenuHolder->getChild("MarketplaceListings")->setVisible(FALSE); + + // Also disable the toolbar button for Marketplace Listings + gToolBar->getChild("marketplace_listings_btn")->setEnabled(false); + + // Launch an SLM test connection to get the merchant status + LLMarketplaceData::instance().initializeSLM(boost::bind(&set_merchant_SLM_menu)); + + // Launch a Merchant Outbox test connection to get the migration status + LLMarketplaceInventoryImporter::instance().setStatusReportCallback(boost::bind(&set_merchant_outbox_menu, _1, _2)); + LLMarketplaceInventoryImporter::instance().initialize(); + } +} + void init_menus() { S32 top = gViewerWindow->getRootView()->getRect().getHeight(); @@ -8815,6 +8866,16 @@ class LLWorldEnableEnvSettings : public view_listener_t } }; +bool canAccessMarketplace(); +class LLMarketplaceEnabled : public LLMemberListener +{ + bool handleEvent(LLPointer, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString())->setValue(canAccessMarketplace()); + return true; + } +}; + class SinguCloseAllDialogs : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -9378,6 +9439,7 @@ void initialize_menus() addMenu(new LLWorldVisibleDestinations(), "World.VisibleDestinations"); (new LLWorldEnvSettings())->registerListener(gMenuHolder, "World.EnvSettings"); (new LLWorldEnableEnvSettings())->registerListener(gMenuHolder, "World.EnableEnvSettings"); + addMenu(new LLMarketplaceEnabled, "Marketplace.Enabled"); // Tools menu diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 72a2b80fa..b3f2cda7b 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -89,6 +89,7 @@ BOOL enable_god_full(void* user_data); BOOL enable_god_liaison(void* user_data); BOOL enable_god_basic(void* user_data); void set_underclothes_menu_options(); +void check_merchant_status(); void exchange_callingcard(const LLUUID& dest_id); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e331f16a8..f27547ba0 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -76,6 +76,7 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" #include "llinventorypanel.h" +#include "llmarketplacefunctions.h" #include "llmutelist.h" #include "llnotify.h" #include "llnotifications.h" @@ -6709,7 +6710,7 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) LL_WARNS() << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << LL_ENDL; } } - + if ( (notificationID == "RegionEntryAccessBlocked") || (notificationID == "LandClaimAccessBlocked") || @@ -6761,6 +6762,25 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) LLUI::sAudioCallback(LLUUID(gSavedSettings.getString("UISndRestart"))); return true; // Floater is enough. } + else + + // Special Marketplace update notification + if (notificationID == "SLM_UPDATE_FOLDER") + { + std::string state = llsdBlock["state"].asString(); + if (state == "deleted") + { + // Perform the deletion viewer side, no alert shown in this case + LLMarketplaceData::instance().deleteListing(llsdBlock["listing_id"].asInteger()); + return true; + } + else + { + // In general, no message will be displayed, all we want is to get the listing updated in the marketplace floater + // If getListing() fails though, the message of the alert will be shown by the caller of attempt_standard_notification() + return LLMarketplaceData::instance().getListing(llsdBlock["listing_id"].asInteger()); + } + } LLNotificationsUtil::add(notificationID, llsdBlock); return true; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index d1208658a..d8d6acc98 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1894,6 +1894,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("CreateInventoryCategory"); capabilityNames.append("CustomMenuAction"); capabilityNames.append("DispatchRegionInfo"); + capabilityNames.append("DirectDelivery"); capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); diff --git a/indra/newview/skins/default/textures/Inv_StockFolderClosed.png b/indra/newview/skins/default/textures/Inv_StockFolderClosed.png new file mode 100644 index 0000000000000000000000000000000000000000..4dc484dc2279d69506fe4b7b7e9ead14523bef4c GIT binary patch literal 1143 zcmbVLO=#0l9FG=7X2ppoMeq@2D4HfOP1?P%s$0^!$~sG1*$y7|n!K%XOI}P~Z8ro# zya)yHq!$G*o>V*v3JyU*@E{@{q==vwLD)^ii@t2_R2Uw7@V@`Q-~an+dGh3rfxQEQ zAnYiPTNS^@lR4MVi7StviokVp;*?%eN`Xll?)Xwjs!NkK zduiTAj(GeS7-<`vz#|NUwzuF%Mms02@*4acKg%Lm6=Cx^am}ddQW=;uBtTE;lC7u^ zWKjz08iMdBP!*V#p(3kDg3!>i2F!ro5V^L{nKdd_p{I-Aa$wuutCeb{Q#72D zAwo!2R9RIeE+IvWKErLvj}G)1EE3tF8!(spAZEmM+GIJATe{|g7nDlt#D3HZ6c3r) z#(@k|itKr@U#rp)tB~JjY$zSo76T$zNJN{V&GRvPpbzG;`?sQ4kXvJ%4qcuUykJqg z=@Fk5t(?ezQjY5w2x6jRMU`gLiY`G-LlVjkBgr9kgrTkRsrGp`!kaLoD3&>Dq6~yE zpB@{|777JT$!C;&-h%7cq8~Br+oZ4C<+^)V^iQl|h6FPj)+k-*S3$W!8I2k=0Hz5J zO}V~9ThZY-Lu(FOBy=wjrw~#PtcKWdH&CxTm^d0jQWmDOQaz*9rD0pwB~{ZACI}-1 zW3h*K{x@_oH%gA{1;y)%YvtE{{b%_8RMKnF+XWN7@N8L3-Cd7(EtDd literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Inv_StockFolderOpen.png b/indra/newview/skins/default/textures/Inv_StockFolderOpen.png new file mode 100644 index 0000000000000000000000000000000000000000..0d140b56a70408c4a237538bed20aeca9f43118e GIT binary patch literal 1319 zcmbVMZ%o`|7_T`|LN@<=kmyXbDTZuH+k5-xu5CESad*hg9lP9}105giweN9-YkRak z?;ufO#u$u>5tU2)Jzu;TALqCwk_$KZ9 zJa2!$=Xu`ePlw`d_ieA)QG;RF_GoK3f%co+r`m(wJINc9XuHjcbT~;f;}m5JVnNkR zLp-X>T`&PO&(=qL=na4vRd}q66sAkdLk3hA>GIe}C z#gev#d+6me0>}GFA?;(x4A1eTC?mFe$fcX<|8R+t0&%?F=OT7N3|+R=F%SH}nwU+nJ{hI`hVvXS;ey zIs{P=ojO&}p<5O~P$cA~vgqKYs93~97f;Hos+4HmSaWBWkW}V^~ z9m@|I7IEV!l8IMbpP@Ofx3A(OYZ_C&geM&@E&O=(THqZ!EsSb;Iqzrilryjp;E{!) zqO~w>L0jlM$@f+aM&M$K_=-E@g+?VUZ1WoI96d_{zb!(i)54BXWBFk|LjufeaZR*j z1@JnI15Jeh^ze!d5)zOl1&RO|s&0TJUmF6`7T6O;!8}vb#cpX~ic;T@q;k0&FDK$8 zn3Mp7P?8l%QA8#ohVvdp710Yj>kK9it-$fALp(lWL?tpu(*m<}#|78V=XZ&{uofs5 zGO2=m3B+Z|btAvFr9)c4f6draIxNonSSsL<%mo(9$3$lx%wo5{qDYWgW1J5hmJ~E& z5^K)I9?h9)f&Ij7$2K59SVOWRP9$Va1gZ)}sHdQ4;}S%`QrT4MJbU3&$t3L8bu9yv z0Dx>Fl}M;iQFS#Tn`&0q>)4zZQsh~-`w8%{-Ud^E45ku%1{SsVQ6IQ z-@#>9s;lg$KWFwAE8j{R*;w21@?iDU%Hpw=_NEsHzP!A^#bUSKd{~V&er)SG+OhIB r*3iaZYi$~DT;*PIpPu^<8y2}3*Zi(kpIuX085x($4w*}*$FKbX(2a96 literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Inv_VersionFolderOpen.png b/indra/newview/skins/default/textures/Inv_VersionFolderOpen.png new file mode 100644 index 0000000000000000000000000000000000000000..659d7d392f85fb8b8900fd89eb4431d5101af0a5 GIT binary patch literal 1521 zcmb_cdu$VR96!Y+IJ1CJBKVJIkp&~y>-Bo=U0G#auY&@eU0d9Mf?V%@+m7ve-SxT_ zEFi>3#v?-*;~}8oxd(z69dkn^zzAa-?tzAfD2yd2j~S3;o7*yf9V_|=;Sb|4x%+*8 zzx#eZ-}jZevSy|vznqL9NQz??>xTRI_%n74d{4#3dEholwdblHIZusn3P7xa><6er z;_`tTaKf_UCXj|8V~Ry@uA1wdYvyH1%f)rHQ7H(~2$D7<8szvwprU?|F9s}{KN`N* zprT;WQcbSl^BLT$1FP)bW-yiQM`CPqt828JM}p?V!b;)D*@GZ;ab4JI>TL`Rnf zT2q8PvzyHvwFOTWO@XQg%{U&3M6?l7D=Yap!7vQ2)8l$Q1~stIvVh7(u|Q~Yf`J7g zUJ-+;C#TB79=C>3P7KJNfWj-u{fa+i^8u&#kh=Q5ojU2@p zO&CS%2#ll+0HZ0A#(2O3iZK}sgwBxQc^sb1)RR_LpJ8K+1VPxz3_ZirHk!1WbVk~w z&rDz)fso1tc#yCyLffNQ>l3kNn*umhR=l!Yl;{Fifvn1*0y&7-?6SrN(C-Fl$q*v3ybl>FYd`*z1?v0`p@iE#bg-c>4p^2qv|KUCgm-hb)T zr+;1h@=j;XPbYQ_b=8)CSR7Nh2bXJpYpY7P(A7KMyLPtxc4y7t_1cEYwM}1_@2`m6 z>Zoq5t2nrI#jj_#Rc>3^_sbXEE!&S*trL9NTTAo5tJ#pokUQ3e?_M}`_rhM0^KAL3 z_}<0J1>QNU!wZB(*~8s+KR1@8rlxijOo;!mvQ&4LXZZhI$RX#zdB}Xm!CJl1%f7Np z?vv@`?bFFi=(F!ndClrNky7561iXLR=LR0_! zp8i+c_dM0ob>)S^SiiTeEc+b^?JamWtvSQmzWC2m!I^iGCL-CQG3P{m^azXyaoDri J!x>A~{0$}RZoB{h literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/icn_toolbar_marketplace_listings.tga b/indra/newview/skins/default/textures/icn_toolbar_marketplace_listings.tga new file mode 100644 index 0000000000000000000000000000000000000000..49e5d11a21f391e18b563286837733b8990f34b7 GIT binary patch literal 49 rcmZQz;9`IQMg~R(1rCP)|Np~87(#+VUBf(sJ^lT3BiuZFT=f_Lkxd94 literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index b9d30eb02..999f60c4a 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -297,6 +297,11 @@ with the same filename but different name + + + + + diff --git a/indra/newview/skins/default/xui/en-us/floater_associate_listing.xml b/indra/newview/skins/default/xui/en-us/floater_associate_listing.xml new file mode 100644 index 000000000..e019ed58d --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_associate_listing.xml @@ -0,0 +1,53 @@ + + + + Listing ID: + + + Type ID here + + + + + + From e89e57b5a0335218ba234dfc80280a27d4d34566 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 13:56:18 -0500 Subject: [PATCH 173/332] [OpenSim] Feature Request: Allow passwords up to 24 characters long --- indra/newview/llpanellogin.cpp | 2 +- indra/newview/skins/default/xui/en-us/panel_login.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 6d873d0be..517bfb424 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -459,7 +459,7 @@ void LLPanelLogin::mungePassword(const std::string& password) } else { - LLMD5 pass((unsigned char *)utf8str_truncate(password, 16).c_str()); + LLMD5 pass((unsigned char *)utf8str_truncate(password, gHippoGridManager->getCurrentGrid()->isOpenSimulator() ? 24 : 16).c_str()); char munged_password[MD5HEX_STR_SIZE]; pass.hex_digest(munged_password); mMungedPassword = munged_password; diff --git a/indra/newview/skins/default/xui/en-us/panel_login.xml b/indra/newview/skins/default/xui/en-us/panel_login.xml index d1c875ad1..9a490a516 100644 --- a/indra/newview/skins/default/xui/en-us/panel_login.xml +++ b/indra/newview/skins/default/xui/en-us/panel_login.xml @@ -35,7 +35,7 @@ From f599278d8ddce32fb006b0b0348a2fdeba6f0eba Mon Sep 17 00:00:00 2001 From: Pytak Date: Thu, 28 Feb 2019 21:03:38 +0100 Subject: [PATCH 174/332] rename non-prefixed PyFX Ambient windlights to [PyFX], add one new Ambient windlight, add PyFX 0.6 Ice water preset --- ...%5BPyFX%5D%20Ambient%20%2BFog%20Black.xml} | 0 ...X%5D%20Ambient%20%2BFog%20Dark%20Blue.xml} | 0 ... %5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml} | 0 ...X%5D%20Ambient%20%2BFog%20Gray%20Blue.xml} | 0 ... %5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml} | 0 ...FX%5D%20Ambient%20%2BFog%20Underwater.xml} | 0 ...%5D%20Ambient%20%2BFog%20White%20Wash.xml} | 0 ...%5BPyFX%5D%20Ambient%20%2BFog%20White.xml} | 12 +- ...k.xml => %5BPyFX%5D%20Ambient%20Black.xml} | 0 ...5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml} | 0 ...rk.xml => %5BPyFX%5D%20Ambient%20Dark.xml} | 0 ...BPyFX%5D%20Ambient%20Gray%20Background.xml | 141 ++++++++++++++++++ ...ay.xml => %5BPyFX%5D%20Ambient%20Gray.xml} | 0 ...e.xml => %5BPyFX%5D%20Ambient%20White.xml} | 0 .../windlight/water/%5BPyFX%5D%20Ice.xml | 43 ++++++ 15 files changed, 190 insertions(+), 6 deletions(-) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Black.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Black.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Dark%20Blue.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Dark%20Blue.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Dark.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Gray%20Blue.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Gray%20Blue.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Gray.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20Underwater.xml => %5BPyFX%5D%20Ambient%20%2BFog%20Underwater.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20White%20Wash.xml => %5BPyFX%5D%20Ambient%20%2BFog%20White%20Wash.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20%2BFog%20White.xml => %5BPyFX%5D%20Ambient%20%2BFog%20White.xml} (95%) rename indra/newview/app_settings/windlight/skies/{Ambient%20Black.xml => %5BPyFX%5D%20Ambient%20Black.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20Dark%20H%2DBomb.xml => %5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20Not%20As%20Dark.xml => %5BPyFX%5D%20Ambient%20Dark.xml} (100%) create mode 100644 indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml rename indra/newview/app_settings/windlight/skies/{Ambient%20Gray.xml => %5BPyFX%5D%20Ambient%20Gray.xml} (100%) rename indra/newview/app_settings/windlight/skies/{Ambient%20Not%20As%20White.xml => %5BPyFX%5D%20Ambient%20White.xml} (100%) create mode 100644 indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Black.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Black.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Black.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Black.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark%20Blue.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark%20Blue.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark%20Blue.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark%20Blue.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray%20Blue.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray%20Blue.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray%20Blue.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray%20Blue.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Underwater.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Underwater.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Underwater.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Underwater.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White%20Wash.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White%20Wash.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White%20Wash.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White%20Wash.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml similarity index 95% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml index b33f7ad69..c91588466 100644 --- a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml @@ -2,10 +2,10 @@ ambient - 3 - 3 - 3 - 1 + 1.5 + 1.5 + 1.5 + 0.5 blue_density @@ -63,14 +63,14 @@ density_multiplier - 4.999999873689375817775726e-05 + 0.0008999999845400452613830566 0 0 1 distance_multiplier - 50 + 1 0 0 1 diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Black.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Black.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Black.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Black.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Dark%20H%2DBomb.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Dark%20H%2DBomb.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20Dark.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20Dark.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark.xml diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml new file mode 100644 index 000000000..fe1c9f8c0 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.5 + 1.5 + 1.5 + 0.5 + + blue_density + + 0.449999993698308176082179 + 0.4499999939833594919669224 + 0.4499999944309046040480382 + 1 + + blue_horizon + + 0.89999997615814208984375 + 0.89999997615814208984375 + 0.89999997615814208984375 + 0.449999988079071044921875 + + cloud_color + + 0.2261540024157503436796901 + 0.2261540024157503436796901 + 0.2261540024157503436796901 + 1 + + cloud_pos_density1 + + 0 + 0 + 0 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.4199999868869781494140625 + 0 + 0 + 1 + + cloud_scroll_rate + + 10 + 10 + + cloud_shadow + + 0.36000001430511474609375 + 0 + 0 + 1 + + density_multiplier + + 0.0004499999922700226306915283 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.001000000047497451305389404 + -0 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.1991560043746730401803546 + 0.1991560043746730401803546 + 1 + + lightnorm + + -0 + 1 + 1.148381556959066074341536e-06 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 26 + star_brightness + 0 + sun_angle + 1.57079517841339111328125 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Gray.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Gray.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20White.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20White.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20White.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20White.xml diff --git a/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml new file mode 100644 index 000000000..a5cdf76ae --- /dev/null +++ b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml @@ -0,0 +1,43 @@ + + + blurMultiplier + 0.1600000113248825073242188 + fresnelOffset + 0.5 + fresnelScale + 1 + normScale + + 5 + 5 + 5 + + normalMap + f92da839-f302-c813-668d-601ab1957af1 + scaleAbove + 0.199999988079071044921875 + scaleBelow + 0.2000000029802322387695313 + underWaterFogMod + 1 + waterFogColor + + 0.02851562201976776123046875 + 0.07148437201976776123046875 + 0.06242064759135246276855469 + 1 + + waterFogDensity + 2 + wave1Dir + + 0 + 0 + + wave2Dir + + 0 + 0 + + + From 8e18f2ab43e422f3488aecf73499a19512922787 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 20:09:00 -0500 Subject: [PATCH 175/332] So sick of these EEP warnings in my log, add the asset types --- indra/llcommon/llassettype.cpp | 3 + indra/llcommon/llassettype.h | 13 ++- indra/llinventory/llinventorytype.cpp | 10 ++ indra/llinventory/llinventorytype.h | 10 +- indra/newview/llinventorybridge.cpp | 3 + indra/newview/llinventoryicon.cpp | 25 +++++ indra/newview/llinventoryicon.h | 1 + indra/newview/llviewerassetstats.cpp | 97 ++++-------------- indra/newview/llviewerassettype.cpp | 3 + .../skins/default/textures/Inv_Settings.png | Bin 0 -> 925 bytes .../default/textures/Inv_SettingsDay.png | Bin 0 -> 925 bytes .../default/textures/Inv_SettingsSky.png | Bin 0 -> 883 bytes .../default/textures/Inv_SettingsWater.png | Bin 0 -> 764 bytes .../default/textures/Inv_UnknownObject.png | Bin 0 -> 1048 bytes .../skins/default/textures/textures.xml | 5 + 15 files changed, 90 insertions(+), 80 deletions(-) create mode 100644 indra/newview/skins/default/textures/Inv_Settings.png create mode 100644 indra/newview/skins/default/textures/Inv_SettingsDay.png create mode 100644 indra/newview/skins/default/textures/Inv_SettingsSky.png create mode 100644 indra/newview/skins/default/textures/Inv_SettingsWater.png create mode 100644 indra/newview/skins/default/textures/Inv_UnknownObject.png diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 5b22d5bab..66602f4e7 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -101,6 +101,9 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_OUTFIT, new AssetEntry("OUTFIT", "outfit", "outfit", false, false, false)); addEntry(LLAssetType::AT_MY_OUTFITS, new AssetEntry("MY_OUTFITS", "my_otfts", "my outfits", false, false, false)); addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, true, true)); + addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true)); + + addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false)); }; diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index e6fee54ca..272c63399 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -132,7 +132,16 @@ public: AT_MESH = 49, // Mesh data in our proprietary SLM format - AT_COUNT = 50, + AT_RESERVED_1 = 50, + AT_RESERVED_2 = 51, + AT_RESERVED_3 = 52, + AT_RESERVED_4 = 53, + AT_RESERVED_5 = 54, + AT_RESERVED_6 = 55, + + AT_SETTINGS = 56, // Collection of settings + + AT_COUNT = 57, // +*********************************************************+ // | TO ADD AN ELEMENT TO THIS ENUM: | @@ -143,7 +152,7 @@ public: // | 4. ADD TO LLViewerAssetType.cpp | // | 5. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | // +*********************************************************+ - + AT_UNKNOWN = 255, AT_NONE = -1 }; diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index 78651b55f..fcef1611c 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -85,6 +85,7 @@ LLInventoryDictionary::LLInventoryDictionary() addEntry(LLInventoryType::IT_ANIMATION, new InventoryEntry("animation", "animation", 1, LLAssetType::AT_ANIMATION)); addEntry(LLInventoryType::IT_GESTURE, new InventoryEntry("gesture", "gesture", 1, LLAssetType::AT_GESTURE)); addEntry(LLInventoryType::IT_MESH, new InventoryEntry("mesh", "mesh", 1, LLAssetType::AT_MESH)); + addEntry(LLInventoryType::IT_SETTINGS, new InventoryEntry("settings", "settings", 1, LLAssetType::AT_SETTINGS)); } @@ -145,6 +146,15 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = LLInventoryType::IT_NONE, // 47 AT_NONE LLInventoryType::IT_NONE, // 48 AT_NONE LLInventoryType::IT_MESH, // 49 AT_MESH + + LLInventoryType::IT_NONE, // 50 AT_RESERVED_1 + LLInventoryType::IT_NONE, // 52 AT_RESERVED_2 + LLInventoryType::IT_NONE, // 53 AT_RESERVED_3 + LLInventoryType::IT_NONE, // 54 AT_RESERVED_4 + LLInventoryType::IT_NONE, // 55 AT_RESERVED_5 + LLInventoryType::IT_NONE, // 56 AT_RESERVED_6 + + LLInventoryType::IT_SETTINGS, // 57 AT_SETTINGS }; // static diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h index b444ecfa5..1d9627ffb 100644 --- a/indra/llinventory/llinventorytype.h +++ b/indra/llinventory/llinventorytype.h @@ -62,8 +62,10 @@ public: IT_ANIMATION = 19, IT_GESTURE = 20, IT_MESH = 22, - IT_COUNT = 23, + IT_SETTINGS = 25, + IT_COUNT = 26, + IT_UNKNOWN = 255, IT_NONE = -1 }; @@ -108,8 +110,14 @@ public: ICONNAME_LINKFOLDER, ICONNAME_MESH, + ICONNAME_SETTINGS, + ICONNAME_SETTINGS_SKY, + ICONNAME_SETTINGS_WATER, + ICONNAME_SETTINGS_DAY, + ICONNAME_CLOTHING_UNKNOWN, ICONNAME_INVALID, + ICONNAME_UNKNOWN, ICONNAME_COUNT, ICONNAME_NONE = -1 }; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 67cd288b4..3eb442385 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1296,6 +1296,9 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, //LL_WARNS() << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << LL_ENDL; break; + case LLAssetType::AT_SETTINGS: + break; + default: LL_INFOS() << "Unhandled asset type (llassetstorage.h): " << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << LL_ENDL; diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp index f93dc8dd7..f2739df69 100644 --- a/indra/newview/llinventoryicon.cpp +++ b/indra/newview/llinventoryicon.cpp @@ -88,8 +88,14 @@ LLIconDictionary::LLIconDictionary() addEntry(LLInventoryType::ICONNAME_LINKFOLDER, new IconEntry("inv_link_folder.tga")); addEntry(LLInventoryType::ICONNAME_MESH, new IconEntry("inv_item_mesh.tga")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_SKY, new IconEntry("Inv_SettingsSky.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_WATER, new IconEntry("Inv_SettingsWater.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_DAY, new IconEntry("Inv_SettingsDay.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS, new IconEntry("Inv_Settings.png")); + addEntry(LLInventoryType::ICONNAME_CLOTHING_UNKNOWN, new IconEntry("inv_item_unknown.tga")); addEntry(LLInventoryType::ICONNAME_INVALID, new IconEntry("inv_invalid.png")); + addEntry(LLInventoryType::ICONNAME_UNKNOWN, new IconEntry("Inv_UnknownObject.png")); addEntry(LLInventoryType::ICONNAME_NONE, new IconEntry("NONE")); } @@ -163,6 +169,14 @@ const std::string& LLInventoryIcon::getIconName(LLAssetType::EType asset_type, break; case LLAssetType::AT_MESH: idx = LLInventoryType::ICONNAME_MESH; + break; + case LLAssetType::AT_SETTINGS: + // TODO: distinguish between Sky and Water settings. + idx = assignSettingsIcon(misc_flag); + break; + case LLAssetType::AT_UNKNOWN: + idx = LLInventoryType::ICONNAME_UNKNOWN; + break; default: break; } @@ -182,3 +196,14 @@ LLInventoryType::EIconName LLInventoryIcon::assignWearableIcon(U32 misc_flag) const LLWearableType::EType wearable_type = LLWearableType::EType(LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK & misc_flag); return LLWearableType::getIconName(wearable_type); } + +LLInventoryType::EIconName LLInventoryIcon::assignSettingsIcon(U32 misc_flag) +{ + switch (misc_flag & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK) + { + case 0: return LLInventoryType::ICONNAME_SETTINGS_SKY; + case 1: return LLInventoryType::ICONNAME_SETTINGS_WATER; + case 2: return LLInventoryType::ICONNAME_SETTINGS_DAY; + } + return LLInventoryType::ICONNAME_SETTINGS; +} diff --git a/indra/newview/llinventoryicon.h b/indra/newview/llinventoryicon.h index 659448143..58e1ffdb6 100644 --- a/indra/newview/llinventoryicon.h +++ b/indra/newview/llinventoryicon.h @@ -49,6 +49,7 @@ public: protected: static LLInventoryType::EIconName assignWearableIcon(U32 misc_flag); + static LLInventoryType::EIconName assignSettingsIcon(U32 misc_flag); }; #endif // LL_LLINVENTORYICON_H diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 90260ed6f..7e41b4bdd 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -524,88 +524,31 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i // - gestures // - everything else. // - llassert_always(50 == LLViewerAssetType::AT_COUNT); // Multiple asset definitions are floating around so this requires some // maintenance and attention. - static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] = - { - LLViewerAssetStats::EVACTextureTempHTTPGet, // (0) AT_TEXTURE - LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND - LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD - LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK - LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT - LLViewerAssetStats::EVACWearableUDPGet, // AT_CLOTHING - LLViewerAssetStats::EVACOtherGet, // AT_OBJECT - LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD - LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT - LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE - LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA - LLViewerAssetStats::EVACWearableUDPGet, // AT_BODYPART - LLViewerAssetStats::EVACOtherGet, // AT_TRASH - LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND - LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND_WAV - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG - LLViewerAssetStats::EVACGestureUDPGet, // (20) AT_ANIMATION - LLViewerAssetStats::EVACGestureUDPGet, // AT_GESTURE - LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE - LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE - LLViewerAssetStats::EVACOtherGet, // AT_LINK - LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (30) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (40) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // AT_MESH - // (50) - }; - - if (at < 0 || at >= LLViewerAssetType::AT_COUNT) + switch (at) { + case LLAssetType::AT_TEXTURE: + return is_temp ? with_http ? LLViewerAssetStats::EVACTextureTempHTTPGet : LLViewerAssetStats::EVACTextureTempUDPGet + : with_http ? LLViewerAssetStats::EVACTextureTempHTTPGet : LLViewerAssetStats::EVACTextureNonTempUDPGet; + break; + case LLAssetType::AT_SOUND: + case LLAssetType::AT_SOUND_WAV: + return LLViewerAssetStats::EVACSoundUDPGet; + break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + return LLViewerAssetStats::EVACWearableUDPGet; + break; + case LLAssetType::AT_ANIMATION: + case LLAssetType::AT_GESTURE: + return LLViewerAssetStats::EVACGestureUDPGet; + break; + case LLAssetType::AT_LANDMARK: + default: return LLViewerAssetStats::EVACOtherGet; + break; } - LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]); - if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret) - { - // Indexed with [is_temp][with_http] - static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] = - { - { - LLViewerAssetStats::EVACTextureNonTempUDPGet, - LLViewerAssetStats::EVACTextureNonTempHTTPGet, - }, - { - LLViewerAssetStats::EVACTextureTempUDPGet, - LLViewerAssetStats::EVACTextureTempHTTPGet, - } - }; - - ret = texture_bin_map[is_temp][with_http]; - } - return ret; } - } // anonymous namespace diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index 3eee60882..b98767b1b 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -88,7 +88,10 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_MESH, new ViewerAssetEntry(DAD_MESH)); + addEntry(LLViewerAssetType::AT_UNKNOWN, new ViewerAssetEntry(DAD_NONE)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); + addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_NONE)); }; EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type) diff --git a/indra/newview/skins/default/textures/Inv_Settings.png b/indra/newview/skins/default/textures/Inv_Settings.png new file mode 100644 index 0000000000000000000000000000000000000000..c43ba349c486e32db0ff2ec222c665a72b0d0674 GIT binary patch literal 925 zcmV;O17iG%P){0OnIuy|=$p+QY-l#zy-q%q2`&6_vB z)ckn8Hsi3R1Rx+)<(YO^?b|e&4^XE*i59sHhPhHcTrg zJT4qu$(f3`gD01OJde6beJnFGExtoWJ2oK#_|cePj8S~_F(lSU5B>DYj56`weEcL?i8aKUtYWc5 zf+RV*MO~KcG4XNHW71Qss`~$}s~sQcFJ!OxL)DYxymdnL7JP3y&gY@yd>?`P5iT;N z`0N1;zKgzD(meanxDIERB`vZ8=Nr)WVCt5hm)tU3@i@GmK*JogTtn?bp=QN(j_)D7 zCQo$*yL<9gWPju04-GVDy>nq^#{d8T32;bRa{vGi!~g&e!~vBn4jTXf0m4Z{K~y-6 zrIJlZR8bg)pL@^fFs)2%%E3qov{JG`F&83(p|%Lb7IuUd8HkHCkjRCB3?d2=4rU-U zV4L8cVnhlN#<<%EgG6SUgf>Fiom}J~NmSgKHYwBfZ=!eio$tI44~GN)87+HIN`(RM zK6lrplwJD*lu~zrGQcaPTy%hIQ>oO?WHR|R7z}p*1yD*o1d0Jyj@CBtSxVUhp#NhT zK-jij0<>%ccFIom>}2nR5E@Xu4ahl$VfYQh@c$V#$U!|8iyezbqo;J2XsC*e6od-v zzFU@c9(a4GK|CHW`qFf^*3(?=>spKy=t9t8KK8g9FO*)N4-WwD0}V{mJmJko;~&Z zBcmq)$Ygll(8-UDO^y^7apmPB0MfRN4p`}Q+I9ju+OI8zj$YhKF0X*5QQp~#$6HAG zjaC3Yy{0OnIuy|=$p+QY-l#zy-q%q2`&6_vB z)ckn8Hsi3R1Rx+)<(YO^?b|e&4^XE*i59sHhPhHcTrg zJT4qu$(f3`gD01OJde6beJnFGExtoWJ2oK#_|cePj8S~_F(lSU5B>DYj56`weEcL?i8aKUtYWc5 zf+RV*MO~KcG4XNHW71Qss`~$}s~sQcFJ!OxL)DYxymdnL7JP3y&gY@yd>?`P5iT;N z`0N1;zKgzD(meanxDIERB`vZ8=Nr)WVCt5hm)tU3@i@GmK*JogTtn?bp=QN(j_)D7 zCQo$*yL<9gWPju04-GVDy>nq^#{d8T32;bRa{vGi!~g&e!~vBn4jTXf0m4Z{K~y-6 z#ge~k6hRb#zxQSqgF6A;y=_!3XcAFOX=5W25sy@MmV&8MX(yPou(PqTO6=2F`d9cT z1U;0%lKbi1u6V+;m|G3pACL#b3~y$>;eGSqyhGg64@NbDdWckGsK>}`0KaGd4(;td z-|^)C?UtvI0!iwS`b=TDB$8H z5Tz{p?Cd;1M3w-KhcCLK4n#ndxK;$fc@SmNKc(~dK5J`NDa!%#`5!hnuQQ!iIO)9V zA{yz=gBcA_L_wAEbj0_s-?(%82HV^BSYK~*eB9&Yq(pdMo77{dhER`?Y6$fNnf0Nb zKs7+B5mci%pOFjTNmelDdy_2BHxV+?4J{Hzv}Y*LM%jKyqk)C|&%_YJr6!f?b0D`Dm5FJmdMbj~s68 zBA*Sy6mcF?4^3~Vi#c(KdJr&mcz`S!aJaS0lP{lQVHX0XjTaX$i(B|n%te5xj}Dr6 zck3nA5%xFUK(Z1WEEKW!roqw-(j1yH&OQDD7NC~4P%M1K00000NkvXXu0mjfx=gEj literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Inv_SettingsSky.png b/indra/newview/skins/default/textures/Inv_SettingsSky.png new file mode 100644 index 0000000000000000000000000000000000000000..77858b23c312ab5aae88888672a34d967ffbb62c GIT binary patch literal 883 zcmV-(1C0EMP){0OnIuy|=$p+QY-l#zy-q%q2`&6_vB z)ckn8Hsi3R1Rx+)<(YO^?b|e&4^XE*i59sHhPhHcTrg zJT4qu$(f3`gD01OJde6beJnFGExtoWJ2oK#_|cePj8S~_F(lSU5B>DYj56`weEcL?i8aKUtYWc5 zf+RV*MO~KcG4XNHW71Qss`~$}s~sQcFJ!OxL)DYxymdnL7JP3y&gY@yd>?`P5iT;N z`0N1;zKgzD(meanxDIERB`vZ8=Nr)WVCt5hm)tU3@i@GmK*JogTtn?bp=QN(j_)D7 zCQo$*yL<9gWPju04-GVDy>nq^#{d8T32;bRa{vGi!~g&e!~vBn4jTXf0hmcdK~y-6 z)soGM6HyR_zq(boyVL1RCJl}<4w48mh~UD_z`VdLd=T+Pd?nqw^$Ao&QNf>y=_Z*l zll0$p(UEcEP7n_ibrw~eqRxT;9>UWXAx;v47*!WQJ*a_t4n_(0Z>78~8;-^?@0LPY z0TzKXaEj&mm?vissoIX$UoQ}Gy!yD}eBDqs{ZKYg(j;PLN;%(&I0f}sgU8firaYSF zfG|7Qo#!Nk!qZT8LgqtaRyX>E6_=L_W z%UYl_5CwIh3Kp@(*rMc_^8E^qGMJ9w{0h1N4vWCYUPt! literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/Inv_SettingsWater.png b/indra/newview/skins/default/textures/Inv_SettingsWater.png new file mode 100644 index 0000000000000000000000000000000000000000..46fb58c3f24add810048393a5abb95eed712ee15 GIT binary patch literal 764 zcmV{0OnIuy|=$p+QY-l#zy-q%q2`&6_vB z)ckn8Hsi3R1Rx+)<(YO^?b|e&4^XE*i59sHhPhHcTrg zJT4qu$(f3`gD01OJde6beJnFGExtoWJ2oK#_|cePj8S~_F(lSU5B>DYj56`weEcL?i8aKUtYWc5 zf+RV*MO~KcG4XNHW71Qss`~$}s~sQcFJ!OxL)DYxymdnL7JP3y&gY@yd>?`P5iT;N z`0N1;zKgzD(meanxDIERB`vZ8=Nr)WVCt5hm)tU3@i@GmK*JogTtn?bp=QN(j_)D7 zCQo$*yL<9gWPju04-GVDy>nq^#{d8T32;bRa{vGi!~g&e!~vBn4jTXf0U=35K~y-6 z#geZ}Ls1xppR-K`LFaIC(L0b~P=*>TcC8jQ8pMA<5NwKJSj6OyAZQeeE}D#M3nGRi zh_JGxLD04<_qANc-Q{|)h+jG9JiOm?z5@sTw5(P+*S;%a9L)p36W4eYD6?r=X|aq$ zz6bcU$q{h-Qz%_vH4SWa&Tq_etc~9ZtbE`G$ae8pE;!V(TVOQ?)Pczk8;sU3GQ3mc zNQ1bLJ$s2Bl_ zW*dgGaOFdf0SZ8WtYef1*Avb@LRe`*AUN?NT8d3mqKdPus#pmtO}$9ov&5amrNpCl ulkxPgY_$Mv19LzK+ye(xOx=C^f7CY)+fx87^wLiN0000|qj zUdZK(S~%!;EkqYxwr6Z~BYwd}ou0T_(@VKIC zMd2yo&{|P7d0V(k$;1-Er^0ScE+~9!>hUVSn4GTim&sZr8~}?`*p;s1cpWPlEhj85 zr!SXXRetYdzQmr)&(BHh74JbOOb|aVrkNzb2t$m?Jc0)wgR-JQzV|18hJxfOGOLu( zSRqY@B5qMvrFv3gO7x_REG4u5fBUMZhJDrQc0aT{8OB>b*tVcjHjK|h!>AmA^${-e zwfw>XjJ}Iru4#Sy;Mjn(%bFJ7f%8r1eo$jt>_@VSTT&@_J%NrWbY4T-QnhEl`yAgx zWL>`M3U>EkK+ZdszX3vQXVnq^QB42<010qNS#tmY3ljhU3ljkVnw%H_00KNoL_t(I zjfIn6Xi`xe$A9PC+wBf-X{|Q@wCk)8!OB0F&_gZAC?b08A!3c9Jr(q@f~|rOeHk&JIAk-d|Wf=!2#xEUT zdpGFP6U5R8vLZp@UzXI#p*s(pPUmtfh;q4H4vynEmSq6|;K}&d6@UNR!7~TeDANvt zIi`V~dIeoDm-JK9ky8fG*;g8kM$YH+Y&aa&Q9hsVpdbh!NfI;~4Z1rPcyxNdOl2jC zT%EDcPS?Dzi$A^}IcAcnLwix}&ayQoZ++wjT{TBcL zP$(3%*9@PB?0X4bSjOb~hrTK6(G$@A%{wru=A+d%H=ZFn zCz%yO2)5ZwFWnt{9d95A!oI~}d3$+8W~$Yyv6Bx2KoZuwYKs&_p;)>B6aqj9$tqb9 z0FupF2nK^&S~`=-^v=#i&X*+-8TtM%*mh7NkpQ*erv3uNRO_f) SXw2mR0000 + + + + + From 96306321202f3aa5f8f5638ecd93a4a0f04b8f3a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 20:17:33 -0500 Subject: [PATCH 176/332] Enable wearable export --- indra/newview/llfloatercustomize.cpp | 2 -- indra/newview/llviewerwearable.cpp | 7 ++++--- indra/newview/llviewerwearable.h | 4 +--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index ac33926bc..63aa904dd 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -503,7 +503,6 @@ void LLFloaterCustomize::onBtnExport() //static void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, AIFilePicker* filepicker) { -#if 0 if (!filepicker->hasFilename()) { // User canceled export. @@ -529,7 +528,6 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, { AIAlert::add_modal("AIXMLExportSuccess", AIArgs("[FILE]", filename)); } -#endif } void LLFloaterCustomize::onBtnOk() diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 6b9c37586..da7aa4794 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -42,6 +42,7 @@ #include "llinventoryfunctions.h" #include "lllocaltextureobject.h" #include "llpaneleditwearable.h" +#include "aixmllindengenepool.h" using namespace LLAvatarAppearanceDefines; @@ -135,8 +136,8 @@ LLWearable::EImportResult LLViewerWearable::importStream( std::istream& input_st return result; } -#if 0 -AIArchetype LLViewerWearable::getArchetype(void) const + +AIArchetype LLViewerWearable::getArchetype() const { AIArchetype archetype(this); for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); ++iter) @@ -149,7 +150,7 @@ AIArchetype LLViewerWearable::getArchetype(void) const } return archetype; } -#endif + // Avatar parameter and texture definitions can change over time. // This function returns true if parameters or textures have been added or removed // since this wearable was created. diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index 245753865..94f70fefa 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -68,9 +68,7 @@ public: /*virtual*/ EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp ); // Singu extension. -#if 0 - AIArchetype getArchetype(void) const; -#endif + class AIArchetype getArchetype() const; void setParamsToDefaults(); void setTexturesToDefaults(); From 1850cd159db34ee4f36a60ec713e6b93c46c7b77 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 20:23:21 -0500 Subject: [PATCH 177/332] My inventory patch everyone's been testing I'm still wary, but let's toss it into our release, shall we? This changes the maps in inventorymodel to unordered maps. On the surface this seems like a good thing, definitely is for inventory loading. It seemed a lot snappier to literally everyone before the HTTP Asset patch rolled out, so that's good. Thanks to Router, Bitten, Gooz, Deltek, Damian, Melaniecosti, Torric, and anyone else I might be forgetting at the moment who tested this for me! --- indra/newview/llinventorymodel.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index b64881f83..915dbc296 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -27,7 +27,7 @@ #ifndef LL_LLINVENTORYMODEL_H #define LL_LLINVENTORYMODEL_H -#include +#include #include #include #include @@ -154,13 +154,13 @@ private: // the inventory using several different identifiers. // mInventory member data is the 'master' list of inventory, and // mCategoryMap and mItemMap store uuid->object mappings. - typedef std::map > cat_map_t; - typedef std::map > item_map_t; + typedef std::unordered_map > cat_map_t; + typedef std::unordered_map > item_map_t; cat_map_t mCategoryMap; item_map_t mItemMap; // This last set of indices is used to map parents to children. - typedef std::map parent_cat_map_t; - typedef std::map parent_item_map_t; + typedef std::unordered_map parent_cat_map_t; + typedef std::unordered_map parent_item_map_t; parent_cat_map_t mParentChildCategoryTree; parent_item_map_t mParentChildItemTree; @@ -467,7 +467,7 @@ public: LLInitializedS32& operator++() { ++mValue; return *this; } LLInitializedS32& operator--() { --mValue; return *this; } }; - typedef std::map update_map_t; + typedef std::unordered_map update_map_t; // Call when there are category updates. Call them *before* the // actual update so the method can do descendent accounting correctly. @@ -592,8 +592,8 @@ protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); item_array_t* getUnlockedItemArray(const LLUUID& id); private: - std::map mCategoryLock; - std::map mItemLock; + std::unordered_map mCategoryLock; + std::unordered_map mItemLock; //-------------------------------------------------------------------- // Debugging From 28b1ed82a5799d72138b3d6737a343f16f20caa1 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 21:13:04 -0500 Subject: [PATCH 178/332] Steam Hardware Survey says 100% SSE3 It's been over a decade since SSE3 came out Almost 95% of CPUs on Steam have support for SSE4.2 Therefore we are enabling optimizations for SSE3 but someone could easily make an SSE2 Build --- indra/cmake/00-Common.cmake | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index fbbf1446f..49ed82f62 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -107,7 +107,7 @@ if (WINDOWS) endif (USE_LTO) if (WORD_SIZE EQUAL 32) - add_compile_options(/arch:SSE2) + add_compile_options(/arch:SSE3) endif (WORD_SIZE EQUAL 32) if (NOT DISABLE_FATAL_WARNINGS) @@ -212,23 +212,23 @@ if (LINUX) if (NOT STANDALONE) set(MARCH_FLAG " -march=pentium4") endif (NOT STANDALONE) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") endif (${ARCH} STREQUAL "x86_64") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -msse2") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -msse2") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse2") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse2") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -msse3") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -msse3") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse3") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse3") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") if (NOT STANDALONE) - set(MARCH_FLAG " -axsse4.1 -msse2") + set(MARCH_FLAG " -axsse4.1 -msse3") endif (NOT STANDALONE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline-functions") From 92fd0f56b47bb8af31eca964ad72a23237ce9255 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 21:51:44 -0500 Subject: [PATCH 179/332] In the modern era, if we don't recognize a GPU, give it full class 3 support and pray --- indra/newview/llfeaturemanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 42f46fb2e..096df5cde 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -421,6 +421,9 @@ void LLFeatureManager::parseGPUTable(std::string filename) else { LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL; + mGPUString = rawRenderer; + mGPUClass = EGPUClass::GPU_CLASS_3; + mGPUSupported = true; } } From c420751ffc26982fed5a7d1e1647785d3c4ab4c9 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 21:57:52 -0500 Subject: [PATCH 180/332] Get these boost warnings out of our compile logs! --- indra/cmake/00-Common.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 49ed82f62..e9868cfea 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -84,6 +84,7 @@ if (WINDOWS) /DNOMINMAX /DUNICODE /D_UNICODE + /DBOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE /GS /TP /W3 From b80be075d7dfea50329829865534b0354ca69f57 Mon Sep 17 00:00:00 2001 From: bittenbythedark Date: Fri, 1 Mar 2019 09:29:16 +0100 Subject: [PATCH 181/332] Add support for Partner URL on OpenSim Grids --- README.md | 1 + indra/newview/hippogridmanager.cpp | 11 +++++++++++ indra/newview/hippogridmanager.h | 5 ++++- indra/newview/hippopanelgrids.cpp | 10 ++++++++++ indra/newview/llpanelavatar.cpp | 12 ++++++++++-- .../xui/en-us/panel_preferences_grids.xml | 16 ++++++++++++++++ 6 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..30f944d4d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# singuosfork \ No newline at end of file diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 0ba0d92c4..00047a103 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -49,6 +49,7 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : mSupportUrl(LLStringUtil::null), mRegisterUrl(LLStringUtil::null), mPasswordUrl(LLStringUtil::null), + mPartnerUrl(LLStringUtil::null), mSearchUrl(LLStringUtil::null), mGridMessage(""), mXmlState(XML_VOID), @@ -220,6 +221,11 @@ void HippoGridInfo::setPasswordUrl(const std::string& url) mPasswordUrl = url; } +void HippoGridInfo::setPartnerUrl(const std::string& url) +{ + mPartnerUrl = url; +} + void HippoGridInfo::setSearchUrl(const std::string& url) { mSearchUrl = url; @@ -279,6 +285,8 @@ void HippoGridInfo::onXmlElementStart(void* userData, const XML_Char* name, cons self->mXmlState = XML_REGISTER; else if (strcasecmp(name, "password") == 0) self->mXmlState = XML_PASSWORD; + else if (strcasecmp(name, "partner") == 0) + self->mXmlState = XML_PARTNER; else if (strcasecmp(name, "search") == 0) self->mXmlState = XML_SEARCH; else if (strcasecmp(name, "message") == 0) @@ -348,6 +356,7 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le case XML_SUPPORT: self->mSupportUrl.assign(s, len); break; case XML_REGISTER: self->mRegisterUrl.assign(s, len); break; case XML_PASSWORD: self->mPasswordUrl.assign(s, len); break; + case XML_PARTNER: self->mPartnerUrl.assign(s, len); break; case XML_MESSAGE: self->mGridMessage.assign(s, len); break; case XML_VOID: break; @@ -894,6 +903,7 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer) if (gridMap.has("support")) grid->setSupportUrl(gridMap["support"]); if (gridMap.has("register")) grid->setRegisterUrl(gridMap["register"]); if (gridMap.has("password")) grid->setPasswordUrl(gridMap["password"]); + if (gridMap.has("partner")) grid->setPartnerUrl(gridMap["partner"]); if (gridMap.has("search")) grid->setSearchUrl(gridMap["search"]); if (gridMap.has("render_compat")) grid->setRenderCompat(gridMap["render_compat"]); if (gridMap.has("auto_update")) grid->mAutoUpdate = gridMap["auto_update"]; @@ -929,6 +939,7 @@ void HippoGridManager::saveFile() gridInfo[i]["support"] = grid->getSupportUrl(); gridInfo[i]["register"] = grid->getRegisterUrl(); gridInfo[i]["password"] = grid->getPasswordUrl(); + gridInfo[i]["partner"] = grid->getPartnerUrl(); gridInfo[i]["search"] = grid->getSearchUrl(); gridInfo[i]["render_compat"] = grid->isRenderCompat(); diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 73eeca2d7..73f8b45db 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -49,6 +49,7 @@ public: const std::string& getSupportUrl() const { return mSupportUrl; } const std::string& getRegisterUrl() const { return mRegisterUrl; } const std::string& getPasswordUrl() const { return mPasswordUrl; } + const std::string& getPartnerUrl() const { return mPartnerUrl; } // Returns the url base used for the Web Search tab const std::string& getSearchUrl() const { return mSearchUrl; } const std::string& getGridMessage() const { return mGridMessage; } @@ -76,6 +77,7 @@ public: void setSupportUrl(const std::string& url); void setRegisterUrl(const std::string& url); void setPasswordUrl(const std::string& url); + void setPartnerUrl(const std::string& url); // sets the url base used for the Web Search tab void setSearchUrl(const std::string& url); void setGridMessage(const std::string& message); @@ -112,6 +114,7 @@ private: std::string mSupportUrl; std::string mRegisterUrl; std::string mPasswordUrl; + std::string mPartnerUrl; std::string mSearchUrl; std::string mVoiceConnector; bool mIsInProductionGrid; @@ -134,7 +137,7 @@ private: { XML_VOID, XML_PLATFORM, XML_GRIDNAME, XML_GRIDNICK, XML_LOGINURI, XML_LOGINPAGE, XML_HELPERURI, - XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PASSWORD, XML_SEARCH, XML_MESSAGE + XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PASSWORD, XML_PARTNER, XML_SEARCH, XML_MESSAGE }; XmlState mXmlState; diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index d5570e84a..2b1024c33 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -131,6 +131,7 @@ BOOL HippoPanelGridsImpl::postBuild() requires("support"); requires("register"); requires("password"); + requires("partner"); requires("search"); requires("btn_delete"); requires("btn_add"); @@ -245,6 +246,7 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("search", gridInfo->getSearchUrl()); childSetText("register", gridInfo->getRegisterUrl()); childSetText("password", gridInfo->getPasswordUrl()); + childSetText("partner", gridInfo->getPartnerUrl()); childSetValue("render_compat", gridInfo->isRenderCompat()); enableEditing(!gridInfo->getLocked()); } else { @@ -260,6 +262,7 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("search", empty); childSetText("register", empty); childSetText("password", empty); + childSetText("partner", empty); childSetValue("render_compat", true); enableEditing(true); } @@ -362,6 +365,7 @@ bool HippoPanelGridsImpl::saveCurGrid() gridInfo->setSupportUrl(childGetValue("support")); gridInfo->setRegisterUrl(childGetValue("register")); gridInfo->setPasswordUrl(childGetValue("password")); + gridInfo->setPartnerUrl(childGetValue("partner")); gridInfo->setSearchUrl(childGetValue("search")); gridInfo->setRenderCompat(childGetValue("render_compat")); @@ -420,6 +424,7 @@ void HippoPanelGridsImpl::retrieveGridInfo() if (grid->getSupportUrl() != "") childSetText("support", grid->getSupportUrl()); if (grid->getRegisterUrl() != "") childSetText("register", grid->getRegisterUrl()); if (grid->getPasswordUrl() != "") childSetText("password", grid->getPasswordUrl()); + if (grid->getPartnerUrl() != "") childSetText("partner", grid->getPartnerUrl()); if (grid->getSearchUrl() != "") childSetText("search", grid->getSearchUrl()); if (grid->getGridMessage() != "") childSetText("gridmessage", grid->getGridMessage()); } @@ -534,6 +539,8 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("register", false); self->childSetVisible("password_label", false); self->childSetVisible("password", false); + self->childSetVisible("partner_label", false); + self->childSetVisible("partner", false); self->childSetVisible("search_label", false); self->childSetVisible("search", false); self->childSetVisible("render_compat", false); @@ -553,6 +560,8 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("register", true); self->childSetVisible("password_label", true); self->childSetVisible("password", true); + self->childSetVisible("partner_label", true); + self->childSetVisible("partner", true); self->childSetVisible("search_label", true); self->childSetVisible("search", true); self->childSetVisible("render_compat", true); @@ -579,6 +588,7 @@ void HippoPanelGridsImpl::enableEditing(bool b) "support", "register", "password", + "partner", "search", "btn_delete", "btn_gridinfo", diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index d1a431917..1f4d5ea53 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -70,6 +70,10 @@ #include #include +//Include Gridmanager for Profile +#include "hippogridmanager.h" +#include "hippopanelgrids.h" + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -299,11 +303,15 @@ void LLPanelAvatarSecondLife::onDoubleClickGroup() LLGroupActions::show(item->getUUID()); } -// static +// static - Not anymore :P bool LLPanelAvatarSecondLife::onClickPartnerHelpLoadURL(const LLSD& notification, const LLSD& response) { if (!LLNotification::getSelectedOption(notification, response)) - LLWeb::loadURL("http://secondlife.com/partner"); + { + const auto& grid = *gHippoGridManager->getConnectedGrid(); + const std::string url = grid.isSecondLife() ? "http://secondlife.com/partner" : grid.getPartnerUrl(); + if (!url.empty()) LLWeb::loadURL(url); + } return false; } diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml index ea53548f7..59f38f138 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml @@ -210,6 +210,22 @@ halign="right" font="SansSerifSmall" follows="left|top|right" border_visible="false" mouse_opaque="false" + drop_shadow_visible="true" border_drop_shadow_visible="false" /> + + + Partner URL: + + Date: Fri, 1 Mar 2019 16:23:30 +0100 Subject: [PATCH 182/332] do some magic EXPECTO PATRONUM --- README.md | 1 - indra/newview/hippogridmanager.cpp | 22 +++++++++---------- indra/newview/hippogridmanager.h | 8 +++---- indra/newview/hippopanelgrids.cpp | 20 ++++++++--------- indra/newview/lfsimfeaturehandler.cpp | 2 ++ indra/newview/lfsimfeaturehandler.h | 3 +++ indra/newview/llpanelavatar.cpp | 5 ++--- indra/newview/llpanelevent.cpp | 21 ++++++++++++++---- .../xui/en-us/panel_preferences_grids.xml | 19 +++------------- 9 files changed, 52 insertions(+), 49 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 30f944d4d..000000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# singuosfork \ No newline at end of file diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 00047a103..315b7cf8b 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -48,8 +48,8 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : mWebSite(LLStringUtil::null), mSupportUrl(LLStringUtil::null), mRegisterUrl(LLStringUtil::null), - mPasswordUrl(LLStringUtil::null), mPartnerUrl(LLStringUtil::null), + mPasswordUrl(LLStringUtil::null), mSearchUrl(LLStringUtil::null), mGridMessage(""), mXmlState(XML_VOID), @@ -216,16 +216,16 @@ void HippoGridInfo::setRegisterUrl(const std::string& url) mRegisterUrl = url; } -void HippoGridInfo::setPasswordUrl(const std::string& url) -{ - mPasswordUrl = url; -} - void HippoGridInfo::setPartnerUrl(const std::string& url) { mPartnerUrl = url; } +void HippoGridInfo::setPasswordUrl(const std::string& url) +{ + mPasswordUrl = url; +} + void HippoGridInfo::setSearchUrl(const std::string& url) { mSearchUrl = url; @@ -283,10 +283,10 @@ void HippoGridInfo::onXmlElementStart(void* userData, const XML_Char* name, cons self->mXmlState = XML_SUPPORT; else if ((strcasecmp(name, "register") == 0) || (strcasecmp(name, "account") == 0)) self->mXmlState = XML_REGISTER; - else if (strcasecmp(name, "password") == 0) - self->mXmlState = XML_PASSWORD; else if (strcasecmp(name, "partner") == 0) self->mXmlState = XML_PARTNER; + else if (strcasecmp(name, "password") == 0) + self->mXmlState = XML_PASSWORD; else if (strcasecmp(name, "search") == 0) self->mXmlState = XML_SEARCH; else if (strcasecmp(name, "message") == 0) @@ -355,8 +355,8 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le case XML_WEBSITE: self->mWebSite.assign(s, len); break; case XML_SUPPORT: self->mSupportUrl.assign(s, len); break; case XML_REGISTER: self->mRegisterUrl.assign(s, len); break; - case XML_PASSWORD: self->mPasswordUrl.assign(s, len); break; case XML_PARTNER: self->mPartnerUrl.assign(s, len); break; + case XML_PASSWORD: self->mPasswordUrl.assign(s, len); break; case XML_MESSAGE: self->mGridMessage.assign(s, len); break; case XML_VOID: break; @@ -902,8 +902,8 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer) if (gridMap.has("website")) grid->setWebSite(gridMap["website"]); if (gridMap.has("support")) grid->setSupportUrl(gridMap["support"]); if (gridMap.has("register")) grid->setRegisterUrl(gridMap["register"]); - if (gridMap.has("password")) grid->setPasswordUrl(gridMap["password"]); if (gridMap.has("partner")) grid->setPartnerUrl(gridMap["partner"]); + if (gridMap.has("password")) grid->setPasswordUrl(gridMap["password"]); if (gridMap.has("search")) grid->setSearchUrl(gridMap["search"]); if (gridMap.has("render_compat")) grid->setRenderCompat(gridMap["render_compat"]); if (gridMap.has("auto_update")) grid->mAutoUpdate = gridMap["auto_update"]; @@ -938,8 +938,8 @@ void HippoGridManager::saveFile() gridInfo[i]["website"] = grid->getWebSite(); gridInfo[i]["support"] = grid->getSupportUrl(); gridInfo[i]["register"] = grid->getRegisterUrl(); - gridInfo[i]["password"] = grid->getPasswordUrl(); gridInfo[i]["partner"] = grid->getPartnerUrl(); + gridInfo[i]["password"] = grid->getPasswordUrl(); gridInfo[i]["search"] = grid->getSearchUrl(); gridInfo[i]["render_compat"] = grid->isRenderCompat(); diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 73f8b45db..8d2ae9650 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -48,8 +48,8 @@ public: const std::string& getWebSite() const { return mWebSite; } const std::string& getSupportUrl() const { return mSupportUrl; } const std::string& getRegisterUrl() const { return mRegisterUrl; } - const std::string& getPasswordUrl() const { return mPasswordUrl; } const std::string& getPartnerUrl() const { return mPartnerUrl; } + const std::string& getPasswordUrl() const { return mPasswordUrl; } // Returns the url base used for the Web Search tab const std::string& getSearchUrl() const { return mSearchUrl; } const std::string& getGridMessage() const { return mGridMessage; } @@ -76,8 +76,8 @@ public: void setWebSite (const std::string& website); void setSupportUrl(const std::string& url); void setRegisterUrl(const std::string& url); - void setPasswordUrl(const std::string& url); void setPartnerUrl(const std::string& url); + void setPasswordUrl(const std::string& url); // sets the url base used for the Web Search tab void setSearchUrl(const std::string& url); void setGridMessage(const std::string& message); @@ -113,8 +113,8 @@ private: std::string mWebSite; std::string mSupportUrl; std::string mRegisterUrl; - std::string mPasswordUrl; std::string mPartnerUrl; + std::string mPasswordUrl; std::string mSearchUrl; std::string mVoiceConnector; bool mIsInProductionGrid; @@ -137,7 +137,7 @@ private: { XML_VOID, XML_PLATFORM, XML_GRIDNAME, XML_GRIDNICK, XML_LOGINURI, XML_LOGINPAGE, XML_HELPERURI, - XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PASSWORD, XML_PARTNER, XML_SEARCH, XML_MESSAGE + XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PARTNER, XML_PASSWORD, XML_SEARCH, XML_MESSAGE }; XmlState mXmlState; diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index 2b1024c33..45479a697 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -130,8 +130,8 @@ BOOL HippoPanelGridsImpl::postBuild() requires("website"); requires("support"); requires("register"); - requires("password"); requires("partner"); + requires("password"); requires("search"); requires("btn_delete"); requires("btn_add"); @@ -245,8 +245,8 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("support", gridInfo->getSupportUrl()); childSetText("search", gridInfo->getSearchUrl()); childSetText("register", gridInfo->getRegisterUrl()); - childSetText("password", gridInfo->getPasswordUrl()); childSetText("partner", gridInfo->getPartnerUrl()); + childSetText("password", gridInfo->getPasswordUrl()); childSetValue("render_compat", gridInfo->isRenderCompat()); enableEditing(!gridInfo->getLocked()); } else { @@ -261,8 +261,8 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("support", empty); childSetText("search", empty); childSetText("register", empty); - childSetText("password", empty); childSetText("partner", empty); + childSetText("password", empty); childSetValue("render_compat", true); enableEditing(true); } @@ -364,8 +364,8 @@ bool HippoPanelGridsImpl::saveCurGrid() gridInfo->setWebSite(childGetValue("website")); gridInfo->setSupportUrl(childGetValue("support")); gridInfo->setRegisterUrl(childGetValue("register")); - gridInfo->setPasswordUrl(childGetValue("password")); gridInfo->setPartnerUrl(childGetValue("partner")); + gridInfo->setPasswordUrl(childGetValue("password")); gridInfo->setSearchUrl(childGetValue("search")); gridInfo->setRenderCompat(childGetValue("render_compat")); @@ -423,8 +423,8 @@ void HippoPanelGridsImpl::retrieveGridInfo() if (grid->getWebSite() != "") childSetText("website", grid->getWebSite()); if (grid->getSupportUrl() != "") childSetText("support", grid->getSupportUrl()); if (grid->getRegisterUrl() != "") childSetText("register", grid->getRegisterUrl()); - if (grid->getPasswordUrl() != "") childSetText("password", grid->getPasswordUrl()); if (grid->getPartnerUrl() != "") childSetText("partner", grid->getPartnerUrl()); + if (grid->getPasswordUrl() != "") childSetText("password", grid->getPasswordUrl()); if (grid->getSearchUrl() != "") childSetText("search", grid->getSearchUrl()); if (grid->getGridMessage() != "") childSetText("gridmessage", grid->getGridMessage()); } @@ -537,10 +537,10 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("support", false); self->childSetVisible("register_label", false); self->childSetVisible("register", false); - self->childSetVisible("password_label", false); - self->childSetVisible("password", false); self->childSetVisible("partner_label", false); self->childSetVisible("partner", false); + self->childSetVisible("password_label", false); + self->childSetVisible("password", false); self->childSetVisible("search_label", false); self->childSetVisible("search", false); self->childSetVisible("render_compat", false); @@ -558,10 +558,10 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("support", true); self->childSetVisible("register_label", true); self->childSetVisible("register", true); - self->childSetVisible("password_label", true); - self->childSetVisible("password", true); self->childSetVisible("partner_label", true); self->childSetVisible("partner", true); + self->childSetVisible("password_label", true); + self->childSetVisible("password", true); self->childSetVisible("search_label", true); self->childSetVisible("search", true); self->childSetVisible("render_compat", true); @@ -587,8 +587,8 @@ void HippoPanelGridsImpl::enableEditing(bool b) "website", "support", "register", - "password", "partner", + "password", "search", "btn_delete", "btn_gridinfo", diff --git a/indra/newview/lfsimfeaturehandler.cpp b/indra/newview/lfsimfeaturehandler.cpp index 65d1e6e55..dbdf2e5de 100644 --- a/indra/newview/lfsimfeaturehandler.cpp +++ b/indra/newview/lfsimfeaturehandler.cpp @@ -92,6 +92,7 @@ void LFSimFeatureHandler::setSupportedFeatures() mGridName = gHippoGridManager->getConnectedGrid()->getGridName() != grid_name ? grid_name : LLStringUtil::null; } } + has_feature_or_default(mEventsURL, extras, "EventsURL"); has_feature_or_default(mSayRange, extras, "say-range"); has_feature_or_default(mShoutRange, extras, "shout-range"); has_feature_or_default(mWhisperRange, extras, "whisper-range"); @@ -105,6 +106,7 @@ void LFSimFeatureHandler::setSupportedFeatures() mMapServerURL = LLStringUtil::null; mSearchURL.reset(); mGridName.reset(); + mEventsURL.reset(); } mSayRange.reset(); mShoutRange.reset(); diff --git a/indra/newview/lfsimfeaturehandler.h b/indra/newview/lfsimfeaturehandler.h index 893edb5a0..9b7554e32 100644 --- a/indra/newview/lfsimfeaturehandler.h +++ b/indra/newview/lfsimfeaturehandler.h @@ -65,6 +65,7 @@ public: boost::signals2::connection setSupportsExportCallback(const SignaledType::slot_t& slot) { return mSupportsExport.connect(slot); } boost::signals2::connection setDestinationGuideURLCallback(const SignaledType::slot_t& slot) { return mDestinationGuideURL.connect(slot); } boost::signals2::connection setSearchURLCallback(const SignaledType::slot_t& slot) { return mSearchURL.connect(slot); } + boost::signals2::connection setEventsURLCallback(const SignaledType::slot_t& slot) { return mEventsURL.connect(slot); } boost::signals2::connection setSayRangeCallback(const SignaledType::slot_t& slot) { return mSayRange.connect(slot); } boost::signals2::connection setShoutRangeCallback(const SignaledType::slot_t& slot) { return mShoutRange.connect(slot); } boost::signals2::connection setWhisperRangeCallback(const SignaledType::slot_t& slot) { return mWhisperRange.connect(slot); } @@ -74,6 +75,7 @@ public: std::string destinationGuideURL() const { return mDestinationGuideURL; } std::string mapServerURL() const { return mMapServerURL; } std::string searchURL() const { return mSearchURL; } + const std::string& getEventsURL() const { return mEventsURL.ref(); } const std::string& gridName() const { return mGridName.ref(); } U32 sayRange() const { return mSayRange; } U32 shoutRange() const { return mShoutRange; } @@ -87,6 +89,7 @@ private: std::string mMapServerURL; SignaledType mSearchURL; SignaledType mGridName; + SignaledType mEventsURL; SignaledType mSayRange; SignaledType mShoutRange; SignaledType mWhisperRange; diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 1f4d5ea53..09ab17196 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -70,9 +70,8 @@ #include #include -//Include Gridmanager for Profile -#include "hippogridmanager.h" -#include "hippopanelgrids.h" + +#include "hippogridmanager.h" // Include Gridmanager for OpenSim Support in profiles, provides ability for the helpbutton to redirect to GRID Websites Help page // [RLVa:KB] #include "rlvhandler.h" diff --git a/indra/newview/llpanelevent.cpp b/indra/newview/llpanelevent.cpp index 5295d97b8..21efbafd9 100644 --- a/indra/newview/llpanelevent.cpp +++ b/indra/newview/llpanelevent.cpp @@ -54,6 +54,8 @@ #include "llweb.h" #include "llworldmap.h" #include "lluictrlfactory.h" +#include "hippogridmanager.h" +#include "lfsimfeaturehandler.h" //static std::list LLPanelEvent::sAllPanels; @@ -69,6 +71,10 @@ LLPanelEvent::~LLPanelEvent() sAllPanels.remove(this); } +void enable_create(const std::string& url, LLView* btn) +{ + btn->setEnabled(!url.empty()); +} BOOL LLPanelEvent::postBuild() { @@ -97,8 +103,15 @@ BOOL LLPanelEvent::postBuild() mNotifyBtn = getChild( "notify_btn"); mNotifyBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickNotify,this)); - mCreateEventBtn = getChild( "create_event_btn"); - mCreateEventBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickCreateEvent,this)); + mCreateEventBtn = getChild("create_event_btn"); + mCreateEventBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickCreateEvent, this)); + if (!gHippoGridManager->getConnectedGrid()->isSecondLife()) + { + auto& inst(LFSimFeatureHandler::instance()); + mCreateEventBtn->setEnabled(!inst.getEventsURL().empty()); + inst.setEventsURLCallback(boost::bind(enable_create, _1, mCreateEventBtn)); + } + return TRUE; } @@ -286,8 +299,8 @@ bool LLPanelEvent::callbackCreateEventWebPage(const LLSD& notification, const LL if (0 == option) { LL_INFOS() << "Loading events page " << EVENTS_URL << LL_ENDL; - - LLWeb::loadURL(EVENTS_URL); + const std::string& opensim_events = LFSimFeatureHandler::instance().getEventsURL(); + LLWeb::loadURL(opensim_events.empty() ? EVENTS_URL : opensim_events); } return false; } diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml index 59f38f138..ef6d5c312 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_grids.xml @@ -211,22 +211,9 @@ font="SansSerifSmall" follows="left|top|right" border_visible="false" mouse_opaque="false" drop_shadow_visible="true" border_drop_shadow_visible="false" /> - - - Partner URL: - - + + Partner URL: + Date: Fri, 1 Mar 2019 16:00:30 -0500 Subject: [PATCH 183/332] Set Text Editor Enabled before loading text into it --- indra/newview/llpreviewnotecard.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 64248f970..f191b3368 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -388,7 +388,11 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, // put a EOS at the end buffer[file_length] = 0; - + // Singu Note: Set Enabled first, it determines whether or not our text will be linked + const LLInventoryItem* item = preview->getItem(); + BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY, + item->getPermissions(), GP_OBJECT_MANIPULATE); + preview->setEnabled(modifiable); if (LLViewerTextEditor* previewEditor = preview->findChild("Notecard Editor")) { if ((file_length > 19) && !strncmp(buffer, "Linden text version", 19)) @@ -406,11 +410,6 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, previewEditor->makePristine(); } - - const LLInventoryItem* item = preview->getItem(); - BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY, - item->getPermissions(), GP_OBJECT_MANIPULATE); - preview->setEnabled(modifiable); delete[] buffer; preview->mAssetStatus = PREVIEW_ASSET_LOADED; } From f6cf4e2878f12fedde5ae44a3d445b38961678a4 Mon Sep 17 00:00:00 2001 From: Damian Zhaoying Date: Sat, 2 Mar 2019 02:57:10 -0300 Subject: [PATCH 184/332] Big Update in Spanish language Files --- .../en-us/floater_inventory_view_finder.xml | 8 +- .../skins/default/xui/es/floater_ao.xml | 25 +++- .../default/xui/es/floater_customize.xml | 104 +++++++++++----- .../xui/es/floater_instant_message.xml | 4 +- .../xui/es/floater_instant_message_ad_hoc.xml | 1 + ..._instant_message_ad_hoc_concisebuttons.xml | 3 +- ...floater_instant_message_concisebuttons.xml | 3 +- .../xui/es/floater_inventory_view_finder.xml | 15 ++- .../default/xui/es/floater_top_objects.xml | 1 + .../skins/default/xui/es/menu_url_agent.xml | 4 +- .../skins/default/xui/es/menu_url_email.xml | 1 + .../default/xui/es/menu_url_experience.xml | 2 + .../skins/default/xui/es/menu_url_group.xml | 4 +- .../skins/default/xui/es/menu_url_http.xml | 5 +- .../default/xui/es/menu_url_inventory.xml | 1 + .../skins/default/xui/es/menu_url_map.xml | 5 +- .../default/xui/es/menu_url_objectim.xml | 4 +- .../skins/default/xui/es/menu_url_parcel.xml | 7 +- .../skins/default/xui/es/menu_url_slapp.xml | 3 +- .../skins/default/xui/es/menu_url_slurl.xml | 7 +- .../default/xui/es/menu_url_teleport.xml | 5 +- .../skins/default/xui/es/menu_viewer.xml | 6 +- .../skins/default/xui/es/notifications.xml | 52 +++++++- .../es/panel_preferences_ascent_system.xml | 1 + .../es/panel_preferences_ascent_vanity.xml | 6 +- .../xui/es/panel_prim_media_controls.xml | 6 + .../newview/skins/default/xui/es/strings.xml | 117 +++++++++++------- .../xui/es/wlfPanel_AdvSettings_expanded.xml | 5 +- 28 files changed, 296 insertions(+), 109 deletions(-) diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory_view_finder.xml b/indra/newview/skins/default/xui/en-us/floater_inventory_view_finder.xml index 807818698..798d49417 100644 --- a/indra/newview/skins/default/xui/en-us/floater_inventory_view_finder.xml +++ b/indra/newview/skins/default/xui/en-us/floater_inventory_view_finder.xml @@ -76,11 +76,11 @@ - - -