diff options
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 ¬if, 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 ¬if, 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() |