summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autobuild.xml88
-rwxr-xr-xdoc/contributions.txt1
-rw-r--r--indra/llaudio/llaudioengine.cpp2
-rw-r--r--indra/llcommon/llevents.cpp20
-rw-r--r--indra/llcommon/llevents.h5
-rw-r--r--indra/llcommon/llmemory.cpp30
-rw-r--r--indra/llmessage/lldatapacker.cpp7
-rw-r--r--indra/llui/llfiltereditor.h1
-rw-r--r--indra/llui/llfloater.cpp2
-rw-r--r--indra/llui/llnotifications.cpp41
-rw-r--r--indra/llui/llnotifications.h36
-rw-r--r--indra/llui/llnotificationslistener.cpp10
-rw-r--r--indra/llui/llsearcheditor.cpp8
-rw-r--r--indra/llui/llsearcheditor.h2
-rw-r--r--indra/llui/lltooltip.cpp10
-rw-r--r--indra/llwindow/lldxhardware.cpp22
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llagent.cpp4
-rw-r--r--indra/newview/llaisapi.cpp16
-rw-r--r--indra/newview/llappviewer.cpp2
-rw-r--r--indra/newview/llchiclet.cpp5
-rw-r--r--indra/newview/llchiclet.h11
-rw-r--r--indra/newview/llfloaterinventorythumbnailshelper.cpp543
-rw-r--r--indra/newview/llfloaterinventorythumbnailshelper.h82
-rw-r--r--indra/newview/llfloatermarketplacelistings.cpp25
-rw-r--r--indra/newview/llfloatermarketplacelistings.h4
-rwxr-xr-xindra/newview/llfloaterworldmap.cpp1
-rw-r--r--indra/newview/llinspecttexture.cpp5
-rw-r--r--indra/newview/llinventorybridge.cpp9
-rw-r--r--indra/newview/llinventoryfilter.cpp12
-rw-r--r--indra/newview/llinventoryfunctions.cpp25
-rw-r--r--indra/newview/llinventorygallery.cpp28
-rw-r--r--indra/newview/llinventorymodel.cpp20
-rw-r--r--indra/newview/llinventorymodelbackgroundfetch.cpp118
-rw-r--r--indra/newview/llinventorymodelbackgroundfetch.h4
-rw-r--r--indra/newview/llinventoryobserver.cpp1
-rw-r--r--indra/newview/llmachineid.cpp4
-rw-r--r--indra/newview/llmarketplacefunctions.cpp52
-rw-r--r--indra/newview/lloutfitgallery.cpp12
-rw-r--r--indra/newview/lloutfitgallery.h6
-rw-r--r--indra/newview/lloutfitslist.cpp83
-rw-r--r--indra/newview/lloutfitslist.h27
-rw-r--r--indra/newview/llpanelteleporthistory.cpp9
-rw-r--r--indra/newview/llperfstats.cpp4
-rw-r--r--indra/newview/llplacesinventorypanel.cpp10
-rw-r--r--indra/newview/llplacesinventorypanel.h7
-rw-r--r--indra/newview/llviewerfloaterreg.cpp5
-rw-r--r--indra/newview/llviewerinventory.cpp15
-rw-r--r--indra/newview/llviewerinventory.h6
-rw-r--r--indra/newview/llviewermessage.cpp6
-rw-r--r--indra/newview/llviewerobjectlist.cpp1
-rw-r--r--indra/newview/llvoavatarself.cpp6
-rw-r--r--indra/newview/skins/default/xui/en/floater_inventory_thumbnails_helper.xml99
-rw-r--r--indra/newview/skins/default/xui/en/menu_gallery_inventory.xml6
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml94
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml6
-rw-r--r--indra/newview/skins/default/xui/en/menu_outfit_gear.xml138
-rw-r--r--indra/newview/skins/default/xui/en/menu_outfit_tab.xml37
-rw-r--r--indra/newview/skins/default/xui/en/menu_teleport_history_item.xml13
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml15
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml44
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_water.xml2
-rw-r--r--scripts/code_tools/modified_strings.py8
63 files changed, 1508 insertions, 409 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 0e5a5a2a25..ef1b840bfc 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -408,11 +408,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>f6835c4d7745cd1cadfbce47b40331d08affb532</string>
+ <string>e03eb77224290c875ff84f75b7fe3d0e7c162c94</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-dictionaries/releases/download/v1.0.1-dev2.gf887629-f887629/dictionaries-common-None.tar.zst</string>
+ <string>https://github.com/secondlife/3p-dictionaries/releases/download/v1-a01bb6c/dictionaries-1.a01bb6c-common-a01bb6c.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
@@ -425,7 +425,7 @@
<key>copyright</key>
<string>Copyright 2014 Apache OpenOffice software</string>
<key>version</key>
- <string>None</string>
+ <string>1.a01bb6c</string>
<key>name</key>
<string>dictionaries</string>
<key>description</key>
@@ -548,11 +548,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>fb6797ff93b6e881b060d2a8b396d8d7477834ee</string>
+ <string>a2074b67de7ad4c04b5ca8f8f161506add9697b2</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/108908444</string>
+ <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207589</string>
</map>
<key>name</key>
<string>darwin64</string>
@@ -564,11 +564,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>a378bd1604aa97ca763140911f9f4e463ced85c0</string>
+ <string>8c1b701648c077220dbc576c3d9aefbef47f8324</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/108908446</string>
+ <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207592</string>
</map>
<key>name</key>
<string>linux64</string>
@@ -580,11 +580,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>72304491d86bd797b840999b255358f195b06609</string>
+ <string>7e0c3d50e8b99d8735c6c9596a72ded9ee2bc1c8</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/108908456</string>
+ <string>https://api.github.com/repos/secondlife/3p-fmodstudio/releases/assets/149207594</string>
</map>
<key>name</key>
<string>windows64</string>
@@ -597,7 +597,7 @@
<key>copyright</key>
<string>FMOD Studio by Firelight Technologies Pty Ltd.</string>
<key>version</key>
- <string>2.02.13.578928</string>
+ <string>2.02.20.c78ef55</string>
<key>name</key>
<string>fmodstudio</string>
<key>description</key>
@@ -734,11 +734,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>6604c1cca515d287e697997a8d5593d1cae172a9</string>
+ <string>066625e7aa7f697a4b6cd461aad960c57181011f</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-glh_linear/releases/download/v1.0.1-dev2.g3253ed7-3253ed7/glh_linear-common-None.tar.zst</string>
+ <string>https://github.com/secondlife/3p-glh_linear/releases/download/v1.0.1-dev4-984c397/glh_linear-1.0.1-dev4-common-984c397.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
@@ -751,7 +751,7 @@
<key>copyright</key>
<string>Copyright (c) 2000 Cass Everitt</string>
<key>version</key>
- <string>None</string>
+ <string>1.0.1-dev4</string>
<key>name</key>
<string>glh_linear</string>
<key>description</key>
@@ -882,11 +882,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>a193ff65d6db48626d65d96c6124c6efca85e8ec</string>
+ <string>ae2c2a215b1bc2e3f37a67e301926dc405902d1a</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/108912596</string>
+ <string>https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/136778143</string>
</map>
<key>name</key>
<string>darwin64</string>
@@ -910,11 +910,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>ebfb82b6143874e7938b9d1e8a70d0a2e28aa818</string>
+ <string>0393dd75c58f7046bed47e62a8884a78cb02a5c3</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/108912599</string>
+ <string>https://api.github.com/repos/secondlife/3p-havok-source/releases/assets/136778145</string>
</map>
<key>name</key>
<string>windows64</string>
@@ -1120,11 +1120,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>bcc7e2c34896fc9cbc41828dee8a4ddf54f10453</string>
+ <string>ad72fa1d103df777906f0d98f3e882b9916aeada</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/108298968</string>
+ <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/136774118</string>
</map>
<key>name</key>
<string>darwin64</string>
@@ -1136,11 +1136,11 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>9de772df2ed12e9c742df6c90670c7cbbb9c93a6</string>
+ <string>e46e4ac93a237b5c4a14183766f76ba5d58935a2</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/108298969</string>
+ <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/136774125</string>
</map>
<key>name</key>
<string>linux64</string>
@@ -1152,15 +1152,31 @@
<key>creds</key>
<string>github</string>
<key>hash</key>
- <string>92533ff0f8c1881ad85e75800f9072c413ccf7b7</string>
+ <string>bb37557f78c72b26580a521f8b8dabfa1b34e6e6</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/108298970</string>
+ <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/136774126</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
+ <key>linux</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>creds</key>
+ <string>github</string>
+ <key>hash</key>
+ <string>711b82f9f588d3a125af7dcd8c81f93d9c343a7d</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://api.github.com/repos/secondlife/3p-kdu/releases/assets/136774121</string>
+ </map>
+ <key>name</key>
+ <string>linux</string>
+ </map>
</map>
<key>license</key>
<string>Kakadu</string>
@@ -1169,7 +1185,7 @@
<key>copyright</key>
<string>Kakadu software</string>
<key>version</key>
- <string>7.10.4.539108</string>
+ <string>7.10.4.4b9ec5f</string>
<key>name</key>
<string>kdu</string>
<key>description</key>
@@ -1461,6 +1477,15 @@
</map>
<key>llca</key>
<map>
+ <key>copyright</key>
+ <string>Copyright (c) 2016, Linden Research, Inc.; data provided by the Mozilla NSS Project.
+ </string>
+ <key>license</key>
+ <string>mit</string>
+ <key>license_file</key>
+ <string>LICENSES/ca-license.txt</string>
+ <key>name</key>
+ <string>llca</string>
<key>platforms</key>
<map>
<key>common</key>
@@ -1468,27 +1493,18 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>e50ea94bbaa4ff41bf53b84b7192df1a694c5337</string>
+ <string>a9503e1b4e1d9790cf29d18a3d9ab39e6a515679</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/llca/releases/download/v202310121525.0-d22bd98/llca-202310121530.0-common-d22bd98.tar.zst</string>
+ <string>https://github.com/secondlife/llca/releases/download/v202402012004.0-0f5d9c3/llca-202402012004.0-common-0f5d9c3.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
</map>
</map>
- <key>license</key>
- <string>mit</string>
- <key>license_file</key>
- <string>LICENSES/ca-license.txt</string>
- <key>copyright</key>
- <string>Copyright (c) 2016, Linden Research, Inc.; data provided by the Mozilla NSS Project.
- </string>
<key>version</key>
- <string>202310121530.0</string>
- <key>name</key>
- <string>llca</string>
+ <string>202402012004.0</string>
</map>
<key>llphysicsextensions_source</key>
<map>
diff --git a/doc/contributions.txt b/doc/contributions.txt
index a097aad7f6..14d85b5e23 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -844,6 +844,7 @@ Kadah Coba
Jondan Lundquist
Joosten Briebers
MAINT-7074
+ BUG-225288
Josef Munster
Josette Windlow
Juilan Tripsa
diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp
index a387bb23cd..ece0a12a7a 100644
--- a/indra/llaudio/llaudioengine.cpp
+++ b/indra/llaudio/llaudioengine.cpp
@@ -398,7 +398,7 @@ void LLAudioEngine::idle()
for (source_map::value_type& src_pair : mAllSources)
{
LLAudioSource *sourcep = src_pair.second;
- if (sourcep->isMuted() && sourcep->isSyncMaster() && sourcep->getPriority() > max_sm_priority)
+ if (!sourcep->isMuted() && sourcep->isSyncMaster() && sourcep->getPriority() > max_sm_priority)
{
sync_masterp = sourcep;
master_channelp = sync_masterp->getChannel();
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 1a305ec3dc..70931f3a65 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -211,12 +211,21 @@ void LLEventPumps::clear()
}
}
-void LLEventPumps::reset()
+void LLEventPumps::reset(bool log_pumps)
{
// Reset every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the reset() call.
+ if (log_pumps)
+ {
+ LL_INFOS() << "Resetting " << (S32)mPumpMap.size() << " pumps" << LL_ENDL;
+ }
+
for (PumpMap::value_type& pair : mPumpMap)
{
+ if (log_pumps)
+ {
+ LL_INFOS() << "Resetting pump " << pair.first << LL_ENDL;
+ }
pair.second->reset();
}
}
@@ -373,9 +382,11 @@ std::string LLEventPump::inventName(const std::string& pfx)
void LLEventPump::clear()
{
+ LLMutexLock lock(&mConnectionListMutex);
// Destroy the original LLStandardSignal instance, replacing it with a
// whole new one.
mSignal = std::make_shared<LLStandardSignal>();
+
mConnections.clear();
}
@@ -383,6 +394,7 @@ void LLEventPump::reset()
{
// Resetting mSignal is supposed to disconnect everything on its own
// But due to crash on 'reset' added explicit cleanup to get more data
+ LLMutexLock lock(&mConnectionListMutex);
ConnectionMap::const_iterator iter = mConnections.begin();
ConnectionMap::const_iterator end = mConnections.end();
while (iter!=end)
@@ -407,6 +419,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
return LLBoundListener();
}
+ LLMutexLock lock(&mConnectionListMutex);
+
float nodePosition = 1.0;
// if the supplied name is empty we are not interested in the ordering mechanism
@@ -566,8 +580,9 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
return bound;
}
-LLBoundListener LLEventPump::getListener(const std::string& name) const
+LLBoundListener LLEventPump::getListener(const std::string& name)
{
+ LLMutexLock lock(&mConnectionListMutex);
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
@@ -579,6 +594,7 @@ LLBoundListener LLEventPump::getListener(const std::string& name) const
void LLEventPump::stopListening(const std::string& name)
{
+ LLMutexLock lock(&mConnectionListMutex);
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index c1dbf4392f..bebcfacdcb 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -332,7 +332,7 @@ public:
* Reset all known LLEventPump instances
* workaround for DEV-35406 crash on shutdown
*/
- void reset();
+ void reset(bool log_pumps = false);
private:
friend class LLEventPump;
@@ -558,7 +558,7 @@ public:
/// Get the LLBoundListener associated with the passed name (dummy
/// LLBoundListener if not found)
- virtual LLBoundListener getListener(const std::string& name) const;
+ virtual LLBoundListener getListener(const std::string& name);
/**
* Instantiate one of these to block an existing connection:
* @code
@@ -601,6 +601,7 @@ private:
LLHandle<LLEventPumps> mRegistry;
std::string mName;
+ LLMutex mConnectionListMutex;
protected:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 7cdf7254ff..574b9b8b3b 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -38,6 +38,7 @@
#include <mach/mach_host.h>
#elif LL_LINUX
# include <unistd.h>
+# include <sys/resource.h>
#endif
#include "llmemory.h"
@@ -273,33 +274,16 @@ U64 LLMemory::getCurrentRSS()
U64 LLMemory::getCurrentRSS()
{
- static const char statPath[] = "/proc/self/stat";
- LLFILE *fp = LLFile::fopen(statPath, "r");
- U64 rss = 0;
+ struct rusage usage;
- if (fp == NULL)
- {
- LL_WARNS() << "couldn't open " << statPath << LL_ENDL;
+ if (getrusage(RUSAGE_SELF, &usage) != 0) {
+ // Error handling code could be here
return 0;
}
- // Eee-yew! See Documentation/filesystems/proc.txt in your
- // nearest friendly kernel tree for details.
-
- {
- int ret = fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*d %*d "
- "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %Lu",
- &rss);
- if (ret != 1)
- {
- LL_WARNS() << "couldn't parse contents of " << statPath << LL_ENDL;
- rss = 0;
- }
- }
-
- fclose(fp);
-
- return rss;
+ // ru_maxrss (since Linux 2.6.32)
+ // This is the maximum resident set size used (in kilobytes).
+ return usage.ru_maxrss * 1024;
}
#else
diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp
index 9f7768f78e..b7013dbb6e 100644
--- a/indra/llmessage/lldatapacker.cpp
+++ b/indra/llmessage/lldatapacker.cpp
@@ -298,6 +298,13 @@ BOOL LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char
}
htolememcpy(&size, mCurBufferp, MVT_S32, 4);
+
+ if (size < 0)
+ {
+ LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData unpacked invalid size, aborting!" << LL_ENDL;
+ return FALSE;
+ }
+
mCurBufferp += 4;
if (!verifyLength(size, name))
diff --git a/indra/llui/llfiltereditor.h b/indra/llui/llfiltereditor.h
index 3a05bc05a1..52cad3bff4 100644
--- a/indra/llui/llfiltereditor.h
+++ b/indra/llui/llfiltereditor.h
@@ -43,6 +43,7 @@ class LLFilterEditor : public LLSearchEditor
public:
struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
{};
+ virtual ~LLFilterEditor() {}
protected:
LLFilterEditor(const Params&);
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 2303cd24b7..c67db54ea6 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1772,6 +1772,8 @@ void LLFloater::onClickTearOff(LLFloater* self)
{
if (self->mSaveRect)
{
+ LLRect screen_rect = self->calcScreenRect();
+ self->mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
self->storeRectControl();
}
self->setMinimized(FALSE); // to reenable minimize button if it was minimized
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index d736aa6634..d87241a9bf 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -994,6 +994,7 @@ LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListe
// all of the notifications that are already in the channel
// we use a special signal called "load" in case the channel wants to care
// only about new notifications
+ LLMutexLock lock(&mItemsMutex);
for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
{
slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
@@ -1171,29 +1172,33 @@ LLNotificationChannel::LLNotificationChannel(const std::string& name,
connectToChannel(parent);
}
-bool LLNotificationChannel::isEmpty() const
+LLNotificationChannel::~LLNotificationChannel()
{
- return mItems.empty();
+ for (LLBoundListener &listener : mListeners)
+ {
+ listener.disconnect();
+ }
}
-S32 LLNotificationChannel::size() const
+bool LLNotificationChannel::isEmpty() const
{
- return mItems.size();
+ return mItems.empty();
}
-LLNotificationChannel::Iterator LLNotificationChannel::begin()
+S32 LLNotificationChannel::size() const
{
- return mItems.begin();
+ return mItems.size();
}
-LLNotificationChannel::Iterator LLNotificationChannel::end()
+size_t LLNotificationChannel::size()
{
- return mItems.end();
+ return mItems.size();
}
-size_t LLNotificationChannel::size()
+void LLNotificationChannel::forEachNotification(NotificationProcess process)
{
- return mItems.size();
+ LLMutexLock lock(&mItemsMutex);
+ std::for_each(mItems.begin(), mItems.end(), process);
}
std::string LLNotificationChannel::summarize()
@@ -1201,7 +1206,8 @@ std::string LLNotificationChannel::summarize()
std::string s("Channel '");
s += mName;
s += "'\n ";
- for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it)
+ LLMutexLock lock(&mItemsMutex);
+ for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it)
{
s += (*it)->summarize();
s += "\n ";
@@ -1213,14 +1219,14 @@ void LLNotificationChannel::connectToChannel( const std::string& channel_name )
{
if (channel_name.empty())
{
- LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ mListeners.push_back(LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
}
else
{
mParents.push_back(channel_name);
LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
- p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
}
}
@@ -1728,6 +1734,7 @@ void LLNotifications::cancel(LLNotificationPtr pNotif)
void LLNotifications::cancelByName(const std::string& name)
{
+ LLMutexLock lock(&mItemsMutex);
std::vector<LLNotificationPtr> notifs_to_cancel;
for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
it != end_it;
@@ -1752,6 +1759,7 @@ void LLNotifications::cancelByName(const std::string& name)
void LLNotifications::cancelByOwner(const LLUUID ownerId)
{
+ LLMutexLock lock(&mItemsMutex);
std::vector<LLNotificationPtr> notifs_to_cancel;
for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end();
it != end_it;
@@ -1799,11 +1807,6 @@ LLNotificationPtr LLNotifications::find(LLUUID uuid)
}
}
-void LLNotifications::forEachNotification(NotificationProcess process)
-{
- std::for_each(mItems.begin(), mItems.end(), process);
-}
-
std::string LLNotifications::getGlobalString(const std::string& key) const
{
GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 921398a693..4d71d189f2 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -738,16 +738,19 @@ class LLNotificationChannelBase :
{
LOG_CLASS(LLNotificationChannelBase);
public:
- LLNotificationChannelBase(LLNotificationFilter filter)
- : mFilter(filter),
- mItems()
- {}
+ LLNotificationChannelBase(LLNotificationFilter filter)
+ : mFilter(filter)
+ , mItems()
+ , mItemsMutex()
+ {}
+
virtual ~LLNotificationChannelBase()
{
// explicit cleanup for easier issue detection
mChanged.disconnect_all_slots();
mPassedFilter.disconnect_all_slots();
mFailedFilter.disconnect_all_slots();
+ LLMutexLock lock(&mItemsMutex);
mItems.clear();
}
// you can also connect to a Channel, so you can be notified of
@@ -786,6 +789,7 @@ protected:
LLStandardSignal mChanged;
LLStandardSignal mPassedFilter;
LLStandardSignal mFailedFilter;
+ LLMutex mItemsMutex;
// these are action methods that subclasses can override to take action
// on specific types of changes; the management of the mItems list is
@@ -835,7 +839,7 @@ public:
LLNotificationChannel(const Params& p = Params());
LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter);
- virtual ~LLNotificationChannel() {}
+ virtual ~LLNotificationChannel();
typedef LLNotificationSet::iterator Iterator;
std::string getName() const { return mName; }
@@ -844,21 +848,23 @@ public:
{
return boost::iterator_range<parents_iter>(mParents);
}
-
- void connectToChannel(const std::string& channel_name);
-
+
bool isEmpty() const;
S32 size() const;
-
- Iterator begin();
- Iterator end();
- size_t size();
-
+ size_t size();
+
+ typedef boost::function<void(LLNotificationPtr)> NotificationProcess;
+ void forEachNotification(NotificationProcess process);
+
std::string summarize();
+protected:
+ void connectToChannel(const std::string& channel_name);
+
private:
std::string mName;
std::vector<std::string> mParents;
+ std::vector<LLBoundListener> mListeners;
};
// An interface class to provide a clean linker seam to the LLNotifications class.
@@ -924,10 +930,6 @@ public:
void update(const LLNotificationPtr pNotif);
LLNotificationPtr find(LLUUID uuid);
-
- typedef boost::function<void (LLNotificationPtr)> NotificationProcess;
-
- void forEachNotification(NotificationProcess process);
// This is all stuff for managing the templates
// take your template out
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
index e73ba1fbe9..83b5bf03ac 100644
--- a/indra/llui/llnotificationslistener.cpp
+++ b/indra/llui/llnotificationslistener.cpp
@@ -149,11 +149,11 @@ void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
if (channel)
{
LLSD notifications(LLSD::emptyArray());
- for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
- ni != nend; ++ni)
- {
- notifications.append(asLLSD(*ni));
- }
+ std::function<void(LLNotificationPtr)> func = [notifications](LLNotificationPtr ni) mutable
+ {
+ notifications.append(asLLSD(ni));
+ };
+ channel->forEachNotification(func);
response["notifications"] = notifications;
}
LLEventPumps::instance().obtain(params["reply"]).post(response);
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index bafeef41fb..7a88ecc76f 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -104,6 +104,14 @@ LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
}
}
+LLSearchEditor::~LLSearchEditor()
+{
+ mSearchButton = NULL;
+ mClearButton = NULL;
+ mSearchEditor->deleteAllChildren();
+ deleteAllChildren();
+}
+
//virtual
void LLSearchEditor::draw()
{
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index c0f3c1d60c..bd51988d07 100644
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -74,7 +74,7 @@ protected:
friend class LLUICtrlFactory;
public:
- virtual ~LLSearchEditor() {}
+ virtual ~LLSearchEditor();
/*virtual*/ void draw();
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index a6552d4ff1..328d36c6e1 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -440,7 +440,13 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
tooltip_params.rect = LLRect (0, 1, 1, 0);
if (tooltip_params.create_callback.isProvided())
- mToolTip = tooltip_params.create_callback()(tooltip_params);
+ {
+ mToolTip = tooltip_params.create_callback()(tooltip_params);
+ if (mToolTip == NULL)
+ {
+ return;
+ }
+ }
else
mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
@@ -492,7 +498,7 @@ void LLToolTipMgr::show(const LLToolTip::Params& params)
{
if (!params.styled_message.isProvided()
&& (!params.message.isProvided() || params.message().empty())
- && !params.image.isProvided()) return;
+ && !params.image.isProvided() && !params.create_callback.isProvided()) return;
// fill in default tooltip params from tool_tip.xml
LLToolTip::Params params_with_defaults(params);
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
index 391a377280..aaa2f6aef1 100644
--- a/indra/llwindow/lldxhardware.cpp
+++ b/indra/llwindow/lldxhardware.cpp
@@ -65,13 +65,12 @@ HRESULT GetVideoMemoryViaWMI(WCHAR* strInputDeviceID, DWORD* pdwAdapterRam)
{
HRESULT hr;
bool bGotMemory = false;
- HRESULT hrCoInitialize = S_OK;
IWbemLocator* pIWbemLocator = nullptr;
IWbemServices* pIWbemServices = nullptr;
BSTR pNamespace = nullptr;
*pdwAdapterRam = 0;
- hrCoInitialize = CoInitialize( 0 );
+ CoInitializeEx(0, COINIT_APARTMENTTHREADED);
hr = CoCreateInstance( CLSID_WbemLocator,
nullptr,
@@ -208,8 +207,7 @@ HRESULT GetVideoMemoryViaWMI(WCHAR* strInputDeviceID, DWORD* pdwAdapterRam)
SAFE_RELEASE( pIWbemLocator );
- if( SUCCEEDED( hrCoInitialize ) )
- CoUninitialize();
+ CoUninitialize();
if( bGotMemory )
return S_OK;
@@ -232,9 +230,8 @@ S32 LLDXHardware::getMBVideoMemoryViaWMI()
std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
{
std::string mDriverVersion;
- HRESULT hrCoInitialize = S_OK;
HRESULT hres;
- hrCoInitialize = CoInitialize(0);
+ CoInitializeEx(0, COINIT_APARTMENTTHREADED);
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
@@ -437,10 +434,10 @@ std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
{
pEnumerator->Release();
}
- if (SUCCEEDED(hrCoInitialize))
- {
- CoUninitialize();
- }
+
+ // supposed to always call CoUninitialize even if init returned false
+ CoUninitialize();
+
return mDriverVersion;
}
@@ -687,7 +684,8 @@ BOOL LLDXHardware::getInfo(BOOL vram_only)
BOOL ok = FALSE;
HRESULT hr;
- CoInitialize(NULL);
+ // CLSID_DxDiagProvider does not work with Multithreaded?
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
IDxDiagProvider *dx_diag_providerp = NULL;
IDxDiagContainer *dx_diag_rootp = NULL;
@@ -976,7 +974,7 @@ LLSD LLDXHardware::getDisplayInfo()
LLTimer hw_timer;
HRESULT hr;
LLSD ret;
- CoInitialize(NULL);
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
IDxDiagProvider *dx_diag_providerp = NULL;
IDxDiagContainer *dx_diag_rootp = NULL;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7a70d0b6e6..fc83c16e5d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -233,6 +233,7 @@ set(viewer_SOURCE_FILES
llfloaterimcontainer.cpp
llfloaterinspect.cpp
llfloaterinventorysettings.cpp
+ llfloaterinventorythumbnailshelper.cpp
llfloaterjoystick.cpp
llfloaterlagmeter.cpp
llfloaterland.cpp
@@ -887,6 +888,7 @@ set(viewer_HEADER_FILES
llfloaterimcontainer.h
llfloaterinspect.h
llfloaterinventorysettings.h
+ llfloaterinventorythumbnailshelper.h
llfloaterjoystick.h
llfloaterlagmeter.h
llfloaterland.h
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 3853aaa8fd..b7f452904a 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -4333,6 +4333,10 @@ void LLAgent::teleportRequest(
// Landmark ID = LLUUID::null means teleport home
void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
{
+ if (landmark_asset_id.isNull())
+ {
+ gAgentCamera.resetView();
+ }
mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLandmark(landmark_asset_id));
startTeleportRequest();
}
diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index 17e1a27934..f23ce13608 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -1738,10 +1738,6 @@ void AISUpdate::doUpdate()
LL_DEBUGS("Inventory") << "cat version update " << cat->getName() << " to version " << cat->getVersion() << LL_ENDL;
if (cat->getVersion() != version)
{
- LL_WARNS() << "Possible version mismatch for category " << cat->getName()
- << ", viewer version " << cat->getVersion()
- << " AIS version " << version << " !!!Adjusting local version!!!" << LL_ENDL;
-
// the AIS version should be considered the true version. Adjust
// our local category model to reflect this version number. Otherwise
// it becomes possible to get stuck with the viewer being out of
@@ -1751,13 +1747,23 @@ void AISUpdate::doUpdate()
// is performed. This occasionally gets out of sync however.
if (version != LLViewerInventoryCategory::VERSION_UNKNOWN)
{
+ LL_WARNS() << "Possible version mismatch for category " << cat->getName()
+ << ", viewer version " << cat->getVersion()
+ << " AIS version " << version << " !!!Adjusting local version!!!" << LL_ENDL;
cat->setVersion(version);
}
else
{
// We do not account for update if version is UNKNOWN, so we shouldn't rise version
// either or viewer will get stuck on descendants count -1, try to refetch folder instead
- cat->fetch();
+ //
+ // Todo: proper backoff?
+
+ LL_WARNS() << "Possible version mismatch for category " << cat->getName()
+ << ", viewer version " << cat->getVersion()
+ << " AIS version " << version << " !!!Rerequesting category!!!" << LL_ENDL;
+ const S32 LONG_EXPIRY = 360;
+ cat->fetch(LONG_EXPIRY);
}
}
}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4a43133ff6..ec411a680f 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1712,7 +1712,7 @@ bool LLAppViewer::cleanup()
LLNotifications::instance().clear();
// workaround for DEV-35406 crash on shutdown
- LLEventPumps::instance().reset();
+ LLEventPumps::instance().reset(true);
//dump scene loading monitor results
if (LLSceneMonitor::instanceExists())
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index cc4f4536a4..3444f50e52 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -177,6 +177,11 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p)
LLFloaterNotificationsTabbed::getInstance()->setSysWellChiclet(this);
}
+LLNotificationChiclet::~LLNotificationChiclet()
+{
+ mNotificationChannel.reset();
+}
+
void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data)
{
std::string action = user_data.asString();
diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h
index 58a797218f..1698fa269a 100644
--- a/indra/newview/llchiclet.h
+++ b/indra/newview/llchiclet.h
@@ -531,8 +531,9 @@ public:
struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{};
protected:
- struct ChicletNotificationChannel : public LLNotificationChannel
+ class ChicletNotificationChannel : public LLNotificationChannel
{
+ public:
ChicletNotificationChannel(LLNotificationChiclet* chiclet)
: LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString()))
, mChiclet(chiclet)
@@ -542,6 +543,7 @@ protected:
connectToChannel("Offer");
connectToChannel("Notifications");
}
+ virtual ~ChicletNotificationChannel() {}
static bool filterNotification(LLNotificationPtr notify);
// connect counter updaters to the corresponding signals
@@ -553,9 +555,10 @@ protected:
};
boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel;
-
- LLNotificationChiclet(const Params& p);
-
+
+ LLNotificationChiclet(const Params& p);
+ ~LLNotificationChiclet();
+
/**
* Processes clicks on chiclet menu.
*/
diff --git a/indra/newview/llfloaterinventorythumbnailshelper.cpp b/indra/newview/llfloaterinventorythumbnailshelper.cpp
new file mode 100644
index 0000000000..814f88e9b9
--- /dev/null
+++ b/indra/newview/llfloaterinventorythumbnailshelper.cpp
@@ -0,0 +1,543 @@
+/**
+ * @file llfloaterinventorythumbnailshelper.cpp
+ * @author Callum Prentice
+ * @brief LLFloaterInventoryThumbnailsHelper class implementation
+ *
+ * Usage instructions and some brief notes can be found in Confluence here:
+ * https://lindenlab.atlassian.net/wiki/spaces/~174746736/pages/2928672843/Inventory+Thumbnail+Helper+Tool
+ *
+ * $LicenseInfo:firstyear=2008&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 "llaisapi.h"
+#include "llclipboard.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "llnotifications.h"
+#include "llnotificationsutil.h"
+#include "llscrolllistctrl.h"
+#include "lltexteditor.h"
+#include "lluictrlfactory.h"
+#include "lluuid.h"
+
+#include "llfloaterinventorythumbnailshelper.h"
+
+LLFloaterInventoryThumbnailsHelper::LLFloaterInventoryThumbnailsHelper(const LLSD& key)
+ : LLFloater("floater_inventory_thumbnails_helper")
+{
+}
+
+LLFloaterInventoryThumbnailsHelper::~LLFloaterInventoryThumbnailsHelper()
+{
+}
+
+BOOL LLFloaterInventoryThumbnailsHelper::postBuild()
+{
+ mInventoryThumbnailsList = getChild<LLScrollListCtrl>("inventory_thumbnails_list");
+ mInventoryThumbnailsList->setAllowMultipleSelection(true);
+
+ mOutputLog = getChild<LLTextEditor>("output_log");
+ mOutputLog->setMaxTextLength(0xffff * 0x10);
+
+ mPasteItemsBtn = getChild<LLUICtrl>("paste_items_btn");
+ mPasteItemsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteItems, this));
+ mPasteItemsBtn->setEnabled(true);
+
+ mPasteTexturesBtn = getChild<LLUICtrl>("paste_textures_btn");
+ mPasteTexturesBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onPasteTextures, this));
+ mPasteTexturesBtn->setEnabled(true);
+
+ mWriteThumbnailsBtn = getChild<LLUICtrl>("write_thumbnails_btn");
+ mWriteThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onWriteThumbnails, this));
+ mWriteThumbnailsBtn->setEnabled(false);
+
+ mLogMissingThumbnailsBtn = getChild<LLUICtrl>("log_missing_thumbnails_btn");
+ mLogMissingThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onLogMissingThumbnails, this));
+ mLogMissingThumbnailsBtn->setEnabled(false);
+
+ mClearThumbnailsBtn = getChild<LLUICtrl>("clear_thumbnails_btn");
+ mClearThumbnailsBtn->setCommitCallback(boost::bind(&LLFloaterInventoryThumbnailsHelper::onClearThumbnails, this));
+ mClearThumbnailsBtn->setEnabled(false);
+
+ return true;
+}
+
+// Records an entry in the pasted items - saves it to a map and writes it to the log
+// window for later confirmation/validation - since it uses a map, duplicates (based on
+// the name) are discarded
+void LLFloaterInventoryThumbnailsHelper::recordInventoryItemEntry(LLViewerInventoryItem* item)
+{
+ const std::string name = item->getName();
+
+ std::map<std::string, LLViewerInventoryItem*>::iterator iter = mItemNamesItems.find(name);
+ if (iter == mItemNamesItems.end())
+ {
+ mItemNamesItems.insert({name, item});
+
+ writeToLog(
+ STRINGIZE(
+ "ITEM " << mItemNamesItems.size() << "> " <<
+ name <<
+ std::endl
+ ), false);
+ }
+ else
+ {
+ // dupe - do not save
+ }
+}
+
+// Called when the user has copied items from their inventory and selects the Paste Items button
+// in the UI - iterates over items and folders and saves details of each one.
+// The first use of this tool is for updating NUX items and as such, only looks for OBJECTS,
+// CLOTHING and BODYPARTS - later versions of this tool should make that selection editable.
+void LLFloaterInventoryThumbnailsHelper::onPasteItems()
+{
+ if (!LLClipboard::instance().hasContents())
+ {
+ return;
+ }
+
+ writeToLog(
+ STRINGIZE(
+ "\n==== Pasting items from inventory ====" <<
+ std::endl
+ ), false);
+
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+ size_t count = objects.size();
+
+ for (size_t i = 0; i < count; i++)
+ {
+ const LLUUID& entry = objects.at(i);
+
+ // Check for a folder
+ const LLInventoryCategory* cat = gInventory.getCategory(entry);
+ if (cat)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ LLIsType is_object(LLAssetType::AT_OBJECT);
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+
+ LLIsType is_bodypart(LLAssetType::AT_BODYPART);
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_bodypart);
+
+ LLIsType is_clothing(LLAssetType::AT_CLOTHING);
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_clothing);
+
+ for (size_t i = 0; i < item_array.size(); i++)
+ {
+ LLViewerInventoryItem* item = item_array.at(i);
+ recordInventoryItemEntry(item);
+ }
+ }
+
+ // Check for an item
+ LLViewerInventoryItem* item = gInventory.getItem(entry);
+ if (item)
+ {
+ const LLAssetType::EType item_type = item->getType();
+ if (item_type == LLAssetType::AT_OBJECT || item_type == LLAssetType::AT_BODYPART || item_type == LLAssetType::AT_CLOTHING)
+ {
+ recordInventoryItemEntry(item);
+ }
+ }
+ }
+
+ // update the main list view based on what we found
+ updateDisplayList();
+
+ // update the buttons enabled state based on what we found/saved
+ updateButtonStates();
+}
+
+// Records a entry in the pasted textures - saves it to a map and writes it to the log
+// window for later confirmation/validation - since it uses a map, duplicates (based on
+// the name) are discarded
+void LLFloaterInventoryThumbnailsHelper::recordTextureItemEntry(LLViewerInventoryItem* item)
+{
+ const std::string name = item->getName();
+
+ std::map<std::string, LLUUID>::iterator iter = mTextureNamesIDs.find(name);
+ if (iter == mTextureNamesIDs.end())
+ {
+ LLUUID id = item->getAssetUUID();
+ mTextureNamesIDs.insert({name, id});
+
+ writeToLog(
+ STRINGIZE(
+ "TEXTURE " << mTextureNamesIDs.size() << "> " <<
+ name <<
+ //" | " <<
+ //id.asString() <<
+ std::endl
+ ), false);
+ }
+ else
+ {
+ // dupe - do not save
+ }
+}
+
+// Called when the user has copied textures from their inventory and selects the Paste Textures
+// button in the UI - iterates over textures and folders and saves details of each one.
+void LLFloaterInventoryThumbnailsHelper::onPasteTextures()
+{
+ if (!LLClipboard::instance().hasContents())
+ {
+ return;
+ }
+
+ writeToLog(
+ STRINGIZE(
+ "\n==== Pasting textures from inventory ====" <<
+ std::endl
+ ), false);
+
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+ size_t count = objects.size();
+
+ for (size_t i = 0; i < count; i++)
+ {
+ const LLUUID& entry = objects.at(i);
+
+ const LLInventoryCategory* cat = gInventory.getCategory(entry);
+ if (cat)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ LLIsType is_object(LLAssetType::AT_TEXTURE);
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+
+ for (size_t i = 0; i < item_array.size(); i++)
+ {
+ LLViewerInventoryItem* item = item_array.at(i);
+ recordTextureItemEntry(item);
+ }
+ }
+
+ LLViewerInventoryItem* item = gInventory.getItem(entry);
+ if (item)
+ {
+ const LLAssetType::EType item_type = item->getType();
+ if (item_type == LLAssetType::AT_TEXTURE)
+ {
+ recordTextureItemEntry(item);
+ }
+ }
+ }
+
+ // update the main list view based on what we found
+ updateDisplayList();
+
+ // update the buttons enabled state based on what we found/saved
+ updateButtonStates();
+}
+
+// Updates the main list of entries in the UI based on what is in the maps/storage
+void LLFloaterInventoryThumbnailsHelper::updateDisplayList()
+{
+ mInventoryThumbnailsList->deleteAllItems();
+
+ std::map<std::string, LLViewerInventoryItem*>::iterator item_iter = mItemNamesItems.begin();
+ while (item_iter != mItemNamesItems.end())
+ {
+ std::string item_name = (*item_iter).first;
+
+ std::string existing_texture_name = std::string();
+ LLUUID existing_thumbnail_id = (*item_iter).second->getThumbnailUUID();
+ if (existing_thumbnail_id != LLUUID::null)
+ {
+ existing_texture_name = existing_thumbnail_id.asString();
+ }
+ else
+ {
+ existing_texture_name = "none";
+ }
+
+ std::string new_texture_name = std::string();
+ std::map<std::string, LLUUID>::iterator texture_iter = mTextureNamesIDs.find(item_name);
+ if (texture_iter != mTextureNamesIDs.end())
+ {
+ new_texture_name = (*texture_iter).first;
+ }
+ else
+ {
+ new_texture_name = "missing";
+ }
+
+ LLSD row;
+ row["columns"][EListColumnNum::NAME]["column"] = "item_name";
+ row["columns"][EListColumnNum::NAME]["type"] = "text";
+ row["columns"][EListColumnNum::NAME]["value"] = item_name;
+ row["columns"][EListColumnNum::NAME]["font"]["name"] = "Monospace";
+
+ row["columns"][EListColumnNum::EXISTING_TEXTURE]["column"] = "existing_texture";
+ row["columns"][EListColumnNum::EXISTING_TEXTURE]["type"] = "text";
+ row["columns"][EListColumnNum::EXISTING_TEXTURE]["font"]["name"] = "Monospace";
+ row["columns"][EListColumnNum::EXISTING_TEXTURE]["value"] = existing_texture_name;
+
+ row["columns"][EListColumnNum::NEW_TEXTURE]["column"] = "new_texture";
+ row["columns"][EListColumnNum::NEW_TEXTURE]["type"] = "text";
+ row["columns"][EListColumnNum::NEW_TEXTURE]["font"]["name"] = "Monospace";
+ row["columns"][EListColumnNum::NEW_TEXTURE]["value"] = new_texture_name;
+
+ mInventoryThumbnailsList->addElement(row);
+
+ ++item_iter;
+ }
+}
+
+#if 1
+// *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model.
+// temp code in transition
+void inventoryThumbnailsHelperCb(LLPointer<LLInventoryCallback> cb, LLUUID id)
+{
+ if (cb.notNull())
+ {
+ cb->fire(id);
+ }
+}
+#endif
+
+// Makes calls to the AIS v3 API to record the local changes made to the thumbnails.
+// If this is not called, the operations (e.g. set thumbnail or clear thumbnail)
+// appear to work but do not push the changes back to the inventory (local cache view only)
+bool writeInventoryThumbnailID(LLUUID item_id, LLUUID thumbnail_asset_id)
+{
+ if (AISAPI::isAvailable())
+ {
+
+ LLSD updates;
+ updates["thumbnail"] = LLSD().with("asset_id", thumbnail_asset_id.asString());
+
+ LLPointer<LLInventoryCallback> cb;
+
+ AISAPI::completion_t cr = boost::bind(&inventoryThumbnailsHelperCb, cb, _1);
+ AISAPI::UpdateItem(item_id, updates, cr);
+
+ return true;
+ }
+ else
+ {
+ LL_WARNS() << "Unable to write inventory thumbnail because the AIS API is not available" << LL_ENDL;
+ return false;
+ }
+}
+
+// Called when the Write Thumbanils button is pushed. Iterates over the name/item and
+// name/.texture maps and where it finds a common name, extracts what is needed and
+// writes the thumbnail accordingly.
+void LLFloaterInventoryThumbnailsHelper::onWriteThumbnails()
+{
+ // create and show confirmation (Yes/No) textbox since this is a destructive operation
+ LLNotificationsUtil::add("WriteInventoryThumbnailsWarning", LLSD(), LLSD(),
+ [&](const LLSD & notif, const LLSD & resp)
+ {
+ S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp);
+ if (opt == 0)
+ {
+ std::map<std::string, LLViewerInventoryItem*>::iterator item_iter = mItemNamesItems.begin();
+ while (item_iter != mItemNamesItems.end())
+ {
+ std::string item_name = (*item_iter).first;
+
+ std::map<std::string, LLUUID>::iterator texture_iter = mTextureNamesIDs.find(item_name);
+ if (texture_iter != mTextureNamesIDs.end())
+ {
+ LLUUID item_id = (*item_iter).second->getUUID();
+
+ LLUUID thumbnail_asset_id = (*texture_iter).second;
+
+ writeToLog(
+ STRINGIZE(
+ "WRITING THUMB " <<
+ (*item_iter).first <<
+ "\n" <<
+ "item ID: " <<
+ item_id <<
+ "\n" <<
+ "thumbnail texture ID: " <<
+ thumbnail_asset_id <<
+ "\n"
+ ), true);
+
+
+ (*item_iter).second->setThumbnailUUID(thumbnail_asset_id);
+
+ // This additional step (notifying AIS API) is required
+ // to make the changes persist outside of the local cache
+ writeInventoryThumbnailID(item_id, thumbnail_asset_id);
+ }
+
+ ++item_iter;
+ }
+
+ updateDisplayList();
+ }
+ else
+ {
+ LL_INFOS() << "Writing new thumbnails was canceled" << LL_ENDL;
+ }
+ });
+}
+
+// Called when the Log Items with Missing Thumbnails is selected. This merely writes
+// a list of all the items for which the thumbnail ID is Null. Typical use case is to
+// copy from the log window, pasted to Slack to illustrate which items are missing
+// a thumbnail
+void LLFloaterInventoryThumbnailsHelper::onLogMissingThumbnails()
+{
+ std::map<std::string, LLViewerInventoryItem*>::iterator item_iter = mItemNamesItems.begin();
+ while (item_iter != mItemNamesItems.end())
+ {
+ LLUUID thumbnail_id = (*item_iter).second->getThumbnailUUID();
+
+ if (thumbnail_id == LLUUID::null)
+ {
+ writeToLog(
+ STRINGIZE(
+ "Missing thumbnail: " <<
+ (*item_iter).first <<
+ std::endl
+ ), true);
+ }
+
+ ++item_iter;
+ }
+}
+
+// Called when the Clear Thumbnail button is selected. Code to perform the clear (really
+// just writing a NULL UUID into the thumbnail field) is behind an "Are you Sure?" dialog
+// since it cannot be undone and potentinally, you could remove the thumbnails from your
+// whole inventory this way.
+void LLFloaterInventoryThumbnailsHelper::onClearThumbnails()
+{
+ // create and show confirmation (Yes/No) textbox since this is a destructive operation
+ LLNotificationsUtil::add("ClearInventoryThumbnailsWarning", LLSD(), LLSD(),
+ [&](const LLSD & notif, const LLSD & resp)
+ {
+ S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp);
+ if (opt == 0)
+ {
+ std::map<std::string, LLViewerInventoryItem*>::iterator item_iter = mItemNamesItems.begin();
+ while (item_iter != mItemNamesItems.end())
+ {
+ (*item_iter).second->setThumbnailUUID(LLUUID::null);
+
+ // This additional step (notifying AIS API) is required
+ // to make the changes persist outside of the local cache
+ const LLUUID item_id = (*item_iter).second->getUUID();
+ writeInventoryThumbnailID(item_id, LLUUID::null);
+
+ ++item_iter;
+ }
+
+ updateDisplayList();
+ }
+ else
+ {
+ LL_INFOS() << "Clearing on thumbnails was canceled" << LL_ENDL;
+ }
+ });
+}
+
+// Update the endabled state of some of the UI buttons based on what has
+// been recorded so far. For example, if there are no valid item/texture pairs,
+// then the Write Thumbnails button is not enabled.
+void LLFloaterInventoryThumbnailsHelper::updateButtonStates()
+{
+ size_t found_count = 0;
+
+ std::map<std::string, LLViewerInventoryItem*>::iterator item_iter = mItemNamesItems.begin();
+ while (item_iter != mItemNamesItems.end())
+ {
+ std::string item_name = (*item_iter).first;
+
+ std::map<std::string, LLUUID>::iterator texture_iter = mTextureNamesIDs.find(item_name);
+ if (texture_iter != mTextureNamesIDs.end())
+ {
+ found_count++;
+ }
+
+ ++item_iter;
+ }
+
+ // the "Write Thumbnails" button is only enabled when there is at least one
+ // item with a matching texture ready to be written to the thumbnail field
+ if (found_count > 0)
+ {
+ mWriteThumbnailsBtn->setEnabled(true);
+ }
+ else
+ {
+ mWriteThumbnailsBtn->setEnabled(false);
+ }
+
+ // The "Log Missing Items" and "Clear Thumbnails" buttons are only enabled
+ // when there is at least 1 item that was pasted from inventory (doesn't need
+ // to have a matching texture for these operations)
+ if (mItemNamesItems.size() > 0)
+ {
+ mLogMissingThumbnailsBtn->setEnabled(true);
+ mClearThumbnailsBtn->setEnabled(true);
+ }
+ else
+ {
+ mLogMissingThumbnailsBtn->setEnabled(false);
+ mClearThumbnailsBtn->setEnabled(false);
+ }
+}
+
+// Helper function for writing a line to the log window. Currently the only additional
+// feature is that it scrolls to the bottom each time a line is written but it
+// is envisaged that other common actions will be added here eventually - E.G. write eavh
+// line to the Second Life log too for example.
+void LLFloaterInventoryThumbnailsHelper::writeToLog(std::string logline, bool prepend_newline)
+{
+ mOutputLog->appendText(logline, prepend_newline);
+
+ mOutputLog->setCursorAndScrollToEnd();
+}
diff --git a/indra/newview/llfloaterinventorythumbnailshelper.h b/indra/newview/llfloaterinventorythumbnailshelper.h
new file mode 100644
index 0000000000..b42a85d1a5
--- /dev/null
+++ b/indra/newview/llfloaterinventorythumbnailshelper.h
@@ -0,0 +1,82 @@
+/**
+ * @file llfloaterinventorythumbnailshelper.h
+ * @author Callum Prentice
+ * @brief Helper floater for bulk processing of inventory thumbnails tool
+ *
+ * $LicenseInfo:firstyear=2008&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_LLFLOATERINVENTORYTHUMBNAILSHELPER_H
+#define LL_LLFLOATERINVENTORYTHUMBNAILSHELPER_H
+
+#include "llfloater.h"
+class LLTextEditor;
+class LLScrollListCtrl;
+class LLViewerInventoryItem;
+class LLUUID;
+
+class LLFloaterInventoryThumbnailsHelper:
+ public LLFloater
+{
+ friend class LLFloaterReg;
+ private:
+ LLFloaterInventoryThumbnailsHelper(const LLSD& key);
+ BOOL postBuild() override;
+ ~LLFloaterInventoryThumbnailsHelper();
+
+ LLScrollListCtrl* mInventoryThumbnailsList;
+
+ LLTextEditor* mOutputLog;
+
+ LLUICtrl* mPasteItemsBtn;
+ void onPasteItems();
+
+ LLUICtrl* mPasteTexturesBtn;
+ void onPasteTextures();
+
+ LLUICtrl* mWriteThumbnailsBtn;
+ void onWriteThumbnails();
+
+ LLUICtrl* mLogMissingThumbnailsBtn;
+ void onLogMissingThumbnails();
+
+ LLUICtrl* mClearThumbnailsBtn;
+ void onClearThumbnails();
+
+ void recordInventoryItemEntry(LLViewerInventoryItem* item);
+ void recordTextureItemEntry(LLViewerInventoryItem* item);
+ void updateButtonStates();
+ void updateDisplayList();
+ void writeToLog(std::string logline, bool prepend_newline);
+
+ std::map<std::string, LLViewerInventoryItem*> mItemNamesItems;
+ std::map<std::string, LLUUID> mTextureNamesIDs;
+
+ enum EListColumnNum
+ {
+ NAME = 0,
+ EXISTING_TEXTURE = 1,
+ NEW_TEXTURE = 2
+ };
+};
+
+#endif // LL_LLFLOATERINVENTORYTHUMBNAILSHELPER_H
diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp
index 71b3b16809..6216f4e39a 100644
--- a/indra/newview/llfloatermarketplacelistings.cpp
+++ b/indra/newview/llfloatermarketplacelistings.cpp
@@ -183,7 +183,8 @@ void LLPanelMarketplaceListings::draw()
// Get the audit button enabled only after the whole inventory is fetched
if (!mAuditBtn->getEnabled())
{
- mAuditBtn->setEnabled(LLInventoryModelBackgroundFetch::instance().isEverythingFetched());
+ LLInventoryModelBackgroundFetch* inst = LLInventoryModelBackgroundFetch::getInstance();
+ mAuditBtn->setEnabled(inst->isEverythingFetched() && !inst->folderFetchActive());
}
LLPanel::draw();
@@ -410,8 +411,14 @@ BOOL LLFloaterMarketplaceListings::postBuild()
mCategoryAddedObserver = new LLMarketplaceListingsAddedObserver(this);
gInventory.addObserver(mCategoryAddedObserver);
- // Fetch aggressively so we can interact with listings right onOpen()
- fetchContents();
+
+ // Fetch aggressively so we can interact with listings as soon as possible
+ if (!fetchContents())
+ {
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ LLInventoryModelBackgroundFetch::instance().start(marketplacelistings_id, true);
+ }
+
return TRUE;
}
@@ -440,17 +447,19 @@ void LLFloaterMarketplaceListings::onFocusReceived()
updateView();
}
-void LLFloaterMarketplaceListings::fetchContents()
+bool LLFloaterMarketplaceListings::fetchContents()
{
- if (mRootFolderId.notNull() &&
+ if (mRootFolderId.notNull() &&
(LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_LOADING) &&
(LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_DONE))
- {
+ {
LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this));
LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING);
- LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true);
+ LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true);
LLMarketplaceData::instance().getSLMListings();
- }
+ return true;
+ }
+ return false;
}
void LLFloaterMarketplaceListings::setRootFolder()
diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h
index 085e517a9d..78d43f97a9 100644
--- a/indra/newview/llfloatermarketplacelistings.h
+++ b/indra/newview/llfloatermarketplacelistings.h
@@ -114,8 +114,8 @@ public:
protected:
void setRootFolder();
void setPanels();
- void fetchContents();
-
+ bool fetchContents();
+
void setStatusString(const std::string& statusString);
void onClose(bool app_quitting);
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index c8559fc9d3..62e4022ddb 100755
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -327,6 +327,7 @@ void* LLFloaterWorldMap::createWorldMapView(void* data)
BOOL LLFloaterWorldMap::postBuild()
{
mMapView = dynamic_cast<LLWorldMapView*>(getChild<LLPanel>("objects_mapview"));
+ mMapView->setPan(0, 0, true);
LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
avatar_combo->selectFirstItem();
diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp
index da4e3c0949..76e428b7d0 100644
--- a/indra/newview/llinspecttexture.cpp
+++ b/indra/newview/llinspecttexture.cpp
@@ -84,7 +84,10 @@ LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p)
}
}
}
-
+ if ((!p.message.isProvided() || p.message().empty()))
+ {
+ return NULL;
+ }
// No or more than one texture found => show default tooltip
return LLUICtrlFactory::create<LLToolTip>(p);
}
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 932a0316dd..f806614e29 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -2284,8 +2284,11 @@ BOOL LLFolderBridge::isItemMovable() const
void LLFolderBridge::selectItem()
{
- // Have no fear: the first thing start() does is to test if everything for that folder has been fetched...
- LLInventoryModelBackgroundFetch::instance().start(getUUID(), true);
+ LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID());
+ if (cat)
+ {
+ cat->fetch();
+ }
}
void LLFolderBridge::buildDisplayName() const
@@ -2810,7 +2813,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size);
}
- if (is_movable)
+ if (is_movable && !move_is_into_landmarks)
{
LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
is_movable = active_panel != NULL;
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 7b4283e94d..332c6d3085 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -202,12 +202,18 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
&& !LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress())
{
LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
- if ((!cat && folder_id.notNull()) || (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
+ if ((!cat && folder_id.notNull()))
{
- // At the moment background fetch only cares about VERSION_UNKNOWN,
- // so do not check isCategoryComplete that compares descendant count
+ // Shouldn't happen? Server provides full list of folders on startup
LLInventoryModelBackgroundFetch::instance().start(folder_id, false);
}
+ else if (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // At the moment background fetch only cares about VERSION_UNKNOWN,
+ // so do not check isCategoryComplete that compares descendant count,
+ // but if that is nesesary, do a forced scheduleFolderFetch.
+ cat->fetch();
+ }
}
if (!checkAgainstFilterThumbnails(folder_id))
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 4aeacae6ed..6662f14b12 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -462,6 +462,13 @@ void copy_inventory_category(LLInventoryModel* model,
gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID());
}
+void copy_cb(const LLUUID& dest_folder, const LLUUID& root_id)
+{
+ // Decrement the count in root_id since that one item won't be copied over
+ LLMarketplaceData::instance().decrementValidationWaiting(root_id);
+ update_folder_cb(dest_folder);
+};
+
void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items)
{
model->notifyObservers();
@@ -480,12 +487,21 @@ void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryMode
LLMarketplaceData::instance().setValidationWaiting(root_id, count_descendants_items(cat->getUUID()));
}
+ LLPointer<LLInventoryCallback> cb;
+ if (root_copy_id.isNull())
+ {
+ cb = new LLBoostFuncInventoryCallback(boost::bind(copy_cb, new_cat_uuid, root_id));
+ }
+ else
+ {
+ cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid));
+ }
+
// Copy all the items
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;
- LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid));
if (item->getIsLinkType())
{
@@ -500,8 +516,11 @@ void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryMode
LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *)item;
gInventory.changeItemParent(viewer_inv_item, new_cat_uuid, true);
}
- // Decrement the count in root_id since that one item won't be copied over
- LLMarketplaceData::instance().decrementValidationWaiting(root_id);
+ if (root_copy_id.isNull())
+ {
+ // Decrement the count in root_id since that one item won't be copied over
+ LLMarketplaceData::instance().decrementValidationWaiting(root_id);
+ }
}
else
{
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 68581d04eb..d2056fb44d 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -2402,26 +2402,42 @@ void LLInventoryGallery::startDrag()
{
std::vector<EDragAndDropType> types;
uuid_vec_t ids;
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT;
for (LLUUID& selected_id : mSelectedItemIDs)
{
const LLInventoryItem* item = gInventory.getItem(selected_id);
if (item)
{
+ if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID)
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ }
+
EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType());
types.push_back(type);
ids.push_back(selected_id);
}
const LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);
- if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())
- && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType()))
+ if (cat)
{
- EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());
- types.push_back(type);
- ids.push_back(selected_id);
+ if (gInventory.isObjectDescendentOf(selected_id, gInventory.getLibraryRootFolderID()))
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());
+ types.push_back(type);
+ ids.push_back(selected_id);
+ }
+ else if (gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())
+ && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType()))
+ {
+ EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());
+ types.push_back(type);
+ ids.push_back(selected_id);
+ }
}
}
- LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT);
+ LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, src);
}
bool LLInventoryGallery::areViewsInitialized()
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 05aa2e423f..205e5f3489 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -3550,6 +3550,9 @@ void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, vo
gInventoryCallbacks.fire(callback_id, item_id);
+ // Message system at the moment doesn't support Thumbnails and potential
+ // newer features so just rerequest whole item
+ //
// todo: instead of unpacking message fully,
// grab only an item_id, then fetch
LLInventoryModelBackgroundFetch::instance().scheduleItemFetch(item_id, true);
@@ -3912,19 +3915,22 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
{
- gInventory.updateCategory(*cit);
-
- // Temporary workaround: just fetch the item using AIS to get missing fields.
- // If this works fine we might want to extract ids only from the message
- // then use AIS as a primary fetcher
- LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch((*cit)->getUUID(), true /*force, since it has changes*/);
+ gInventory.updateCategory(*cit);
+ if ((*cit)->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // Temporary workaround: just fetch the item using AIS to get missing fields.
+ // If this works fine we might want to extract 'ids only' from the message
+ // then use AIS as a primary fetcher
+ LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch((*cit)->getUUID(), true /*force, since it has changes*/);
+ }
+ // else already called fetch() above
}
for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
{
gInventory.updateItem(*iit);
// Temporary workaround: just fetch the item using AIS to get missing fields.
- // If this works fine we might want to extract ids only from the message
+ // If this works fine we might want to extract 'ids only' from the message
// then use AIS as a primary fetcher
LLInventoryModelBackgroundFetch::instance().scheduleItemFetch((*iit)->getUUID(), true);
}
diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp
index 1f410bea10..722447b5d7 100644
--- a/indra/newview/llinventorymodelbackgroundfetch.cpp
+++ b/indra/newview/llinventorymodelbackgroundfetch.cpp
@@ -193,13 +193,16 @@ LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch():
mLastFetchCount(0),
mFetchFolderCount(0),
mAllRecursiveFoldersFetched(false),
- mRecursiveInventoryFetchStarted(false),
- mRecursiveLibraryFetchStarted(false),
- mMinTimeBetweenFetches(0.3f)
+ mRecursiveInventoryFetchStarted(false),
+ mRecursiveLibraryFetchStarted(false),
+ mRecursiveMarketplaceFetchStarted(false),
+ mMinTimeBetweenFetches(0.3f)
{}
LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch()
-{}
+{
+ gIdleCallbacks.deleteFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+}
bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const
{
@@ -314,6 +317,23 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive)
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
+ else if (recursive && cat && cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS)
+ {
+ if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != id)
+ {
+ if (recursive && AISAPI::isAvailable())
+ {
+ // Request marketplace folder and content separately
+ mFetchFolderQueue.push_front(FetchQueueInfo(id, FT_FOLDER_AND_CONTENT));
+ }
+ else
+ {
+ mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type));
+ }
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ mRecursiveMarketplaceFetchStarted = true;
+ }
+ }
else
{
if (AISAPI::isAvailable())
@@ -359,8 +379,22 @@ void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id,
mBackgroundFetchActive = true;
mFolderFetchActive = true;
- // Specific folder requests go to front of queue.
- mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
+ if (forced)
+ {
+ // check if already requested
+ if (mForceFetchSet.find(cat_id) == mForceFetchSet.end())
+ {
+ mForceFetchSet.insert(cat_id);
+ mFetchItemQueue.push_front(FetchQueueInfo(cat_id, FT_FORCED));
+ }
+ }
+ else
+ {
+ // Specific folder requests go to front of queue.
+ // version presence acts as dupplicate prevention for normal fetches
+ mFetchItemQueue.push_front(FetchQueueInfo(cat_id, FT_DEFAULT));
+ }
+
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
@@ -370,8 +404,21 @@ void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, b
if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != item_id)
{
mBackgroundFetchActive = true;
+ if (forced)
+ {
+ // check if already requested
+ if (mForceFetchSet.find(item_id) == mForceFetchSet.end())
+ {
+ mForceFetchSet.insert(item_id);
+ mFetchItemQueue.push_front(FetchQueueInfo(item_id, FT_FORCED, false));
+ }
+ }
+ else
+ {
+ // 'isFinished' being set acts as dupplicate prevention for normal fetches
+ mFetchItemQueue.push_front(FetchQueueInfo(item_id, FT_DEFAULT, false));
+ }
- mFetchItemQueue.push_front(FetchQueueInfo(item_id, forced ? FT_FORCED : FT_DEFAULT, false));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
@@ -591,6 +638,7 @@ void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_i
return;
}
+ LLViewerInventoryCategory::EFetchType new_state = LLViewerInventoryCategory::FETCH_NONE;
bool request_descendants = false;
if (response_id.isNull()) // Failure
{
@@ -608,10 +656,12 @@ void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_i
// set folder's version to prevent viewer from trying to request folder indefinetely
LLViewerInventoryCategory* cat(gInventory.getCategory(request_id));
- if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ if (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
cat->setVersion(0);
}
+ // back off for a bit in case something tries to force-request immediately
+ new_state = LLViewerInventoryCategory::FETCH_FAILED;
}
}
else
@@ -664,7 +714,7 @@ void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_i
LLViewerInventoryCategory * cat(gInventory.getCategory(request_id));
if (cat)
{
- cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ cat->setFetching(new_state);
}
}
@@ -753,7 +803,26 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis()
if (isFolderFetchProcessingComplete() && mFolderFetchActive)
{
- setAllFoldersFetched();
+ if (!mRecursiveInventoryFetchStarted || mRecursiveMarketplaceFetchStarted)
+ {
+ setAllFoldersFetched();
+ }
+ else
+ {
+ // Intent is for marketplace request to happen after
+ // main inventory is done, unless requested by floater
+ mRecursiveMarketplaceFetchStarted = true;
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ if (marketplacelistings_id.notNull())
+ {
+ mFetchFolderQueue.push_front(FetchQueueInfo(marketplacelistings_id, FT_FOLDER_AND_CONTENT));
+ }
+ else
+ {
+ setAllFoldersFetched();
+ }
+ }
+
}
if (isBulkFetchProcessingComplete())
@@ -813,22 +882,8 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
if (child_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS)
{
- // special case
- content_done = false;
- if (children.empty())
- {
- // fetch marketplace alone
- // Should it actually be fetched as FT_FOLDER_AND_CONTENT?
- children.push_back(child_cat->getUUID());
- mExpectedFolderIds.push_back(child_cat->getUUID());
- child_cat->setFetching(target_state);
- break;
- }
- else
- {
- // fetch marketplace alone next run
- continue;
- }
+ // special case, marketplace will fetch that as needed
+ continue;
}
children.push_back(child_cat->getUUID());
@@ -902,10 +957,10 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
mExpectedFolderIds.push_back(cat_id);
EFetchType type = fetch_info.mFetchType;
- LLUUID cat_id = cat->getUUID();
- AISAPI::completion_t cb = [cat_id , type](const LLUUID& response_id)
+ LLUUID cat_cb_id = cat_id;
+ AISAPI::completion_t cb = [cat_cb_id, type](const LLUUID& response_id)
{
- LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_id , response_id , type);
+ LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_cb_id, response_id , type);
};
AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY;
@@ -964,6 +1019,11 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback);
}
}
+
+ if (fetch_info.mFetchType == FT_FORCED)
+ {
+ mForceFetchSet.erase(fetch_info.mUUID);
+ }
}
// Bundle up a bunch of requests to send all at once.
diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h
index a712fc7604..989968be53 100644
--- a/indra/newview/llinventorymodelbackgroundfetch.h
+++ b/indra/newview/llinventorymodelbackgroundfetch.h
@@ -76,7 +76,6 @@ public:
void incrFetchFolderCount(S32 fetching);
bool isBulkFetchProcessingComplete() const;
- bool isFolderFetchProcessingComplete() const;
void setAllFoldersFetched();
typedef boost::function<void()> folders_fetched_callback_t;
@@ -86,6 +85,7 @@ public:
void addRequestAtBack(const LLUUID & id, bool recursive, bool is_category);
protected:
+ bool isFolderFetchProcessingComplete() const;
typedef enum {
FT_DEFAULT = 0,
@@ -122,6 +122,7 @@ protected:
private:
bool mRecursiveInventoryFetchStarted;
bool mRecursiveLibraryFetchStarted;
+ bool mRecursiveMarketplaceFetchStarted; // AIS3 specific
bool mAllRecursiveFoldersFetched;
typedef boost::signals2::signal<void()> folders_fetched_signal_t;
folders_fetched_signal_t mFoldersFetchedSignal;
@@ -136,6 +137,7 @@ private:
F32 mMinTimeBetweenFetches;
fetch_queue_t mFetchFolderQueue;
fetch_queue_t mFetchItemQueue;
+ uuid_set_t mForceFetchSet;
std::list<LLUUID> mExpectedFolderIds; // for debug, should this track time?
};
diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp
index fe067b621a..b0617e5b6c 100644
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -356,7 +356,6 @@ void LLInventoryFetchItemsObserver::startFetch()
{
// Start fetching whole folder since we need all items
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
-
}
else
{
diff --git a/indra/newview/llmachineid.cpp b/indra/newview/llmachineid.cpp
index 583742f970..1f4418f119 100644
--- a/indra/newview/llmachineid.cpp
+++ b/indra/newview/llmachineid.cpp
@@ -85,11 +85,11 @@ void LLWMIMethods::initCOMObjects()
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
- mHR = CoInitializeEx(0, COINIT_MULTITHREADED);
+ mHR = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (FAILED(mHR))
{
+ // if result S_FALSE, it's already initialized
LL_DEBUGS("AppInit") << "Failed to initialize COM library. Error code = 0x" << std::hex << mHR << LL_ENDL;
- return;
}
// Step 2: --------------------------------------------------
diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp
index 8784f403cb..d27ee941a6 100644
--- a/indra/newview/llmarketplacefunctions.cpp
+++ b/indra/newview/llmarketplacefunctions.cpp
@@ -611,27 +611,17 @@ private:
static void onIdleProcessQueue(void *userdata);
// doesn't hold just marketplace related ids
- static std::set<LLUUID> sAddQueue;
static std::set<LLUUID> sStructureQueue;
static bool sProcessingQueue;
};
-std::set<LLUUID> LLMarketplaceInventoryObserver::sAddQueue;
std::set<LLUUID> LLMarketplaceInventoryObserver::sStructureQueue;
bool LLMarketplaceInventoryObserver::sProcessingQueue = false;
void LLMarketplaceInventoryObserver::changed(U32 mask)
{
- if (mask & LLInventoryObserver::ADD && LLMarketplaceData::instance().hasValidationWaiting())
- {
- // When things are added to the marketplace, we might need to re-validate and fix the containing listings
- // just add whole list even if it contains items and non-marketplace folders
- const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
- sAddQueue.insert(changed_items.begin(), changed_items.end());
- }
-
- if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE))
- {
+ if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE))
+ {
// 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
@@ -641,7 +631,7 @@ void LLMarketplaceInventoryObserver::changed(U32 mask)
sStructureQueue.insert(changed_items.begin(), changed_items.end());
}
- if (!sProcessingQueue && (!sAddQueue.empty() || !sStructureQueue.empty()))
+ if (!sProcessingQueue && !sStructureQueue.empty())
{
gIdleCallbacks.addFunction(onIdleProcessQueue, NULL);
// can do without sProcessingQueue, but it's usufull for simplicity and reliability
@@ -655,40 +645,6 @@ void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata)
const U64 MAX_PROCESSING_TIME = 1000;
U64 stop_time = start_time + MAX_PROCESSING_TIME;
- if (!sAddQueue.empty())
- {
- // Make a copy of sAddQueue since decrementValidationWaiting
- // can theoretically add more items
- std::set<LLUUID> add_queue(sAddQueue);
- sAddQueue.clear();
-
- std::set<LLUUID>::const_iterator id_it = add_queue.begin();
- std::set<LLUUID>::const_iterator id_end = add_queue.end();
- // First, count the number of items in this list...
- S32 count = 0;
- for (; id_it != id_end; ++id_it)
- {
- LLInventoryObject* obj = gInventory.getObject(*id_it);
- if (obj && (LLAssetType::AT_CATEGORY != obj->getType()))
- {
- count++;
- }
- }
- // Then, decrement the folders of that amount
- // Note that of all of those, only one folder will be a listing folder (if at all).
- // The other will be ignored by the decrement method.
- id_it = add_queue.begin();
- for (; id_it != id_end; ++id_it)
- {
- LLInventoryObject* obj = gInventory.getObject(*id_it);
- if (obj && (LLAssetType::AT_CATEGORY == obj->getType()))
- {
- // can trigger notifyObservers
- LLMarketplaceData::instance().decrementValidationWaiting(obj->getUUID(), count);
- }
- }
- }
-
while (!sStructureQueue.empty() && LLTimer::getTotalTime() < stop_time)
{
std::set<LLUUID>::const_iterator id_it = sStructureQueue.begin();
@@ -722,7 +678,7 @@ void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata)
sStructureQueue.erase(id_it);
}
- if (LLApp::isExiting() || (sAddQueue.empty() && sStructureQueue.empty()))
+ if (LLApp::isExiting() || sStructureQueue.empty())
{
// Nothing to do anymore
gIdleCallbacks.deleteFunction(onIdleProcessQueue, NULL);
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index de988555c5..b26aabca4f 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -1155,22 +1155,13 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu()
registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id));
registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2));
registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id));
+ registrar.add("Outfit.Save", boost::bind(&LLOutfitGalleryContextMenu::onSave, this, selected_id));
enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2));
enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2));
return createFromFile("menu_gallery_outfit_tab.xml");
}
-void LLOutfitGalleryContextMenu::onThumbnail(const LLUUID& outfit_cat_id)
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery && outfit_cat_id.notNull())
- {
- LLSD data(outfit_cat_id);
- LLFloaterReg::showInstance("change_item_thumbnail", data);
- }
-}
-
void LLOutfitGalleryContextMenu::onCreate(const LLSD& data)
{
LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString());
@@ -1205,7 +1196,6 @@ void LLOutfitGalleryGearMenu::onUpdateItemsVisibility()
mMenu->setItemVisible("expand", FALSE);
mMenu->setItemVisible("collapse", FALSE);
mMenu->setItemVisible("thumbnail", have_selection);
- mMenu->setItemVisible("sepatator3", TRUE);
mMenu->setItemVisible("sort_folders_by_name", TRUE);
LLOutfitListGearMenuBase::onUpdateItemsVisibility();
}
diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h
index 9915752962..b18151599f 100644
--- a/indra/newview/lloutfitgallery.h
+++ b/indra/newview/lloutfitgallery.h
@@ -195,17 +195,13 @@ public:
friend class LLOutfitGallery;
LLOutfitGalleryContextMenu(LLOutfitListBase* outfit_list)
- : LLOutfitContextMenu(outfit_list),
- mOutfitList(outfit_list){}
+ : LLOutfitContextMenu(outfit_list){}
protected:
/* virtual */ LLContextMenu* createMenu();
bool onEnable(LLSD::String param);
bool onVisible(LLSD::String param);
- void onThumbnail(const LLUUID& outfit_cat_id);
void onCreate(const LLSD& data);
-private:
- LLOutfitListBase* mOutfitList;
};
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 5c7792b0df..676444397f 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -37,6 +37,7 @@
#include "llappearancemgr.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
+#include "llinspecttexture.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "llmenubutton.h"
@@ -62,7 +63,7 @@ bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LL
return (LLStringUtil::compareDict(name1, name2) < 0);
}
-struct outfit_accordion_tab_params : public LLInitParam::Block<outfit_accordion_tab_params, LLAccordionCtrlTab::Params>
+struct outfit_accordion_tab_params : public LLInitParam::Block<outfit_accordion_tab_params, LLOutfitAccordionCtrlTab::Params>
{
Mandatory<LLWearableItemsList::Params> wearable_list;
@@ -144,7 +145,8 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
std::string name = cat->getName();
outfit_accordion_tab_params tab_params(get_accordion_tab_params());
- LLAccordionCtrlTab* tab = LLUICtrlFactory::create<LLAccordionCtrlTab>(tab_params);
+ tab_params.cat_id = cat_id;
+ LLOutfitAccordionCtrlTab *tab = LLUICtrlFactory::create<LLOutfitAccordionCtrlTab>(tab_params);
if (!tab) return;
LLWearableItemsList* wearable_list = LLUICtrlFactory::create<LLWearableItemsList>(tab_params.wearable_list);
wearable_list->setShape(tab->getLocalRect());
@@ -1028,6 +1030,8 @@ LLContextMenu* LLOutfitContextMenu::createMenu()
registrar.add("Outfit.Edit", boost::bind(editOutfit));
registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id));
registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList));
+ registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitContextMenu::onThumbnail, this, selected_id));
+ registrar.add("Outfit.Save", boost::bind(&LLOutfitContextMenu::onSave, this, selected_id));
enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2));
enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2));
@@ -1092,6 +1096,31 @@ void LLOutfitContextMenu::renameOutfit(const LLUUID& outfit_cat_id)
LLAppearanceMgr::instance().renameOutfit(outfit_cat_id);
}
+void LLOutfitContextMenu::onThumbnail(const LLUUID &outfit_cat_id)
+{
+ if (outfit_cat_id.notNull())
+ {
+ LLSD data(outfit_cat_id);
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
+ }
+}
+
+void LLOutfitContextMenu::onSave(const LLUUID &outfit_cat_id)
+{
+ if (outfit_cat_id.notNull())
+ {
+ LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(),
+ [outfit_cat_id](const LLSD &notif, const LLSD &resp)
+ {
+ S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp);
+ if (opt == 0)
+ {
+ LLAppearanceMgr::getInstance()->onOutfitFolderCreated(outfit_cat_id, true);
+ }
+ });
+ }
+}
+
LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist)
: mOutfitList(olist),
mMenu(NULL)
@@ -1110,6 +1139,7 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist)
registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList));
registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this));
+ registrar.add("Gear.Save", boost::bind(&LLOutfitListGearMenuBase::onSave, this));
registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this));
registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this));
@@ -1135,8 +1165,7 @@ void LLOutfitListGearMenuBase::onUpdateItemsVisibility()
if (!mMenu) return;
bool have_selection = getSelectedOutfitID().notNull();
- mMenu->setItemVisible("sepatator1", have_selection);
- mMenu->setItemVisible("sepatator2", have_selection);
+ mMenu->setItemVisible("wear_separator", have_selection);
mMenu->arrangeAndClear(); // update menu height
}
@@ -1181,6 +1210,20 @@ void LLOutfitListGearMenuBase::onAdd()
}
}
+void LLOutfitListGearMenuBase::onSave()
+{
+ const LLUUID &selected_id = getSelectedOutfitID();
+ LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(),
+ [selected_id](const LLSD &notif, const LLSD &resp)
+ {
+ S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp);
+ if (opt == 0)
+ {
+ LLAppearanceMgr::getInstance()->onOutfitFolderCreated(selected_id, true);
+ }
+ });
+}
+
void LLOutfitListGearMenuBase::onTakeOff()
{
// Take off selected outfit.
@@ -1234,15 +1277,6 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)
return false;
}
- // *TODO This condition leads to menu item behavior inconsistent with
- // "Wear" button behavior and should be modified or removed.
- bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == selected_outfit_id;
-
- if ("wear" == param)
- {
- return !is_worn;
- }
-
return true;
}
@@ -1270,10 +1304,29 @@ void LLOutfitListGearMenu::onUpdateItemsVisibility()
if (!mMenu) return;
mMenu->setItemVisible("expand", TRUE);
mMenu->setItemVisible("collapse", TRUE);
- mMenu->setItemVisible("thumbnail", FALSE); // Never visible?
- mMenu->setItemVisible("sepatator3", FALSE);
+ mMenu->setItemVisible("thumbnail", getSelectedOutfitID().notNull());
mMenu->setItemVisible("sort_folders_by_name", FALSE);
LLOutfitListGearMenuBase::onUpdateItemsVisibility();
}
+BOOL LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ if (y >= getLocalRect().getHeight() - getHeaderHeight())
+ {
+ LLSD params;
+ params["inv_type"] = LLInventoryType::IT_CATEGORY;
+ params["thumbnail_id"] = gInventory.getCategory(mFolderID)->getThumbnailUUID();
+ params["item_id"] = mFolderID;
+
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(getToolTip())
+ .sticky_rect(calcScreenRect())
+ .delay_time(LLView::getTooltipTimeout())
+ .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
+ .create_params(params));
+ return TRUE;
+ }
+
+ return LLAccordionCtrlTab::handleToolTip(x, y, mask);
+}
// EOF
diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h
index 66b3165169..6eeccddb07 100644
--- a/indra/newview/lloutfitslist.h
+++ b/indra/newview/lloutfitslist.h
@@ -31,6 +31,7 @@
#include "llpanel.h"
// newview
+#include "llaccordionctrltab.h"
#include "llinventorymodel.h"
#include "lllistcontextmenu.h"
#include "llpanelappearancetab.h"
@@ -147,6 +148,9 @@ protected:
static void renameOutfit(const LLUUID& outfit_cat_id);
+ void onThumbnail(const LLUUID &outfit_cat_id);
+ void onSave(const LLUUID &outfit_cat_id);
+
private:
LLOutfitListBase* mOutfitList;
};
@@ -178,6 +182,7 @@ private:
void onAdd();
void onTakeOff();
void onRename();
+ void onSave();
void onCreate(const LLSD& data);
bool onEnable(LLSD::String param);
bool onVisible(LLSD::String param);
@@ -193,7 +198,27 @@ protected:
/*virtual*/ void onUpdateItemsVisibility();
};
-/**
+class LLOutfitAccordionCtrlTab : public LLAccordionCtrlTab
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
+ {
+ Optional<LLUUID> cat_id;
+ Params() : cat_id("cat_id") {}
+ };
+
+ virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
+ protected:
+ LLOutfitAccordionCtrlTab(const LLOutfitAccordionCtrlTab::Params &p)
+ : LLAccordionCtrlTab(p),
+ mFolderID(p.cat_id)
+ {}
+ friend class LLUICtrlFactory;
+
+ LLUUID mFolderID;
+};
+ /**
* @class LLOutfitsList
*
* A list of agents's outfits from "My Outfits" inventory category
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp
index b938b30479..3ed444a5af 100644
--- a/indra/newview/llpanelteleporthistory.cpp
+++ b/indra/newview/llpanelteleporthistory.cpp
@@ -1067,6 +1067,12 @@ void LLTeleportHistoryPanel::onGearMenuAction(const LLSD& userdata)
LLLandmarkActions::getSLURLfromPosGlobal(globalPos,
boost::bind(&LLTeleportHistoryPanel::gotSLURLCallback, _1));
}
+ else if ("remove" == command_name)
+ {
+ LLTeleportHistoryStorage::getInstance()->removeItem(index);
+ LLTeleportHistoryStorage::getInstance()->save();
+ showTeleportHistory();
+ }
}
bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const
@@ -1121,7 +1127,8 @@ bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const
if ("teleport" == command_name
|| "view" == command_name
|| "show_on_map" == command_name
- || "copy_slurl" == command_name)
+ || "copy_slurl" == command_name
+ || "remove" == command_name)
{
if (!mLastSelectedFlatlList)
{
diff --git a/indra/newview/llperfstats.cpp b/indra/newview/llperfstats.cpp
index 64e66d520b..8718f7e7b0 100644
--- a/indra/newview/llperfstats.cpp
+++ b/indra/newview/llperfstats.cpp
@@ -69,7 +69,7 @@ namespace LLPerfStats
{
assert_main_thread();
// these following variables are proxies for pipeline statics we do not need a two way update (no llviewercontrol handler)
- if( tuningFlag & NonImpostors ){ gSavedSettings.setU32("IndirectMaxNonImpostors", nonImpostors); };
+ if( tuningFlag & NonImpostors ){ gSavedSettings.setU32("RenderAvatarMaxNonImpostors", nonImpostors); };
if( tuningFlag & ReflectionDetail ){ gSavedSettings.setS32("RenderReflectionDetail", reflectionDetail); };
if( tuningFlag & FarClip ){ gSavedSettings.setF32("RenderFarClip", farClip); };
if( tuningFlag & UserMinDrawDistance ){ gSavedSettings.setF32("AutoTuneRenderFarClipMin", userMinDrawDistance); };
@@ -378,7 +378,7 @@ namespace LLPerfStats
auto count = countNearbyAvatars(std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance));
if( count != tunables.nonImpostors )
{
- tunables.updateNonImposters( (count < LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER)?count : LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER );
+ tunables.updateNonImposters( (count < LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER)?count : 0 );
LL_DEBUGS("AutoTune") << "There are " << count << "avatars within " << std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance) << "m of the camera" << LL_ENDL;
}
}
diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp
index 1c14acd843..f3455bb4f0 100644
--- a/indra/newview/llplacesinventorypanel.cpp
+++ b/indra/newview/llplacesinventorypanel.cpp
@@ -120,3 +120,13 @@ S32 LLPlacesInventoryPanel::notify(const LLSD& info)
}
return 0;
}
+
+BOOL LLPlacesInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept, std::string &tooltip_msg)
+{
+ if (mAcceptsDragAndDrop)
+ {
+ return LLInventoryPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+ }
+ return FALSE;
+}
diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h
index 3c27964ec5..81b623b045 100644
--- a/indra/newview/llplacesinventorypanel.h
+++ b/indra/newview/llplacesinventorypanel.h
@@ -47,11 +47,14 @@ public:
LLPlacesInventoryPanel(const Params& p);
~LLPlacesInventoryPanel();
- LLFolderView * createFolderRoot(LLUUID root_id );
+ LLFolderView * createFolderRoot(LLUUID root_id ) override;
void saveFolderState();
void restoreFolderState();
- virtual S32 notify(const LLSD& info) ;
+ virtual S32 notify(const LLSD& info) override;
+
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept, std::string &tooltip_msg) override;
private:
LLSaveFolderState* mSavedFolderState;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 3a08f748d6..359f2c5b1c 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -86,6 +86,7 @@
#include "llfloaterimsession.h"
#include "llfloaterinspect.h"
#include "llfloaterinventorysettings.h"
+#include "llfloaterinventorythumbnailshelper.h"
#include "llfloaterjoystick.h"
#include "llfloaterlagmeter.h"
#include "llfloaterland.h"
@@ -247,6 +248,7 @@ public:
"group_picker",
"hud",
"incoming_call",
+ "inventory_thumbnails_helper",
"linkreplace",
"mem_leaking",
"marketplace_validation",
@@ -333,7 +335,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("build", "floater_tools.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTools>);
LLFloaterReg::add("build_options", "floater_build_options.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBuildOptions>);
LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>);
-
+
LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);
LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCameraPresets>);
LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);
@@ -380,6 +382,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIncomingCallDialog>);
LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInspect>);
+ LLFloaterReg::add("inventory_thumbnails_helper", "floater_inventory_thumbnails_helper.xml", (LLFloaterBuildFunc) &LLFloaterReg::build<LLFloaterInventoryThumbnailsHelper>);
LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
LLFloaterReg::add("task_properties", "floater_task_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
LLFloaterReg::add("inventory_settings", "floater_inventory_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInventorySettings>);
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 0a0a19d095..b9a7c9448f 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -668,15 +668,14 @@ void LLViewerInventoryCategory::setVersion(S32 version)
mVersion = version;
}
-bool LLViewerInventoryCategory::fetch()
+bool LLViewerInventoryCategory::fetch(S32 expiry_seconds)
{
if((VERSION_UNKNOWN == getVersion())
&& mDescendentsRequested.hasExpired()) //Expired check prevents multiple downloads.
{
LL_DEBUGS(LOG_INV) << "Fetching category children: " << mName << ", UUID: " << mUUID << LL_ENDL;
- const F32 FETCH_TIMER_EXPIRY = 10.0f;
mDescendentsRequested.reset();
- mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
+ mDescendentsRequested.setTimerExpirySec(expiry_seconds);
std::string url;
if (gAgent.getRegion())
@@ -685,7 +684,7 @@ bool LLViewerInventoryCategory::fetch()
}
else
{
- LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL;
+ LL_WARNS_ONCE(LOG_INV) << "agent region is null" << LL_ENDL;
}
if (!url.empty() || AISAPI::isAvailable())
{
@@ -709,7 +708,13 @@ LLViewerInventoryCategory::EFetchType LLViewerInventoryCategory::getFetching()
void LLViewerInventoryCategory::setFetching(LLViewerInventoryCategory::EFetchType fetching)
{
- if (fetching > mFetching) // allow a switch from normal to recursive
+ if (fetching == FETCH_FAILED)
+ {
+ const F32 FETCH_FAILURE_EXPIRY = 60.0f;
+ mDescendentsRequested.setTimerExpirySec(FETCH_FAILURE_EXPIRY);
+ mFetching = fetching;
+ }
+ else if (fetching > mFetching) // allow a switch from normal to recursive
{
if (mDescendentsRequested.hasExpired() || (mFetching == FETCH_NONE))
{
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index e043285ffb..0d0ccc6643 100644
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -209,13 +209,15 @@ public:
S32 getVersion() const;
void setVersion(S32 version);
- // Returns true if a fetch was issued (not nessesary in progress).
- bool fetch();
+ // Returns true if a fetch was issued (not nessesary in progress).
+ // no requests will happen during expiry_seconds even if fetch completed
+ bool fetch(S32 expiry_seconds = 10);
typedef enum {
FETCH_NONE = 0,
FETCH_NORMAL,
FETCH_RECURSIVE,
+ FETCH_FAILED, // back off
} EFetchType;
EFetchType getFetching();
// marks as fetch being in progress or as done
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index ada898b98c..55e43352bc 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -4158,6 +4158,12 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id);
for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it)
{
+ if (anim_it->first != object_id)
+ {
+ // elements with the same key are always contiguous, bail if we went past the
+ // end of this object's animations
+ break;
+ }
if (anim_it->second == animation_id)
{
anim_found = TRUE;
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 5bc7523be1..a53bd982d4 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -2043,6 +2043,7 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
{
LL_WARNS() << objectp->mID << " has self as parent, skipping!"
<< LL_ENDL;
+ ++iter;
continue;
}
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 7b24b9ee02..f12fc3babc 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -834,7 +834,11 @@ void LLVOAvatarSelf::stopMotionFromSource(const LLUUID& source_id)
for (AnimSourceIterator motion_it = mAnimationSources.find(source_id); motion_it != mAnimationSources.end(); )
{
gAgent.sendAnimationRequest(motion_it->second, ANIM_REQUEST_STOP);
- mAnimationSources.erase(motion_it++);
+ mAnimationSources.erase(motion_it);
+ // Must find() after each erase() to deal with potential iterator invalidation
+ // This also ensures that we don't go past the end of this source's animations
+ // into those of another source.
+ motion_it = mAnimationSources.find(source_id);
}
diff --git a/indra/newview/skins/default/xui/en/floater_inventory_thumbnails_helper.xml b/indra/newview/skins/default/xui/en/floater_inventory_thumbnails_helper.xml
new file mode 100644
index 0000000000..aa3500bac2
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_inventory_thumbnails_helper.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_resize="false"
+ height="600"
+ layout="topleft"
+ min_height="175"
+ min_width="500"
+ name="contents"
+ help_topic="contents"
+ title="Inventory Thumbnails Helper"
+ width="800">
+ <scroll_list
+ top="20"
+ height="350"
+ draw_stripes="true"
+ draw_heading="true"
+ follows="all"
+ layout="topleft"
+ left="8"
+ multi_select="true"
+ name="inventory_thumbnails_list"
+ right="-8"
+ tool_tip="Paste items from your inventory">
+ <scroll_list.columns
+ dynamic_width="true"
+ label="Inventory Item"
+ name="item_name"
+ relative_width="0.4" />
+ <scroll_list.columns
+ dynamic_width="true"
+ label="Existing Texture"
+ name="existing_texture"
+ relative_width="0.3" />
+ <scroll_list.columns
+ dynamic_width="true"
+ label="New Texture"
+ name="new_texture"
+ relative_width="0.3" />
+ </scroll_list>
+ <text_editor
+ top="375"
+ height="140"
+ follows="all"
+ left="8"
+ right="-8"
+ name="output_log"
+ font="Monospace"
+ text_color="0.1 0.5 0.1 1.0"
+ width="480">
+ </text_editor>
+ <button
+ follows="left|bottom"
+ height="20"
+ label="Paste items from Inventory"
+ layout="topleft"
+ left="10"
+ name="paste_items_btn"
+ bottom="-60"
+ width="235" />
+ <button
+ follows="left|bottom"
+ height="20"
+ label="Paste textures from Inventory"
+ layout="topleft"
+ left_delta="0"
+ name="paste_textures_btn"
+ top_delta="26 "
+ width="235" />
+ <button
+ follows="left|bottom"
+ height="20"
+ label="Write Thumbnails"
+ layout="topleft"
+ left_delta="0"
+ name="write_thumbnails_btn"
+ top_delta="26 "
+ width="235" />
+ <button
+ follows="left|bottom"
+ height="20"
+ label="Log items with no thumbnail"
+ layout="bottomleft"
+ right="-10"
+ name="log_missing_thumbnails_btn"
+ bottom="60"
+ width="235" />
+ <button
+ follows="left|bottom"
+ height="20"
+ label="Clear thumbnails from pasted items"
+ layout="bottomleft"
+ right="-10"
+ name="clear_thumbnails_btn"
+ top_delta="26"
+ width="235" />
+
+
+</floater> \ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
index d82c453e5f..8cf0479b27 100644
--- a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
@@ -35,7 +35,7 @@
layout="topleft"
name="Folder Wearables Separator" />
<menu_item_call
- label="Replace Current Outfit"
+ label="Replace current outfit"
layout="topleft"
name="Replace Outfit">
<menu_item_call.on_click
@@ -43,7 +43,7 @@
parameter="replaceoutfit" />
</menu_item_call>
<menu_item_call
- label="Add To Current Outfit"
+ label="Add folder items"
layout="topleft"
name="Add To Outfit">
<menu_item_call.on_click
@@ -51,7 +51,7 @@
parameter="addtooutfit" />
</menu_item_call>
<menu_item_call
- label="Remove From Current Outfit"
+ label="Take off folder items"
layout="topleft"
name="Remove From Outfit">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
index 0ca505dd5d..c93a92b2b7 100755
--- a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
@@ -3,7 +3,7 @@
layout="topleft"
name="Outfit">
<menu_item_call
- label="Wear - Replace Current Outfit"
+ label="Replace current outfit"
layout="topleft"
name="wear_replace">
<on_click
@@ -16,7 +16,7 @@
parameter="wear_replace" />
</menu_item_call>
<menu_item_call
- label="Wear - Add to Current Outfit"
+ label="Add outfit items"
layout="topleft"
name="wear_add">
<on_click
@@ -29,7 +29,7 @@
parameter="wear_add" />
</menu_item_call>
<menu_item_call
- label="Take Off - Remove from Current Outfit"
+ label="Take off outfit items"
layout="topleft"
name="take_off">
<on_click
@@ -41,17 +41,60 @@
function="Outfit.OnVisible"
parameter="take_off" />
</menu_item_call>
+ <menu_item_separator/>
<menu_item_call
- label="Image..."
- layout="topleft"
- name="thumbnail">
+ label="Image..."
+ layout="topleft"
+ name="thumbnail">
+ <on_click
+ function="Outfit.Thumbnail" />
+ </menu_item_call>
+ <menu_item_call
+ label="Edit outfit"
+ layout="topleft"
+ name="edit">
+ <on_click
+ function="Outfit.Edit" />
+ <on_visible
+ function="Outfit.OnVisible"
+ parameter="edit" />
+ </menu_item_call>
+ <menu_item_call
+ label="Rename outfit"
+ layout="topleft"
+ name="rename">
+ <on_click
+ function="Outfit.Rename" />
+ <on_enable
+ function="Outfit.OnEnable"
+ parameter="rename" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save to this outfit"
+ layout="topleft"
+ name="save">
+ <on_click
+ function="Outfit.Save" />
+ </menu_item_call>
+ <menu_item_separator>
+ <on_visible
+ function="Outfit.OnVisible"
+ parameter="delete" />
+ </menu_item_separator>
+ <menu_item_call
+ label="Delete outfit"
+ layout="topleft"
+ name="delete">
<on_click
- function="Outfit.Thumbnail" />
+ function="Outfit.Delete" />
+ <on_visible
+ function="Outfit.OnVisible"
+ parameter="delete" />
</menu_item_call>
- <menu_item_separator name="sepatator1" />
+ <menu_item_separator/>
<menu
height="175"
- label="New Clothes"
+ label="New clothes"
layout="topleft"
left_delta="0"
mouse_opaque="false"
@@ -157,7 +200,7 @@
</menu>
<menu
height="85"
- label="New Body Parts"
+ label="New body parts"
layout="topleft"
left_delta="0"
mouse_opaque="false"
@@ -197,35 +240,4 @@
parameter="eyes" />
</menu_item_call>
</menu>
- <menu_item_separator name="sepatator2" />
- <menu_item_call
- label="Edit Outfit"
- layout="topleft"
- name="edit">
- <on_click
- function="Outfit.Edit" />
- <on_visible
- function="Outfit.OnVisible"
- parameter="edit" />
- </menu_item_call>
- <menu_item_call
- label="Rename Outfit"
- layout="topleft"
- name="rename">
- <on_click
- function="Outfit.Rename" />
- <on_enable
- function="Outfit.OnEnable"
- parameter="rename" />
- </menu_item_call>
- <menu_item_call
- label="Delete Outfit"
- layout="topleft"
- name="delete">
- <on_click
- function="Outfit.Delete" />
- <on_visible
- function="Outfit.OnVisible"
- parameter="delete" />
- </menu_item_call>
</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 0295ef8ccd..324c08e68e 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -264,7 +264,7 @@
layout="topleft"
name="Folder Wearables Separator" />
<menu_item_call
- label="Replace Current Outfit"
+ label="Replace current outfit"
layout="topleft"
name="Replace Outfit">
<menu_item_call.on_click
@@ -272,7 +272,7 @@
parameter="replaceoutfit" />
</menu_item_call>
<menu_item_call
- label="Add To Current Outfit"
+ label="Add folder items"
layout="topleft"
name="Add To Outfit">
<menu_item_call.on_click
@@ -280,7 +280,7 @@
parameter="addtooutfit" />
</menu_item_call>
<menu_item_call
- label="Remove From Current Outfit"
+ label="Take off folder items"
layout="topleft"
name="Remove From Outfit">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
index e216962d12..e7a453766b 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
@@ -4,7 +4,7 @@
visible="false"
name="Gear Outfit">
<menu_item_call
- label="Wear - Replace Current Outfit"
+ label="Replace current outfit"
layout="topleft"
name="wear">
<on_click
@@ -17,7 +17,7 @@
parameter="wear" />
</menu_item_call>
<menu_item_call
- label="Wear - Add to Current Outfit"
+ label="Add outfit items"
layout="topleft"
name="wear_add">
<on_click
@@ -25,9 +25,11 @@
<on_enable
function="Gear.OnEnable"
parameter="wear_add" />
+ <on_visible
+ function="Gear.OnVisible"/>
</menu_item_call>
<menu_item_call
- label="Take Off - Remove from Current Outfit"
+ label="Take off outfit items"
layout="topleft"
name="take_off">
<on_click
@@ -39,19 +41,88 @@
function="Gear.OnVisible"
parameter="take_off" />
</menu_item_call>
+ <menu_item_separator name="wear_separator" />
<menu_item_call
label="Image..."
layout="topleft"
name="thumbnail">
+ <on_click
+ function="Gear.Thumbnail" />
+ </menu_item_call>
+ <menu_item_call
+ label="Rename outfit"
+ layout="topleft"
+ name="rename">
+ <on_click
+ function="Gear.Rename" />
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="rename" />
+ <on_visible
+ function="Gear.OnVisible"
+ parameter="rename" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save to this outfit"
+ layout="topleft"
+ name="save">
+ <on_click
+ function="Gear.Save" />
+ <on_visible
+ function="Gear.OnVisible"/>
+ </menu_item_call>
+ <menu_item_separator>
+ <on_visible
+ function="Gear.OnVisible"
+ parameter="delete" />
+ </menu_item_separator>
+ <menu_item_call
+ label="Delete outfit"
+ layout="topleft"
+ name="delete_outfit">
+ <on_click
+ function="Gear.Delete" />
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="delete" />
+ <on_visible
+ function="Gear.OnVisible"
+ parameter="delete" />
+ </menu_item_call>
+ <menu_item_separator>
+ <on_visible
+ function="Gear.OnVisible"/>
+ </menu_item_separator>
+ <menu_item_check
+ label="Sort folders always by name"
+ layout="topleft"
+ name="sort_folders_by_name">
+ <on_click
+ function="Gear.SortByName" />
+ <on_check
+ function="CheckControl"
+ parameter="OutfitGallerySortByName" />
+ </menu_item_check>
+ <menu_item_call
+ label="Expand all folders"
+ layout="topleft"
+ name="expand">
+ <on_click
+ function="Gear.Expand" />
+ </menu_item_call>
+ <menu_item_call
+ label="Collapse all folders"
+ layout="topleft"
+ name="collapse">
<on_click
- function="Gear.Thumbnail" />
+ function="Gear.Collapse" />
</menu_item_call>
- <menu_item_separator name="sepatator1" />
+ <menu_item_separator/>
<!-- copied (with minor modifications) from menu_inventory_add.xml -->
<!-- *TODO: generate dynamically? -->
<menu
height="175"
- label="New Clothes"
+ label="New clothes"
layout="topleft"
left_delta="0"
mouse_opaque="false"
@@ -165,7 +236,7 @@
</menu>
<menu
height="85"
- label="New Body Parts"
+ label="New body parts"
layout="topleft"
left_delta="0"
mouse_opaque="false"
@@ -206,57 +277,4 @@
</menu_item_call>
</menu>
<!-- copied from menu_inventory_add.xml -->
-
- <menu_item_separator name="sepatator2" />
- <menu_item_call
- label="Expand all folders"
- layout="topleft"
- name="expand">
- <on_click
- function="Gear.Expand" />
- </menu_item_call>
- <menu_item_call
- label="Collapse all folders"
- layout="topleft"
- name="collapse">
- <on_click
- function="Gear.Collapse" />
- </menu_item_call>
- <menu_item_call
- label="Rename Outfit"
- layout="topleft"
- name="rename">
- <on_click
- function="Gear.Rename" />
- <on_enable
- function="Gear.OnEnable"
- parameter="rename" />
- <on_visible
- function="Gear.OnVisible"
- parameter="rename" />
- </menu_item_call>
- <menu_item_call
- label="Delete Outfit"
- layout="topleft"
- name="delete_outfit">
- <on_click
- function="Gear.Delete" />
- <on_enable
- function="Gear.OnEnable"
- parameter="delete" />
- <on_visible
- function="Gear.OnVisible"
- parameter="delete" />
- </menu_item_call>
- <menu_item_separator name="sepatator3" />
- <menu_item_check
- label="Sort Folders Always by Name"
- layout="topleft"
- name="sort_folders_by_name">
- <on_click
- function="Gear.SortByName" />
- <on_check
- function="CheckControl"
- parameter="OutfitGallerySortByName" />
- </menu_item_check>
</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
index 8c8bb29baf..522e41df42 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
@@ -3,7 +3,7 @@
layout="topleft"
name="Outfit">
<menu_item_call
- label="Wear - Replace Current Outfit"
+ label="Replace current outfit"
layout="topleft"
name="wear_replace">
<on_click
@@ -16,7 +16,7 @@
parameter="wear_replace" />
</menu_item_call>
<menu_item_call
- label="Wear - Add to Current Outfit"
+ label="Add outfit items"
layout="topleft"
name="wear_add">
<on_click
@@ -29,7 +29,7 @@
parameter="wear_add" />
</menu_item_call>
<menu_item_call
- label="Take Off - Remove from Current Outfit"
+ label="Take off outfit items"
layout="topleft"
name="take_off">
<on_click
@@ -41,19 +41,26 @@
function="Outfit.OnVisible"
parameter="take_off" />
</menu_item_call>
+ <menu_item_separator />
<menu_item_call
- label="Edit Outfit"
- layout="topleft"
- name="edit">
+ label="Image..."
+ layout="topleft"
+ name="thumbnail">
+ <on_click
+ function="Outfit.Thumbnail" />
+ </menu_item_call>
+ <menu_item_call
+ label="Edit outfit"
+ layout="topleft"
+ name="edit">
<on_click
function="Outfit.Edit" />
<on_visible
function="Outfit.OnVisible"
parameter="edit" />
</menu_item_call>
- <menu_item_separator />
<menu_item_call
- label="Rename Outfit"
+ label="Rename outfit"
layout="topleft"
name="rename">
<on_click
@@ -63,7 +70,19 @@
parameter="rename" />
</menu_item_call>
<menu_item_call
- label="Delete Outfit"
+ label="Save to this outfit"
+ layout="topleft"
+ name="save">
+ <on_click
+ function="Outfit.Save" />
+ </menu_item_call>
+ <menu_item_separator>
+ <on_visible
+ function="Outfit.OnVisible"
+ parameter="delete" />
+ </menu_item_separator>
+ <menu_item_call
+ label="Delete outfit"
layout="topleft"
name="delete">
<on_click
diff --git a/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml b/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml
index 153e5a70a9..9bbfdd4291 100644
--- a/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml
+++ b/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml
@@ -49,4 +49,17 @@
function="TeleportHistory.GearMenu.Enable"
parameter="copy_slurl" />
</menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Remove from history"
+ layout="topleft"
+ name="remove_from_history">
+ <on_click
+ function="TeleportHistory.GearMenu.Action"
+ parameter="remove" />
+ <on_enable
+ function="TeleportHistory.GearMenu.Enable"
+ parameter="remove" />
+ </menu_item_call>
</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 660f4b62c7..7ff3f3c2b3 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3530,6 +3530,21 @@ function="World.EnvPreset"
function="Advanced.WebContentTest"
parameter="http://duckduckgo.com"/>
</menu_item_call>
+ <menu_item_call
+ label="Inventory Thumbnails Helper"
+ name="Inventory Thumbnails Helper"
+ shortcut="control|alt|shift|X">
+ <menu_item_call.on_click
+ function="Floater.Show"
+ parameter="inventory_thumbnails_helper" />
+ </menu_item_call>
+ <menu_item_call
+ label="FB Connect Test"
+ name="FB Connect Test">
+ <menu_item_call.on_click
+ function="Advanced.WebContentTest"
+ parameter="https://cryptic-ridge-1632.herokuapp.com/"/>
+ </menu_item_call>
<menu_item_call
label="Dump SelectMgr"
name="Dump SelectMgr">
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index d642ea162c..4d0c7712cd 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -12261,6 +12261,50 @@ Would you like to save them first?
notext="No"
yestext="Yes"/>
</notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ConfirmOverwriteOutfit"
+ type="alertmodal">
+ <unique/>
+This will replace the items in the
+selected outfit with the items you
+are wearing now.
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Confirm before overwriting outfit"
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="Save"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ClearInventoryThumbnailsWarning"
+ type="alertmodal">
+ You are about to remove thumbnail images from the inventory items in the list. This change cannot be undone.
+
+ Would you like to proceed?
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelbuttons"
+ notext="No"
+ yestext="Yes"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="WriteInventoryThumbnailsWarning"
+ type="alertmodal">
+ You are about to overwrite thumbnail images for some or all of the inventory items in the list. This change cannot be undone.
+
+ Would you like to proceed?
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelbuttons"
+ notext="No"
+ yestext="Yes"/>
+ </notification>
<notification
icon="notifytip.tga"
diff --git a/indra/newview/skins/default/xui/en/panel_settings_water.xml b/indra/newview/skins/default/xui/en/panel_settings_water.xml
index f19629df26..da2f915425 100644
--- a/indra/newview/skins/default/xui/en/panel_settings_water.xml
+++ b/indra/newview/skins/default/xui/en/panel_settings_water.xml
@@ -65,7 +65,7 @@
Density Exponent:
</text>
<slider
- decimal_digits="1"
+ decimal_digits="2"
follows="left|top"
height="16"
increment="0.01"
diff --git a/scripts/code_tools/modified_strings.py b/scripts/code_tools/modified_strings.py
index e7a9d239dc..20ed1b0555 100644
--- a/scripts/code_tools/modified_strings.py
+++ b/scripts/code_tools/modified_strings.py
@@ -162,6 +162,8 @@ def make_translation_table(mod_tree, base_tree, lang, args):
filename = mod_blob.path
if mod_blob.type == "tree": # directory, skip
continue
+ if args.files and os.path.basename(filename) not in args.files:
+ continue # process only the specified files
if args.verbose:
print(filename)
@@ -325,9 +327,11 @@ if __name__ == "__main__":
parser.add_argument("--deleted", action="store_true", default = False, help="show all translated entities which don't exist in english")
parser.add_argument("--skip_spreadsheet", action="store_true", default = False, help="skip creating the translation spreadsheet")
parser.add_argument("--rev", help="revision with modified strings, default HEAD", default="HEAD")
- parser.add_argument("--rev_base", help="previous revision to compare against, default master", default="master")
+ parser.add_argument("--rev_base", help="previous revision to compare against, default main", default="main")
parser.add_argument("--base_lang", help="base language, default en (normally leave unchanged - other values are only useful for testing)", default="en")
parser.add_argument("--lang", help="target languages, or 'all_valid' or 'supported'; default is 'supported'", nargs="+", default = ["supported"])
+ parser.add_argument("--files", help='list of files to process', metavar='F', type=str, nargs='*')
+ parser.add_argument("--outfile", help='name of the output file', type=str, nargs='?', default="SL_Translations.xlsx")
args = parser.parse_args()
cwd = os.getcwd()
@@ -370,7 +374,7 @@ if __name__ == "__main__":
print("Target language(s) are", ",".join(sorted(langs)))
sys.stdout.flush()
- outfile = "SL_Translations.xlsx"
+ outfile = args.outfile
try:
f = open(outfile,"a+")
f.close()