diff --git a/doc/responders.txt b/doc/responders.txt new file mode 100644 index 000000000..b1bf31f50 --- /dev/null +++ b/doc/responders.txt @@ -0,0 +1,82 @@ +All Responders are derived from ResponderBase, however you normally do never derived from that directly yourself. +Instead, Responder classes are derived from one of: + +1. Responder base classes + + ResponderHeadersOnly -- Derived classes are used with HTTPClient::head or HTTPClient::getHeaderOnly. + ResponderWithCompleted -- Derived classes implement completed(U32, std::string const&, LLSD const&), + or completedRaw(U32, std::string const&, LLChannelDescriptors const&, buffer_ptr_t const&) + if the response is not (always) LLSD. + ResponderWithResult -- Derived classes implement result(LLSD const&) and optionally + errorWithContent(U32, std::string const&, LLSD const&) OR error(U32, std::string const&). + +2. Special base classes + + ResponderIgnoreBody -- Same as ResponderWithResult but already implements result() that ignored the body. + LLAssetUploadResponder -- Derived from ResponderWithResult. Base class for responders that upload assets via capabilities. + LegacyPolledResponder -- Used for old code that needs polling (do not use). + + +There is one non-base class Responder with a more general purpose: + +3. Special purpose responders: + + ResponderIgnore -- Derived from ResponderIgnoreBody. Used for "fire and forget" requests as it ignores any response. + + +4. Signatures. + +Every final (derived) responder class must implement 'getName(void) const' and 'getHTTPTimeoutPolicy(void)', +except the base classes (this is to alert the developer they have to implement getName as it is pure virtual). + +For example: + + extern AIHTTPTimeoutPolicy myResponder_timeout; // Add 'P(myResponder)' to indra/llmessage/aihttptimeoutpolicy.cpp. + + class MyResponder : public LLHTTPClient::SomeResponderBaseClass { + ... + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return myResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "MyResponder"; } + }; + +Note the convention that the name of a AIHTTPTimeoutPolicy (what goes between the brackets of P()) is +the class name minus any 'AI' or 'LL' prefix and starting with a lowercase character. + +Then, depending on the three main base classes that was derived from, the signatures should be: + + class MyResponder1 : public LLHTTPClient::ResponderHeadersOnly { + /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); + // See for example PostImageResponder + ... + }; + + class MyResponder2 : public LLHTTPClient::ResponderWithCompleted { + /*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer); + // See for example PostImageRedirectResponder + >>>OR<<< + + /*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content); // See for example LLImportPostResponder + + }; + + class MyResponder3 : public LLHTTPClient::ResponderWithResult { + /*virtual*/ void result(const LLSD& content); // See for example LLInventoryModelFetchItemResponder + /*virtual*/ void error(U32 status, const std::string& reason); + >>>OR instead error()<<< + /*virtual*/ void errorWithContent(U32 status, const std::string& reason, const LLSD& content); + // See for example LLSDMessage::EventResponder + }; + +Finally, if a responder derived from ResponderWithCompleted or ResponderWithResult needs to process +individual headers, you need to override 'needsHeaders': + + /*virtual*/ bool needsHeaders(void) const { return true; } // See for example LLWebProfileResponders::PostImageResponder + // which will cause this to be called: + /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); + +And if it needs redirection to work (you'll get an assert if you forget this and it is being redirected): + + /*virtual*/ bool followRedir(void) const { return true; } // See for example LLWebProfileResponders::ConfigResponder + +This is not necessary for ResponderHeadersOnly because that already defines both. + diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 17d86d77f..4a5de2da8 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -562,8 +562,10 @@ namespace dc fake_channel const warning(1, "WARNING "); fake_channel const curl(1, "CURL "); fake_channel const curlio(1, "CURLIO "); +fake_channel const curltr(1, "CURLTR "); fake_channel const statemachine(1, "STATEMACHINE"); fake_channel const notice(1, "NOTICE "); +fake_channel const snapshot(0, "SNAPSHOT "); } // namespace dc } // namespace debug diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 329f0448e..e14fb18b9 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -82,8 +82,10 @@ struct fake_channel { extern LL_COMMON_API fake_channel const warning; extern LL_COMMON_API fake_channel const curl; extern LL_COMMON_API fake_channel const curlio; +extern LL_COMMON_API fake_channel const curltr; extern LL_COMMON_API fake_channel const statemachine; extern LL_COMMON_API fake_channel const notice; +extern LL_COMMON_API fake_channel const snapshot; } // namespace dc } // namespace debug @@ -173,8 +175,8 @@ extern LL_COMMON_API fake_channel const notice; #include #if CWDEBUG_LOCATION #include // Needed for 'backtrace'. -#include "llpreprocessor.h" #endif +#include "llpreprocessor.h" // LL_COMMON_API #include #define CWD_API __attribute__ ((visibility("default"))) @@ -387,21 +389,21 @@ void InstanceTracker::dump(void) // Print "Entering " << \a data to channel \a cntrl and increment // debugging output indentation until the end of the current scope. #define DoutEntering(cntrl, data) \ - int __slviewer_debug_indentation = 2; \ + int __slviewer_debug_indentation = 2; \ { \ LIBCWD_TSD_DECLARATION; \ if (LIBCWD_DO_TSD_MEMBER_OFF(::libcwd::libcw_do) < 0) \ { \ ::libcwd::channel_set_bootstrap_st __libcwd_channel_set(LIBCWD_DO_TSD(::libcwd::libcw_do) LIBCWD_COMMA_TSD); \ - bool on; \ + bool __slviewer_debug_on; \ { \ using namespace LIBCWD_DEBUGCHANNELS; \ - on = (__libcwd_channel_set|cntrl).on; \ + __slviewer_debug_on = (__libcwd_channel_set|cntrl).on; \ } \ - if (on) \ + if (__slviewer_debug_on) \ Dout(cntrl, "Entering " << data); \ else \ - __slviewer_debug_indentation = 0; \ + __slviewer_debug_indentation = 0; \ } \ } \ debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 425741601..461bb4cc5 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -106,9 +106,9 @@ // on a compiler that doesn't need this hack. #define AI_NEED_ACCESS_CC (defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || (__GNUC__ < 4))) -template struct AIReadAccessConst; -template struct AIReadAccess; -template struct AIWriteAccess; +template struct AIReadAccessConst; +template struct AIReadAccess; +template struct AIWriteAccess; template struct AIAccessConst; template struct AIAccess; template struct AISTAccessConst; @@ -225,17 +225,17 @@ protected: * * */ -template +template class AIThreadSafe : public AIThreadSafeBits { protected: // Only these may access the object (through ptr()). - friend struct AIReadAccessConst; - friend struct AIReadAccess; - friend struct AIWriteAccess; + friend struct AIReadAccessConst; + friend struct AIReadAccess; + friend struct AIWriteAccess; // Locking control. - AIRWLock mRWLock; + RWLOCK mRWLock; // For use by AIThreadSafeDC AIThreadSafe(void) { } @@ -310,20 +310,20 @@ public: * * which is not possible with AITHREADSAFE. */ -template -class AIThreadSafeDC : public AIThreadSafe +template +class AIThreadSafeDC : public AIThreadSafe { public: // Construct a wrapper around a default constructed object. - AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; } + AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; } // Allow an arbitrary parameter to be passed for construction. - template AIThreadSafeDC(T2 const& val) { new (AIThreadSafe::ptr()) T(val); } + template AIThreadSafeDC(T2 const& val) { new (AIThreadSafe::ptr()) T(val); } }; /** * @brief Read lock object and provide read access. */ -template +template struct AIReadAccessConst { //! Internal enum for the lock-type of the AI*Access object. @@ -336,8 +336,8 @@ struct AIReadAccessConst }; //! Construct a AIReadAccessConst from a constant AIThreadSafe. - AIReadAccessConst(AIThreadSafe const& wrapper, bool high_priority = false) - : mWrapper(const_cast&>(wrapper)), + AIReadAccessConst(AIThreadSafe const& wrapper, bool high_priority = false) + : mWrapper(const_cast&>(wrapper)), mState(readlocked) #if AI_NEED_ACCESS_CC , mIsCopyConstructed(false) @@ -369,14 +369,14 @@ struct AIReadAccessConst protected: //! Constructor used by AIReadAccess. - AIReadAccessConst(AIThreadSafe& wrapper, state_type state) + AIReadAccessConst(AIThreadSafe& wrapper, state_type state) : mWrapper(wrapper), mState(state) #if AI_NEED_ACCESS_CC , mIsCopyConstructed(false) #endif { } - AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. + AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. state_type const mState; //!< The lock state that mWrapper is in. #if AI_NEED_ACCESS_CC @@ -393,39 +393,39 @@ private: /** * @brief Read lock object and provide read access, with possible promotion to write access. */ -template -struct AIReadAccess : public AIReadAccessConst +template +struct AIReadAccess : public AIReadAccessConst { - typedef typename AIReadAccessConst::state_type state_type; - using AIReadAccessConst::readlocked; + typedef typename AIReadAccessConst::state_type state_type; + using AIReadAccessConst::readlocked; //! Construct a AIReadAccess from a non-constant AIThreadSafe. - AIReadAccess(AIThreadSafe& wrapper, bool high_priority = false) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(high_priority); } + AIReadAccess(AIThreadSafe& wrapper, bool high_priority = false) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(high_priority); } protected: //! Constructor used by AIWriteAccess. - AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { } + AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { } - friend struct AIWriteAccess; + friend struct AIWriteAccess; }; /** * @brief Write lock object and provide read/write access. */ -template -struct AIWriteAccess : public AIReadAccess +template +struct AIWriteAccess : public AIReadAccess { - using AIReadAccessConst::readlocked; - using AIReadAccessConst::read2writelocked; - using AIReadAccessConst::writelocked; - using AIReadAccessConst::write2writelocked; + using AIReadAccessConst::readlocked; + using AIReadAccessConst::read2writelocked; + using AIReadAccessConst::writelocked; + using AIReadAccessConst::write2writelocked; //! Construct a AIWriteAccess from a non-constant AIThreadSafe. - AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();} + AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();} //! Promote read access to write access. - explicit AIWriteAccess(AIReadAccess& access) - : AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked) + explicit AIWriteAccess(AIReadAccess& access) + : AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked) { if (this->mState == read2writelocked) { diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 08d6e2110..f64200c95 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -397,6 +397,43 @@ public: #endif }; +#if LL_DEBUG +class LL_COMMON_API AINRLock +{ +private: + int read_locked; + int write_locked; + + mutable bool mAccessed; + mutable AIThreadID mTheadID; + + void accessed(void) const + { + if (!mAccessed) + { + mAccessed = true; + mTheadID.reset(); + } + else + { + llassert_always(mTheadID.equals_current_thread()); + } + } + +public: + AINRLock(void) : read_locked(false), write_locked(false), mAccessed(false) { } + + bool isLocked() const { return read_locked || write_locked; } + + void rdlock(bool high_priority = false) { accessed(); ++read_locked; } + void rdunlock() { --read_locked; } + void wrlock() { llassert(!isLocked()); accessed(); ++write_locked; } + void wrunlock() { --write_locked; } + void wr2rdlock() { llassert(false); } + void rd2wrlock() { llassert(false); } +}; +#endif + //============================================================================ void LLThread::lockData() diff --git a/indra/llmessage/aihttptimeout.cpp b/indra/llmessage/aihttptimeout.cpp index 61ed5ca86..499921a2e 100644 --- a/indra/llmessage/aihttptimeout.cpp +++ b/indra/llmessage/aihttptimeout.cpp @@ -152,7 +152,7 @@ void HTTPTimeout::upload_finished(void) // ^ ^ ^ ^ ^ ^ ^ ^ // | | | | | | | | bool HTTPTimeout::data_received(size_t n/*,*/ -#ifdef CWDEBUG +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) ASSERT_ONLY_COMMA(bool upload_error_status) #else ASSERT_ONLY_COMMA(bool) @@ -208,8 +208,9 @@ bool HTTPTimeout::lowspeed(size_t bytes) // // We do this as follows: we create low_speed_time (in seconds) buckets and fill them with the number // of bytes received during that second. We also keep track of the sum of all bytes received between 'now' - // and 'now - llmax(starttime, low_speed_time)'. Then if that period reaches at least low_speed_time - // seconds, and the transfer rate (sum / low_speed_time) is less than low_speed_limit, we abort. + // and 'llmax(starttime, now - low_speed_time)'. Then if that period reaches at least low_speed_time + // seconds (when now >= starttime + low_speed_time) and the transfer rate (sum / low_speed_time) is + // less than low_speed_limit, we abort. // When are we? S32 second = (sClockCount - mLowSpeedClock) * sClockWidth; @@ -244,6 +245,7 @@ bool HTTPTimeout::lowspeed(size_t bytes) if (s == -1) { mBucket = 0; // It doesn't really matter where we start. + mOverwriteSecond = second + low_speed_time; mTotalBytes = bytes; mBuckets[mBucket] = bytes; return false; @@ -257,11 +259,17 @@ bool HTTPTimeout::lowspeed(size_t bytes) bucket = 0; if (++s == second) break; - mTotalBytes -= mBuckets[bucket]; + if (s >= mOverwriteSecond) + { + mTotalBytes -= mBuckets[bucket]; + } mBuckets[bucket] = 0; } mBucket = bucket; - mTotalBytes -= mBuckets[mBucket]; + if (s >= mOverwriteSecond) + { + mTotalBytes -= mBuckets[mBucket]; + } mTotalBytes += bytes; mBuckets[mBucket] = bytes; @@ -286,29 +294,40 @@ bool HTTPTimeout::lowspeed(size_t bytes) } // Calculate how long the data transfer may stall until we should timeout. + // + // Assume 6 buckets: 0 1 2 3 4 5 (low_speed_time == 6) + // Seconds since start of transfer: 4 5 6 7 8 9 (mOverwriteSecond == 10) + // Current second: ^ + // Data in buckets: A B w x y z (mTotalBytes = A + B; w, x, y and z are undefined) + // + // Obviously, we need to stall at LEAST till second low_speed_time before we can "timeout". + // And possibly more if mTotalBytes is already >= mintotalbytes. + // + // The code below finds 'max_stall_time', so that when from now on the buckets + // are filled with 0, then at 'second + max_stall_time' we should time out, + // meaning that the resulting mTotalBytes after writing 0 at that second + // will be less than mintotalbytes and 'second + max_stall_time' >= low_speed_time. + // llassert_always(mintotalbytes > 0); - S32 max_stall_time = 0; - U32 dropped_bytes = 0; - while(1) + S32 max_stall_time = low_speed_time - second; // Minimum value. + // Note that if max_stall_time <= 0 here, then second >= low_speed_time and + // thus mTotalBytes >= mintotalbytes because we didn't timeout already above. + if (mTotalBytes >= mintotalbytes) { - if (++bucket == low_speed_time) // The next second the next bucket will be emptied. - bucket = 0; - ++max_stall_time; - dropped_bytes += mBuckets[bucket]; - // Note how, when max_stall_time == low_speed_time, dropped_bytes has - // to be equal to mTotalBytes, the sum of all vector elements. - llassert(max_stall_time < low_speed_time || dropped_bytes == mTotalBytes); - // AIFIXME: This is a bug and should really be fixed instead of just be a warning. - if (!(max_stall_time < low_speed_time || dropped_bytes == mTotalBytes)) + // In this case max_stall_time has a minimum value equal to when we will reach mOverwriteSecond, + // because that is the first second at which mTotalBytes will decrease. + max_stall_time = mOverwriteSecond - second - 1; + U32 total_bytes = mTotalBytes; + int bucket = -1; // Must be one less as the start bucket, which corresponds with mOverwriteSecond (and thus with the current max_stall_time). + do { - llwarns << "ASSERT max_stall_time < low_speed_time || dropped_bytes == mTotalBytes failed... aborting. " - "max_stall_time = " << max_stall_time << "; low_speed_time = " << low_speed_time << - "; dropped_bytes = " << dropped_bytes << "; mTotalBytes = " << mTotalBytes << llendl; - break; + ++max_stall_time; // Next second (mOverwriteSecond upon entry of the loop). + ++bucket; // The next bucket (0 upon entry of the loop). + // Once we reach the end of the vector total_bytes MUST have reached 0 exactly and we should have left this loop. + llassert_always(bucket < low_speed_time); + total_bytes -= mBuckets[bucket]; // Empty this bucket. } - // And thus the following will certainly abort. - if (second + max_stall_time >= low_speed_time && mTotalBytes - dropped_bytes < mintotalbytes) - break; + while(total_bytes >= 1); // Use 1 here instead of mintotalbytes, to test that total_bytes indeed always reaches zero. } // If this function isn't called again within max_stall_time seconds, we stalled. mStalled = sClockCount + max_stall_time / sClockWidth; diff --git a/indra/llmessage/aihttptimeout.h b/indra/llmessage/aihttptimeout.h index 6bb89ee20..ab7895a3b 100644 --- a/indra/llmessage/aihttptimeout.h +++ b/indra/llmessage/aihttptimeout.h @@ -81,6 +81,7 @@ class HTTPTimeout : public LLRefCount { bool mLowSpeedOn; // Set while uploading or downloading data. bool mUploadFinished; // Used to keep track of whether upload_finished was called yet. S32 mLastSecond; // The time at which lowspeed() was last called, in seconds since mLowSpeedClock. + S32 mOverwriteSecond; // The second at which the first bucket of this transfer will be overwritten. U32 mTotalBytes; // The sum of all bytes in mBuckets. U64 mLowSpeedClock; // Clock count at which low speed detection (re)started. U64 mStalled; // The clock count at which this transaction is considered to be stalling if nothing is transfered anymore. diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index 0a302e601..e07cc4ba4 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "llpreprocessor.h" #include #define COMPILING_DEBUG_LIBCURL_CC diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 6aa14e8d6..e35d5b5dd 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -82,6 +82,7 @@ set(llui_SOURCE_FILES set(llui_HEADER_FILES CMakeLists.txt + ailist.h llalertdialog.h llbutton.h llcallbackmap.h diff --git a/indra/llui/ailist.h b/indra/llui/ailist.h new file mode 100644 index 000000000..14ef05f0c --- /dev/null +++ b/indra/llui/ailist.h @@ -0,0 +1,645 @@ +/** + * @file ailist.h + * @brief A linked list with iterators that advance when their element is deleted. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 05/02/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AILIST_H +#define AILIST_H + +#include + +template +class AIList; + +template +class AIConstListIterator; + +/* + * AINode + * + * The actual data stored in a std::list when using AIList. + * T is the template parameter used with AIList. + * count are the number of iterators that currently point to this element. + * dead is 0 or 1, where 1 means that the element was erased from the AIList + * (but not yet from the internal std::list, so that any iterators to it + * are NOT invalidated). + */ +template +struct AINode { + T mElement; // The user visual data. + mutable unsigned short count; // Number of iterators pointing to this element. + mutable unsigned short dead; // Whether or not the element is "erased". + + AINode(void) : count(0), dead(0) { } + explicit AINode(T const& __val) : mElement(__val), count(0), dead(0) { } + + // Equivalence operators. + // __node may not be dead. Dead nodes in the list are "skipped" (obviously), meaning that they are never equal or always unequal. + bool operator==(AINode const& __node) const { llassert(!__node.dead); return mElement == __node.mElement && !dead; } + bool operator!=(AINode const& __node) const { llassert(!__node.dead); return mElement != __node.mElement || dead; } + + // Default ordering for sort(). + bool operator<(AINode const& __node) const { return mElement < __node.mElement; } +}; + +/* + * AIListIterator + * + * A non-const iterator to an element of AIList. + */ +template +class AIListIterator { + private: + typedef AIListIterator _Self; + typedef std::list > _Container; + typedef typename _Container::iterator _Iterator; + + _Container* mContainer; // A pointer to the associated container, or NULL when (still) singular. + // Note that this code does not allow a container to be destructed while + // any non-end iterators still exist (although that could be legal). + // If an iterator points to end() and the container is destructed then + // this pointer is NOT reset to NULL. + _Iterator mIterator; // Internal iterator to element of mContainer (or singular when mContainer is NULL). + + // Increment reference counter for mIterator (if not singular and not end()). + void ref(void) + { + if (mContainer && mContainer->end() != mIterator) + { + // It would be bad a new iterator was created that pointed to a dead element. + // Also, as a sanity check, make sure there aren't a ridiculous number of iterators + // pointing to this element. + llassert(!mIterator->dead && mIterator->count < 100); + ++(mIterator->count); + } + } + + // Decrement reference counter for mIterator (if not singular and not end()). + // If this was the last iterator pointing to a dead element, then really erase it. + void unref(void) + { + if (mContainer && mContainer->end() != mIterator) + { + llassert(mIterator->count > 0); + if (--(mIterator->count) == 0 && mIterator->dead) + { + mContainer->erase(mIterator); + } + } + } + + public: + // Some standard typedefs that have to exist for iterators. + typedef typename _Iterator::difference_type difference_type; + typedef typename _Iterator::iterator_category iterator_category; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + + // Construct a singular iterator. + AIListIterator(void) : mContainer(NULL) { } + + // Construct an iterator to a given element of std::list. Only for internal use by AIList. + AIListIterator(_Container* __c, _Iterator const& __i) : mContainer(__c), mIterator(__i) + { + llassert(mContainer); + ref(); + } + + // Copy constructor. + AIListIterator(AIListIterator const& __i) : mContainer(__i.mContainer), mIterator(__i.mIterator) + { + ref(); + } + + // Destructor. + ~AIListIterator() + { + unref(); + } + + // Assignment operator. + _Self& operator=(_Self const& __x) + { + unref(); // We no longer point to whatever we were pointing. + mContainer = __x.mContainer; + mIterator = __x.mIterator; + llassert(mContainer); + ref(); // We now point an(other) element. + return *this; + } + + // Dereference operator. + reference operator*() const + { + // Iterator may not be singular or dead. + llassert(mContainer && !mIterator->dead); + return mIterator->mElement; + } + + // Dereference operator. + pointer operator->() const + { + // Iterator may not be singular or dead. + llassert(mContainer && !mIterator->dead); + return &mIterator->mElement; + } + + // Pre-increment operator (not being singular is implied). + _Self& operator++() + { + _Iterator cur = mIterator; // Make copy of mIterator. + ++cur; // Advance it. + unref(); // We will no longer be pointing to this element. This might invalidate mIterator! + while(cur != mContainer->end() && cur->dead) // Advance till the first non-dead element. + { + ++cur; + } + mIterator = cur; // Put result back into mIterator. + ref(); // We are now pointing to a valid element again. + return *this; + } + + // Post-increment operator (not being singular is implied). + _Self operator++(int) + { + _Self tmp = *this; + this->operator++(); + return tmp; + } + + // Pre-decrement operator (not being singular is implied). + _Self& operator--() + { + _Iterator cur = mIterator; // See operator++(). + --cur; + unref(); + while(cur->dead) + { + --cur; + } + mIterator = cur; + ref(); + return *this; + } + + // Post-decrement operator (not being singular is implied). + _Self operator--(int) + { + _Self tmp = *this; + this->operator--(); + return tmp; + } + + // Equivalence operators. + // We allow comparing with dead iterators, because if one of them is not-dead + // then the result is "unequal" anyway, which is probably what you want. + bool operator==(_Self const& __x) const { return mIterator == __x.mIterator; } + bool operator!=(_Self const& __x) const { return mIterator != __x.mIterator; } + + friend class AIList; + friend class AIConstListIterator; + template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); + template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); + + // Return the total number of iterators pointing the element that this iterator is pointing to. + // Unless the iterator points to end, a positive number will be returned since this iterator is + // pointing to the element. If it points to end (or is singular) then 0 is returned. + int count(void) const + { + if (mContainer && mIterator != mContainer->end()) + { + return mIterator->count; + } + return 0; + } +}; + +/* + * AIConstListIterator + * + * A const iterator to an element of AIList. + * + * Because this class is very simular to AIListIterator, see above for detailed comments. + */ +template +class AIConstListIterator { + private: + typedef AIConstListIterator _Self; + typedef std::list > _Container; + typedef typename _Container::iterator _Iterator; + typedef typename _Container::const_iterator _ConstIterator; + typedef AIListIterator iterator; + + _Container const* mContainer; + _Iterator mConstIterator; // This has to be an _Iterator instead of _ConstIterator, because the compiler doesn't accept a const_iterator for erase yet (C++11 does). + + void ref(void) + { + if (mContainer && mContainer->end() != mConstIterator) + { + llassert(mConstIterator->count < 100); + mConstIterator->count++; + } + } + + void unref(void) + { + if (mContainer && mContainer->end() != mConstIterator) + { + llassert(mConstIterator->count > 0); + mConstIterator->count--; + if (mConstIterator->count == 0 && mConstIterator->dead) + { + const_cast<_Container*>(mContainer)->erase(mConstIterator); + } + } + } + + public: + typedef typename _ConstIterator::difference_type difference_type; + typedef typename _ConstIterator::iterator_category iterator_category; + typedef T value_type; + typedef T const* pointer; + typedef T const& reference; + + AIConstListIterator(void) : mContainer(NULL) { } + AIConstListIterator(_Container const* __c, _Iterator const& __i) : mContainer(__c), mConstIterator(__i) + { + llassert(mContainer); + ref(); + } + // Allow to construct a const_iterator from an iterator. + AIConstListIterator(iterator const& __x) : mContainer(__x.mContainer), mConstIterator(__x.mIterator) + { + ref(); + } + AIConstListIterator(AIConstListIterator const& __i) : mContainer(__i.mContainer), mConstIterator(__i.mConstIterator) + { + ref(); + } + ~AIConstListIterator() + { + unref(); + } + + _Self& operator=(_Self const& __x) + { + unref(); + mContainer = __x.mContainer; + mConstIterator = __x.mConstIterator; + llassert(mContainer); + ref(); + return *this; + } + + // Allow to assign from a non-const iterator. + _Self& operator=(iterator const& __x) + { + unref(); + mContainer = __x.mContainer; + mConstIterator = __x.mIterator; + llassert(mContainer); + ref(); + return *this; + } + + reference operator*() const + { + llassert(mContainer && !mConstIterator->dead); + return mConstIterator->mElement; + } + + pointer operator->() const + { + llassert(mContainer && !mConstIterator->dead); + return &mConstIterator->mElement; + } + + _Self& operator++() + { + _Iterator cur = mConstIterator; + ++cur; + unref(); + while(cur != mContainer->end() && cur->dead) + { + ++cur; + } + mConstIterator = cur; + ref(); + return *this; + } + + _Self operator++(int) + { + _Self tmp = *this; + this->operator++(); + return tmp; + } + + _Self& operator--() + { + _Iterator cur = mConstIterator; + --cur; + unref(); + while(cur->dead) + { + --cur; + } + mConstIterator = cur; + ref(); + return *this; + } + + _Self operator--(int) + { + _Self tmp = *this; + this->operator--(); + return tmp; + } + + bool operator==(_Self const& __x) const { return mConstIterator == __x.mConstIterator; } + bool operator!=(_Self const& __x) const { return mConstIterator != __x.mConstIterator; } + bool operator==(iterator const& __x) const { return mConstIterator == __x.mIterator; } + bool operator!=(iterator const& __x) const { return mConstIterator != __x.mIterator; } + + template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); + template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); + + int count(void) const + { + if (mContainer && mConstIterator != mContainer->end()) + { + return mConstIterator->count; + } + return 0; + } +}; + +template +inline bool operator==(AIListIterator const& __x, AIConstListIterator const& __y) +{ + return __x.mIterator == __y.mConstIterator; +} + +template +inline bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y) +{ + return __x.mIterator != __y.mConstIterator; +} + +/* + * AIList + * + * A linked list that allows elements to be erased while one or more iterators + * are still pointing to that element, after which pre-increment and pre-decrement + * operators still work. + * + * For example: + * + * for (AIList::iterator i = l.begin(); i != l.end(); ++i) + * { + * int x = *i; + * f(i); // Might erase any element of list l. + * // Should not dereference i anymore here (because it might be erased). + * } + */ +template +class AIList { + private: + typedef std::list > _Container; + typedef typename _Container::iterator _Iterator; + typedef typename _Container::const_iterator _ConstIterator; + + _Container mContainer; + size_t mSize; + + public: + typedef T value_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef AIListIterator iterator; + typedef AIConstListIterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + // Default constructor. Create an empty list. + AIList(void) : mSize(0) { } +#ifdef LL_DEBUG + // Destructor calls clear() to check if there are no iterators left pointing to this list. Destructing an empty list is trivial. + ~AIList() { clear(); } +#endif + + // Construct a list with __n elements of __val. + explicit AIList(size_type __n, value_type const& __val = value_type()) : mContainer(__n, AINode(__val)), mSize(__n) { } + + // Copy constructor. + AIList(AIList const& __list) : mSize(0) + { + for (_ConstIterator __i = __list.mContainer.begin(); __i != __list.mContainer.end(); ++__i) + { + if (!__i->dead) + { + mContainer.push_back(AINode(__i->mElement)); + ++mSize; + } + } + } + + // Construct a list from the range [__first, __last>. + template + AIList(_InputIterator __first, _InputIterator __last) : mSize(0) + { + for (_InputIterator __i = __first; __i != __last; ++__i) + { + mContainer.push_back(AINode(*__i)); + ++mSize; + } + } + + // Assign from another list. + AIList& operator=(AIList const& __list) + { + clear(); + for (_ConstIterator __i = __list.mContainer.begin(); __i != __list.mContainer.end(); ++__i) + { + if (!__i->dead) + { + mContainer.push_back(AINode(__i->mElement)); + ++mSize; + } + } + return *this; + } + + iterator begin() + { + _Iterator __i = mContainer.begin(); + while(__i != mContainer.end() && __i->dead) + { + ++__i; + } + return iterator(&mContainer, __i); + } + + const_iterator begin() const + { + _Iterator __i = const_cast<_Container&>(mContainer).begin(); + while(__i != mContainer.end() && __i->dead) + { + ++__i; + } + return const_iterator(&mContainer, __i); + } + + iterator end() { return iterator(&mContainer, mContainer.end()); } + const_iterator end() const { return const_iterator(&mContainer, const_cast<_Container&>(mContainer).end()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + bool empty() const { return mSize == 0; } + size_type size() const { return mSize; } + size_type max_size() const { return mContainer.max_size(); } + + reference front() { iterator __i = begin(); return *__i; } + const_reference front() const { const_iterator __i = begin(); return *__i; } + reference back() { iterator __i = end(); --__i; return *__i; } + const_reference back() const { const_iterator __i = end(); --__i; return *__i; } + + void push_front(value_type const& __x) + { + mContainer.push_front(AINode(__x)); + ++mSize; + } + void pop_front() + { + iterator __i = begin(); + erase(__i); + } + void push_back(value_type const& __x) + { + mContainer.push_back(AINode(__x)); + ++mSize; + } + void pop_back() + { + iterator __i = end(); + --__i; + erase(__i); + } + + iterator insert(iterator __position, value_type const& __x) + { + ++mSize; + return iterator(&mContainer, mContainer.insert(__position.mIterator, AINode(__x))); + } + + void clear() + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.clear(); + mSize = 0; + } + + iterator erase(iterator __position) + { + // Mark the element __position points to as being erased. + // Iterator may not be singular, point to end, or be dead already. + // Obviously count must be larger than zero since __position is still pointing to it. + llassert(__position.mContainer == &mContainer && __position.mIterator != mContainer.end() && __position.mIterator->count > 0 && !__position.mIterator->dead); + __position.mIterator->dead = 1; + --mSize; + return ++__position; + } + + // Remove all elements, designated by the iterator where, for which *where == __val. + void remove(value_type const& __val) + { + _Iterator const __e = mContainer.end(); + for (_Iterator __i = mContainer.begin(); __i != __e;) + { + if (!__i->dead && __i->mElement == __val) + { + --mSize; + if (__i->count == 0) + { + mContainer.erase(__i++); + continue; + } + // Mark the element as being erased. + __i->dead = 1; + } + ++__i; + } + } + + void sort() + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.sort(); + } + template + struct PredWrapper + { + _StrictWeakOrdering mPred; + PredWrapper(_StrictWeakOrdering const& pred) : mPred(pred) { } + bool operator()(AINode const& __x, AINode const& __y) const { return mPred(__x.mElement, __y.mElement); } + }; + template + void sort(_StrictWeakOrdering const& pred) + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.sort(PredWrapper<_StrictWeakOrdering>(pred)); + } +}; + +#endif diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 1cbb52dab..b91b0df43 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -911,6 +911,8 @@ void LLFloater::setMinimized(BOOL minimize) // Lose keyboard focus when minimized releaseFocus(); + // Also reset mLockedView and mLastKeyboardFocus, to avoid that we get focus back somehow. + gFocusMgr.removeKeyboardFocusWithoutCallback(this); for (S32 i = 0; i < 4; i++) { diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 66834a6a1..7817fd0d5 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -55,6 +55,8 @@ BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa // virtual LLFocusableElement::~LLFocusableElement() { + // Make sure nothing is pointing to us anymore! + gFocusMgr.removeKeyboardFocusWithoutCallback(this); delete mFocusLostCallback; delete mFocusReceivedCallback; delete mFocusChangedCallback; @@ -131,6 +133,7 @@ LLFocusMgr::LLFocusMgr() mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), + mLastDefaultKeyboardFocus( NULL ), mKeystrokesOnly(FALSE), mTopCtrl( NULL ), mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true @@ -171,6 +174,23 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) } } +void LLFocusMgr::restoreDefaultKeyboardFocus(LLFocusableElement* current_default_focus) +{ + if (current_default_focus && mDefaultKeyboardFocus == current_default_focus) + { + setDefaultKeyboardFocus(mLastDefaultKeyboardFocus); + mLastDefaultKeyboardFocus = NULL; + } +} + +void LLFocusMgr::restoreKeyboardFocus(LLFocusableElement* current_focus) +{ + if (current_focus && mKeyboardFocus == current_focus) + { + setKeyboardFocus(mLastKeyboardFocus); + mLastKeyboardFocus = NULL; + } +} void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) { @@ -328,11 +348,22 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f { mLockedView = NULL; } - - if( mKeyboardFocus == focus ) + if (mKeyboardFocus == focus) { mKeyboardFocus = NULL; } + if (mLastKeyboardFocus == focus) + { + mLastKeyboardFocus = NULL; + } + if (mDefaultKeyboardFocus == focus) + { + mDefaultKeyboardFocus = NULL; + } + if (mLastDefaultKeyboardFocus == focus) + { + mLastDefaultKeyboardFocus = NULL; + } } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 14c711b5e..5f1466f5d 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -84,6 +84,7 @@ public: // Keyboard Focus void setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus. + void restoreKeyboardFocus(LLFocusableElement* current_focus); LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } BOOL childHasKeyboardFocus( const LLView* parent ) const; @@ -103,7 +104,8 @@ public: // If setKeyboardFocus(NULL) is called, and there is a non-NULL default // keyboard focus view, focus goes there. JC - void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; } + void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mLastDefaultKeyboardFocus = mDefaultKeyboardFocus; mDefaultKeyboardFocus = default_focus; } + void restoreDefaultKeyboardFocus(LLFocusableElement* current_default_focus); LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } @@ -132,6 +134,7 @@ private: LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object LLFocusableElement* mLastKeyboardFocus; // who last had focus LLFocusableElement* mDefaultKeyboardFocus; + LLFocusableElement* mLastDefaultKeyboardFocus; BOOL mKeystrokesOnly; // Top View diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 8e740384a..1206d3b76 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -77,7 +77,8 @@ LLScrollableContainerView::LLScrollableContainerView( const std::string& name, mReserveScrollCorner( FALSE ), mMinAutoScrollRate( MIN_AUTO_SCROLL_RATE ), mMaxAutoScrollRate( MAX_AUTO_SCROLL_RATE ), - mScrolledView( scrolled_view ) + mScrolledView( scrolled_view ), + mPassBackToChildren(true) { if( mScrolledView ) { @@ -218,7 +219,7 @@ BOOL LLScrollableContainerView::handleScrollWheel( S32 x, S32 y, S32 clicks ) { // Give event to my child views - they may have scroll bars // (Bad UI design, but technically possible.) - if (LLUICtrl::handleScrollWheel(x,y,clicks)) + if (mPassBackToChildren && LLUICtrl::handleScrollWheel(x,y,clicks)) return TRUE; // When the vertical scrollbar is visible, scroll wheel diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index a5002a341..2e1cf1dd3 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -70,6 +70,7 @@ public: virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } void setBorderVisible( BOOL b ); + void setPassBackToChildren(bool b) { mPassBackToChildren = b; } void scrollToShowRect( const LLRect& rect, const LLRect& constraint); void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); } @@ -128,6 +129,7 @@ private: F32 mMinAutoScrollRate; F32 mMaxAutoScrollRate; bool mHideScrollbar; + bool mPassBackToChildren; }; diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 58cb3e5fa..5e388e233 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -302,8 +302,8 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) LLCtrlQuery query = getTabOrderQuery(); // sort things such that the default tab group is at the front query.setSorter(DefaultTabGroupFirstSorter::getInstance()); - child_list_t result = query(this); - if(result.size() > 0) + viewList_t result = query(this); + if(!result.empty()) { LLUICtrl * ctrl = static_cast(result.front()); if(!ctrl->hasFocus()) @@ -322,7 +322,7 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) { LLCtrlQuery query = getTabOrderQuery(); query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - child_list_t result = query(this); + viewList_t result = query(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.front()); @@ -358,7 +358,7 @@ BOOL LLUICtrl::focusLastItem(BOOL prefer_text_fields) { LLCtrlQuery query = getTabOrderQuery(); query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - child_list_t result = query(this); + viewList_t result = query(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.back()); @@ -372,7 +372,7 @@ BOOL LLUICtrl::focusLastItem(BOOL prefer_text_fields) } } // no text field found, or we don't care about text fields - child_list_t result = getTabOrderQuery().run(this); + viewList_t result = getTabOrderQuery().run(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.back()); @@ -395,7 +395,7 @@ BOOL LLUICtrl::focusNextItem(BOOL text_fields_only) { query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); } - child_list_t result = query(this); + viewList_t result = query(this); return focusNext(result); } @@ -407,7 +407,7 @@ BOOL LLUICtrl::focusPrevItem(BOOL text_fields_only) { query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); } - child_list_t result = query(this); + viewList_t result = query(this); return focusPrev(result); } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 07f99132b..6c1bce689 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -518,21 +518,21 @@ LLRect LLView::getRequiredRect() BOOL LLView::focusNextRoot() { - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + viewList_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusNext(result); } BOOL LLView::focusPrevRoot() { - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + viewList_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusPrev(result); } // static -BOOL LLView::focusNext(LLView::child_list_t & result) +BOOL LLView::focusNext(viewList_t& result) { - LLView::child_list_iter_t focused = result.end(); - for(LLView::child_list_iter_t iter = result.begin(); + viewList_t::iterator focused = result.end(); + for(viewList_t::iterator iter = result.begin(); iter != result.end(); ++iter) { @@ -542,7 +542,7 @@ BOOL LLView::focusNext(LLView::child_list_t & result) break; } } - LLView::child_list_iter_t next = focused; + viewList_t::iterator next = focused; next = (next == result.end()) ? result.begin() : ++next; while(next != focused) { @@ -565,10 +565,10 @@ BOOL LLView::focusNext(LLView::child_list_t & result) } // static -BOOL LLView::focusPrev(LLView::child_list_t & result) +BOOL LLView::focusPrev(viewList_t& result) { - LLView::child_list_reverse_iter_t focused = result.rend(); - for(LLView::child_list_reverse_iter_t iter = result.rbegin(); + viewList_t::reverse_iterator focused = result.rend(); + for(viewList_t::reverse_iterator iter = result.rbegin(); iter != result.rend(); ++iter) { @@ -578,7 +578,7 @@ BOOL LLView::focusPrev(LLView::child_list_t & result) break; } } - LLView::child_list_reverse_iter_t next = focused; + viewList_t::reverse_iterator next = focused; next = (next == result.rend()) ? result.rbegin() : ++next; while(next != focused) { @@ -1170,16 +1170,13 @@ void LLView::drawChildren() LLView* rootp = getRootView(); ++sDepth; - for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) + for (child_list_const_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) { - child_list_reverse_iter_t child = child_iter++; - LLView *viewp = *child; - - if (viewp == NULL) - { - continue; - } - + LLView *viewp = *child_iter; + if (viewp == NULL) + { + continue; + } if (viewp->getVisible() && /*viewp != focus_view && */viewp->getRect().isValid()) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 233e56bbc..2ec5ce58c 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -57,6 +57,7 @@ #include "llinitparam.h" #include "llfocusmgr.h" #include +#include "ailist.h" const U32 FOLLOWS_NONE = 0x00; const U32 FOLLOWS_LEFT = 0x01; @@ -231,7 +232,7 @@ public: SNAP_BOTTOM }; - typedef std::list child_list_t; + typedef AIList child_list_t; typedef child_list_t::iterator child_list_iter_t; typedef child_list_t::const_iterator child_list_const_iter_t; typedef child_list_t::reverse_iterator child_list_reverse_iter_t; @@ -582,9 +583,9 @@ public: static std::string escapeXML(const std::string& xml, std::string& indent); // focuses the item in the list after the currently-focused item, wrapping if necessary - static BOOL focusNext(LLView::child_list_t & result); + static BOOL focusNext(viewList_t& result); // focuses the item in the list before the currently-focused item, wrapping if necessary - static BOOL focusPrev(LLView::child_list_t & result); + static BOOL focusPrev(viewList_t& result); // returns query for iterating over controls in tab order static const LLCtrlQuery & getTabOrderQuery(); diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 1c9ba6b8f..bb3bab353 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -74,9 +74,10 @@ filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewLis viewList_t LLViewQuery::run(LLView* view) const { viewList_t result; + viewList_t const child_list(view->getChildList()->begin(), view->getChildList()->end()); // prefilter gets immediate children of view - filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); + filterResult_t pre = runFilters(view, child_list, mPreFilters); if(!pre.first && !pre.second) { // not including ourselves or the children @@ -113,26 +114,26 @@ viewList_t LLViewQuery::run(LLView* view) const void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { - LLView::child_list_t views(*(view->getChildList())); + viewList_t views(view->getChildList()->begin(), view->getChildList()->end()); if (mSorterp) { (*mSorterp)(view, views); // sort the children per the sorter } - for(LLView::child_list_iter_t iter = views.begin(); + for(viewList_t::iterator iter = views.begin(); iter != views.end(); - iter++) + ++iter) { viewList_t indiv_children = this->run(*iter); filtered_children.splice(filtered_children.end(), indiv_children); } } -filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const +filterResult_t LLViewQuery::runFilters(LLView* view, viewList_t const& children, filterList_t const& filters) const { filterResult_t result = filterResult_t(TRUE, TRUE); for(filterList_const_iter_t iter = filters.begin(); iter != filters.end(); - iter++) + ++iter) { filterResult_t filtered = (**iter)(view, children); result.first = result.first && filtered.first; @@ -143,7 +144,7 @@ filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, class SortByTabOrder : public LLQuerySorter, public LLSingleton { - /*virtual*/ void operator() (LLView * parent, LLView::child_list_t &children) const + /*virtual*/ void operator() (LLView* parent, viewList_t& children) const { children.sort(LLCompareByTabOrder(parent->getCtrlOrder())); } diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 98d9bf879..e058985cc 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -126,7 +126,7 @@ public: private: - filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const; + filterResult_t runFilters(LLView* view, viewList_t const& children, filterList_t const& filters) const; filterList_t mPreFilters; filterList_t mPostFilters; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 75eea9830..aef7f4e17 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8732,7 +8732,7 @@ This should be as low as possible, but too low may break functionality LastSnapshotType Comment - Select this as next type of snapshot to take (0 = postcard, 1 = texture, 2 = local image) + Select this as next type of snapshot to take (0 = feed, 1 = postcard, 2 = texture, 3 = local image) Persist 1 Type @@ -14185,6 +14185,50 @@ This should be as low as possible, but too low may break functionality Value 0 + SnapshotFeedKeepAspect + + Comment + When adjusting feed resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotPostcardKeepAspect + + Comment + When adjusting postcard resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotTextureKeepAspect + + Comment + When adjusting texture resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotLocalKeepAspect + + Comment + When adjusting local resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + SnapshotOpenFreezeTime Comment diff --git a/indra/newview/llcontainerview.cpp b/indra/newview/llcontainerview.cpp index be7be72c7..e6d9b0119 100644 --- a/indra/newview/llcontainerview.cpp +++ b/indra/newview/llcontainerview.cpp @@ -278,3 +278,10 @@ void LLContainerView::setDisplayChildren(const BOOL displayChildren) childp->setVisible(mDisplayChildren); } } + +void LLContainerView::setScrollContainer(LLScrollableContainerView* scroll) +{ + mScrollContainer = scroll; + scroll->setPassBackToChildren(false); +} + diff --git a/indra/newview/llcontainerview.h b/indra/newview/llcontainerview.h index 37c2d97f3..1b337b09b 100644 --- a/indra/newview/llcontainerview.h +++ b/indra/newview/llcontainerview.h @@ -61,7 +61,7 @@ public: void showLabel(BOOL show) { mShowLabel = show; } void setDisplayChildren(const BOOL displayChildren); BOOL getDisplayChildren() { return mDisplayChildren; } - void setScrollContainer(LLScrollableContainerView* scroll) {mScrollContainer = scroll;} + void setScrollContainer(LLScrollableContainerView* scroll); private: LLScrollableContainerView* mScrollContainer; diff --git a/indra/newview/llfloatereditui.cpp b/indra/newview/llfloatereditui.cpp index ff67a2069..fbef26b18 100644 --- a/indra/newview/llfloatereditui.cpp +++ b/indra/newview/llfloatereditui.cpp @@ -56,9 +56,9 @@ void LLFloaterEditUI::navigateHierarchyButtonPressed(void* data) const LLView::child_list_t* viewChildren = view->getChildList(); const LLView::child_list_t* parentChildren = parent->getChildList(); //LLView::child_list_t::iterator - std::list::const_iterator itor; - std::list::size_type idx; - std::list::size_type sidx; + LLView::child_list_t::const_iterator itor; + LLView::child_list_t::size_type idx; + LLView::child_list_t::size_type sidx; for(idx = 0,itor = parentChildren->begin();itor!=parentChildren->end();itor++,idx++){ if((*itor)==view)break; } diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 735b033b1..1ef53b269 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -91,8 +91,8 @@ ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- -S32 LLFloaterSnapshot::sUIWinHeightLong = 619 ; -S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 260 ; +S32 LLFloaterSnapshot::sUIWinHeightLong = 625 ; +S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 266 ; S32 LLFloaterSnapshot::sUIWinWidth = 219 ; S32 const THUMBHEIGHT = 159; @@ -111,6 +111,8 @@ S32 BORDER_WIDTH = 6; const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 +static std::string snapshotKeepAspectName(); + ///---------------------------------------------------------------------------- /// Class LLSnapshotLivePreview ///---------------------------------------------------------------------------- @@ -172,6 +174,7 @@ public: BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;} bool getShowFreezeFrameSnapshot() const { return mShowFreezeFrameSnapshot; } LLViewerTexture* getCurrentImage(); + char const* resolutionComboName() const; char const* aspectComboName() const; void setSnapshotType(ESnapshotType type) { mSnapshotType = type; } @@ -305,6 +308,7 @@ public: static void onClickUICheck(LLUICtrl *ctrl, void* data); static void onClickHUDCheck(LLUICtrl *ctrl, void* data); static void onClickKeepOpenCheck(LLUICtrl *ctrl, void* data); + static void onClickKeepAspect(LLUICtrl* ctrl, void* data); static void onCommitQuality(LLUICtrl* ctrl, void* data); static void onCommitFeedResolution(LLUICtrl* ctrl, void* data); static void onCommitPostcardResolution(LLUICtrl* ctrl, void* data); @@ -327,10 +331,14 @@ public: static LLSnapshotLivePreview* getPreviewView(void); static void setResolution(LLFloaterSnapshot* floater, const std::string& comboname, bool visible, bool update_controls = true); static void setAspect(LLFloaterSnapshot* floater, const std::string& comboname, bool update_controls = true); + static void storeAspectSetting(LLComboBox* combo, const std::string& comboname); + static void enforceAspect(LLFloaterSnapshot* floater, F32 new_aspect); + static void enforceResolution(LLFloaterSnapshot* floater, F32 new_aspect); static void updateControls(LLFloaterSnapshot* floater, bool delayed_formatted = false); static void resetFeedAndPostcardAspect(LLFloaterSnapshot* floater); static void updateLayout(LLFloaterSnapshot* floater); static void freezeTime(bool on); + static void keepAspect(LLFloaterSnapshot* view, bool on, bool force = false); static LLHandle sPreviewHandle; @@ -345,7 +353,6 @@ public: LLToolset* mLastToolset; boost::signals2::connection mQualityMouseUpConnection; - LLFocusableElement* mPrevDefaultKeyboardFocus; }; //---------------------------------------------------------------------------- @@ -404,7 +411,7 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLRect& rect) : mNeedsFlash(TRUE), mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), mFormattedDataSize(0), - mSnapshotType(SNAPSHOT_FEED), + mSnapshotType((ESnapshotType)gSavedSettings.getS32("LastSnapshotType")), mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), mShowFreezeFrameSnapshot(FALSE), mCameraPos(LLViewerCamera::getInstance()->getOrigin()), @@ -1716,14 +1723,12 @@ void LLFloaterSnapshot::Impl::freezeTime(bool on) } // Make sure the floater keeps focus so that pressing ESC stops Freeze Time mode. - sInstance->impl.mPrevDefaultKeyboardFocus = gFocusMgr.getDefaultKeyboardFocus(); gFocusMgr.setDefaultKeyboardFocus(sInstance); } else if (gSavedSettings.getBOOL("FreezeTime")) // turning off freeze frame mode { // Restore default keyboard focus. - gFocusMgr.setDefaultKeyboardFocus(sInstance->impl.mPrevDefaultKeyboardFocus); - sInstance->impl.mPrevDefaultKeyboardFocus = NULL; + gFocusMgr.restoreDefaultKeyboardFocus(sInstance); gSnapshotFloaterView->setMouseOpaque(FALSE); @@ -1803,7 +1808,6 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) { - previewp->setSnapshotType(shot_type); LLViewerWindow::ESnapshotType layer_type = (shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL) ? (LLViewerWindow::ESnapshotType)gSavedSettings.getS32("SnapshotLayerType") : @@ -1844,6 +1848,7 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de floater->childSetVisible("more_btn", !is_advance); // the only item hidden in advanced mode floater->childSetVisible("less_btn", is_advance); floater->childSetVisible("type_label2", is_advance); + floater->childSetVisible("keep_aspect", is_advance); floater->childSetVisible("type_label3", is_advance); floater->childSetVisible("format_label", is_advance && is_local); floater->childSetVisible("local_format_combo", is_local); @@ -1899,6 +1904,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD && got_bytes && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLColor4::red : gColors.getColor( "LabelTextColor" )); + std::string target_size_str = gSavedSettings.getBOOL(snapshotKeepAspectName()) ? floater->getString("sourceAR") : floater->getString("targetAR"); + floater->childSetValue("type_label3", target_size_str); bool up_to_date = previewp && previewp->getSnapshotUpToDate(); bool can_upload = up_to_date && !previewp->isUsedBy(shot_type); @@ -1987,6 +1994,11 @@ void LLFloaterSnapshot::Impl::onCommitFeedAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_FEED); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -2000,6 +2012,11 @@ void LLFloaterSnapshot::Impl::onCommitPostcardAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_POSTCARD); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -2026,6 +2043,11 @@ void LLFloaterSnapshot::Impl::onCommitLocalAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_LOCAL); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -2252,6 +2274,18 @@ void LLFloaterSnapshot::Impl::onClickKeepOpenCheck(LLUICtrl* ctrl, void* data) gSavedSettings.setBOOL( "CloseSnapshotOnKeep", !check->get() ); } +// static +void LLFloaterSnapshot::Impl::onClickKeepAspect(LLUICtrl* ctrl, void* data) +{ + LLFloaterSnapshot* view = (LLFloaterSnapshot*)data; + if (view) + { + LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl; + keepAspect(view, check->get()); + updateControls(view); + } +} + // static void LLFloaterSnapshot::Impl::onCommitQuality(LLUICtrl* ctrl, void* data) { @@ -2321,6 +2355,46 @@ static std::string lastSnapshotAspectName() } } +static std::string snapshotKeepAspectName() +{ + switch(gSavedSettings.getS32("LastSnapshotType")) + { + case LLSnapshotLivePreview::SNAPSHOT_FEED: return "SnapshotFeedKeepAspect"; + case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "SnapshotPostcardKeepAspect"; + case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: return "SnapshotTextureKeepAspect"; + default: return "SnapshotLocalKeepAspect"; + } +} + +// static +void LLFloaterSnapshot::Impl::keepAspect(LLFloaterSnapshot* view, bool on, bool force) +{ + DoutEntering(dc::snapshot, "LLFloaterSnapshot::Impl::keepAspect(view, " << on << ", " << force << ")"); + bool cur_on = gSavedSettings.getBOOL(snapshotKeepAspectName()); + if ((!force && cur_on == on) || + gSavedSettings.getBOOL("RenderUIInSnapshot") || + gSavedSettings.getBOOL("RenderHUDInSnapshot")) + { + // No change. + return; + } + view->childSetValue("keep_aspect", on); + gSavedSettings.setBOOL(snapshotKeepAspectName(), on); + if (on) + { + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + S32 w = llround(view->childGetValue("snapshot_width").asReal(), 1.0); + S32 h = llround(view->childGetValue("snapshot_height").asReal(), 1.0); + gSavedSettings.setS32(lastSnapshotWidthName(), w); + gSavedSettings.setS32(lastSnapshotHeightName(), h); + comboSetCustom(view, previewp->resolutionComboName()); + enforceAspect(view, (F32)w / h); + } + } +} + // static void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool update_controls) { @@ -2332,192 +2406,12 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool return; } - S32 new_width = 0; - S32 new_height = 0; - F32 new_aspect = 0; LLSnapshotLivePreview* previewp = getPreviewView(); -#if 0 // Broken -- not doing this for now. - LLSnapshotLivePreview::ESnapshotType shot_type = (LLSnapshotLivePreview::ESnapshotType)gSavedSettings.getS32("LastSnapshotType"); - // Is the snapshot was already used (saved or uploaded) and no manual changes - // have been made since to the current destination type, and the raw snapshot - // is still up to date with regard to visibility of UI, HUD and BufferType etc? - if (previewp && previewp->isUsed() && !previewp->isOverriddenBy(shot_type) && previewp->getRawSnapshotUpToDate()) - { - previewp->getSize(new_width, new_height); - new_aspect = previewp->getAspect(); - S32 const old_width = new_width; - S32 const old_height = new_height; - F32 const old_aspect = new_aspect; - S32 raw_width; - S32 raw_height; - previewp->getRawSize(raw_width, raw_height); - F32 const raw_aspect = (F32)raw_width / raw_height; - bool fixed_crop = false; - bool fixed_size = false; - bool fixed_scale = false; - bool done = false; - bool fail = false; - while (!done) - { - // Attempt to change the size and aspect so the raw snapshot can also be used for this new destination. - S32 w, h, crop_offset; - bool crop_vertically; - previewp->setSize(new_width, new_height); - previewp->setAspect(new_aspect); - LLSnapshotLivePreview::EAspectSizeProblem ret = previewp->getAspectSizeProblem(w, h, crop_vertically, crop_offset); - switch(ret) - { - case LLSnapshotLivePreview::ASPECTSIZE_OK: - done = true; // Don't change anything (else) if the current settings are usable. - break; - case LLSnapshotLivePreview::CANNOT_CROP_HORIZONTALLY: - case LLSnapshotLivePreview::CANNOT_CROP_VERTICALLY: - if (fixed_crop) - { - done = fail = true; - } - else - { - // Set target aspect to aspect of the raw snapshot we have (no reason to crop anything). - new_aspect = raw_aspect; - fixed_crop = true; - } - break; - case LLSnapshotLivePreview::SIZE_TOO_LARGE: - if (fixed_size) - { - done = fail = true; - } - else - { - if (new_width > w) - { - new_width = w; - new_height = llround(new_width / new_aspect); - } - if (new_height > h) - { - new_width = llmin(w, llround(h * new_aspect)); - new_height = llmin(h, llround(new_width / new_aspect)); - } - fixed_size = true; - } - break; - case LLSnapshotLivePreview::CANNOT_RESIZE: - if (fixed_scale) - { - done = fail = true; - } - else - { - F32 ratio = llmin((F32)w / new_width, (F32)h / new_height); - new_width = llround(new_width * ratio); - new_height = llround(new_height * ratio); - fixed_scale = true; - } - break; - } - } - previewp->setAspect(old_aspect); - previewp->setSize(old_width, old_height); - if (fail) - { - new_aspect = 0; - new_width = new_height = 0; - } - else - { - LLComboBox* size_combo_box = NULL; - LLComboBox* aspect_combo_box = NULL; - switch(shot_type) - { - case LLSnapshotLivePreview::SNAPSHOT_FEED: - size_combo_box = view->getChild("feed_size_combo"); - aspect_combo_box = view->getChild("feed_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: - size_combo_box = view->getChild("postcard_size_combo"); - aspect_combo_box = view->getChild("postcard_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: - size_combo_box = view->getChild("texture_size_combo"); - aspect_combo_box = view->getChild("texture_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_LOCAL: - size_combo_box = view->getChild("local_size_combo"); - aspect_combo_box = view->getChild("local_aspect_combo"); - break; - } - S32 index = 0; - S32 const size_custom = size_combo_box->getItemCount() - (shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE ? 0 : 1); // Texture does not end on 'Custom'. - while (index < size_custom) - { - size_combo_box->setCurrentByIndex(index); - std::string sdstring = size_combo_box->getSelectedValue(); - LLSD sdres; - std::stringstream sstream(sdstring); - LLSDSerialize::fromNotation(sdres, sstream, sdstring.size()); - S32 width = sdres[0]; - S32 height = sdres[1]; - if (width == 0 || height == 0) - { - width = gViewerWindow->getWindowDisplayWidth(); - height = gViewerWindow->getWindowDisplayHeight(); - } - if (width == new_width && height == new_height) - { - break; - } - ++index; - } - if (index == size_custom && shot_type != LLSnapshotLivePreview::SNAPSHOT_TEXTURE) - { - size_combo_box->setCurrentByIndex(index); - } - index = 0; - S32 const aspect_custom = aspect_combo_box->getItemCount() - 1; - while (index < aspect_custom) - { - aspect_combo_box->setCurrentByIndex(index); - std::string sdstring = aspect_combo_box->getSelectedValue(); - std::stringstream sstream; - sstream << sdstring; - F32 aspect; - sstream >> aspect; - if (aspect == -2) // Default - { - aspect = (F32)new_width / new_height; - } - if (aspect == 0) // Current window - { - aspect = (F32)gViewerWindow->getWindowDisplayWidth() / gViewerWindow->getWindowDisplayHeight(); - } - if (llabs(aspect - new_aspect) < 0.0001) - { - break; - } - ++index; - } - if (index == aspect_custom) - { - aspect_combo_box->setCurrentByIndex(index); - setAspect(view, previewp->aspectComboName(), update_controls); - } - } - } - else -#endif - { - view->getChild("feed_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastResolution")); - view->getChild("feed_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastAspect")); - view->getChild("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution")); - view->getChild("postcard_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastAspect")); - view->getChild("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution")); - view->getChild("texture_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastAspect")); - view->getChild("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution")); - view->getChild("local_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastAspect")); - } + view->getChild("feed_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastResolution")); + view->getChild("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution")); + view->getChild("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution")); + view->getChild("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution")); std::string sdstring = combobox->getSelectedValue(); LLSD sdres; @@ -2526,6 +2420,12 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool S32 width = sdres[0]; S32 height = sdres[1]; + + if (width != -1 && height != -1) + { + // Not "Custom". + keepAspect(view, false); + } if (previewp && combobox->getCurrentIndex() >= 0) { @@ -2538,15 +2438,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool } else if (width == -1 || height == -1) { - if (previewp->isUsed() && new_width != 0 && new_height != 0) - { - previewp->setSize(new_width, new_height); - } - else - { - // load last custom value - previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName())); - } + // load last custom value + previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName())); } else if (height == -2) { @@ -2597,17 +2490,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool if (previewp) { - if (new_aspect == 0) - { - // In case the aspect is 'Default', need to update aspect (which will call updateControls, if necessary). - setAspect(view, previewp->aspectComboName(), update_controls); - } - else - { - LLSpinCtrl* aspect_spinner = view->getChild("aspect_ratio"); - aspect_spinner->set(new_aspect); - previewp->setAspect(new_aspect); - } + // In case the aspect is 'Default', need to update aspect (which will call updateControls, if necessary). + setAspect(view, previewp->aspectComboName(), update_controls); } } @@ -2640,6 +2524,8 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda S32 width, height; previewp->getSize(width, height); aspect = (F32)width / height; + // Turn off "Keep aspect" when aspect is set to Default. + keepAspect(view, false); } else if (aspect == -1) // Custom { @@ -2651,6 +2537,7 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda } LLSpinCtrl* aspect_spinner = view->getChild("aspect_ratio"); + LLCheckBoxCtrl* keep_aspect = view->getChild("keep_aspect"); // Set whether or not the spinners can be changed. if (gSavedSettings.getBOOL("RenderUIInSnapshot") || @@ -2660,11 +2547,13 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda // Disable without making label gray. aspect_spinner->setAllowEdit(FALSE); aspect_spinner->setIncrement(0); + keep_aspect->setEnabled(FALSE); } else { aspect_spinner->setAllowEdit(TRUE); aspect_spinner->setIncrement(llmax(0.01f, lltrunc(aspect) / 100.0f)); + keep_aspect->setEnabled(TRUE); } // Sync the spinner and cache value. @@ -2685,6 +2574,77 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda } } +// static +void LLFloaterSnapshot::Impl::enforceAspect(LLFloaterSnapshot* floater, F32 new_aspect) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + LLComboBox* combo = floater->getChild(previewp->aspectComboName()); + S32 const aspect_custom = combo->getItemCount() - 1; + for (S32 index = 0; index <= aspect_custom; ++index) + { + combo->setCurrentByIndex(index); + if (index == aspect_custom) + { + gSavedSettings.setF32(lastSnapshotAspectName(), new_aspect); + break; + } + std::string sdstring = combo->getSelectedValue(); + std::stringstream sstream; + sstream << sdstring; + F32 aspect; + sstream >> aspect; + if (aspect == -2) // Default + { + continue; + } + if (aspect == 0) // Current window + { + aspect = (F32)gViewerWindow->getWindowDisplayWidth() / gViewerWindow->getWindowDisplayHeight(); + } + if (llabs(aspect - new_aspect) < 0.0001) + { + break; + } + } + storeAspectSetting(combo, previewp->aspectComboName()); + updateAspect(combo, floater, true); + } +} + +void LLFloaterSnapshot::Impl::enforceResolution(LLFloaterSnapshot* floater, F32 new_aspect) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + // Get current size. + S32 w, h; + previewp->getSize(w, h); + // Do all calculation in floating point. + F32 cw = w; + F32 ch = h; + // Get the current raw size. + previewp->getRawSize(w, h); + F32 rw = w; + F32 rh = h; + // Fit rectangle with aspect new_aspect, around current rectangle, but cropped to raw size. + F32 nw = llmin(llmax(cw, ch * new_aspect), rw); + F32 nh = llmin(llmax(ch, cw / new_aspect), rh); + // Fit rectangle with aspect new_aspect inside that rectangle (in case it was cropped). + nw = llmin(nw, nh * new_aspect); + nh = llmin(nh, nw / new_aspect); + // Round off to nearest integer. + S32 new_width = llround(nw); + S32 new_height = llround(nh); + + gSavedSettings.setS32(lastSnapshotWidthName(), new_width); + gSavedSettings.setS32(lastSnapshotHeightName(), new_height); + comboSetCustom(floater, previewp->resolutionComboName()); + updateResolution(floater->getChild(previewp->resolutionComboName()), floater, false); + } +} + // static void LLFloaterSnapshot::Impl::onCommitLayerTypes(LLUICtrl* ctrl, void*data) { @@ -2703,13 +2663,20 @@ void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data) LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; if (view) { - gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view)); + LLSnapshotLivePreview::ESnapshotType snapshot_type = getTypeIndex(view); + gSavedSettings.setS32("LastSnapshotType", snapshot_type); + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + previewp->setSnapshotType(snapshot_type); + } + keepAspect(view, gSavedSettings.getBOOL(snapshotKeepAspectName()), true); updateControls(view); } } -//static +//static void LLFloaterSnapshot::Impl::onCommitSnapshotFormat(LLUICtrl* ctrl, void* data) { LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; @@ -2729,7 +2696,12 @@ void LLFloaterSnapshot::Impl::comboSetCustom(LLFloaterSnapshot* floater, const s LLComboBox* combo = floater->getChild(comboname); combo->setCurrentByIndex(combo->getItemCount() - 1); // "custom" is always the last index + storeAspectSetting(combo, comboname); +} +// static +void LLFloaterSnapshot::Impl::storeAspectSetting(LLComboBox* combo, const std::string& comboname) +{ if(comboname == "feed_size_combo") { gSavedSettings.setS32("SnapshotFeedLastResolution", combo->getCurrentIndex()); @@ -2766,8 +2738,11 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; if (view) { - S32 w = llround(view->childGetValue("snapshot_width").asReal(), 1.0); - S32 h = llround(view->childGetValue("snapshot_height").asReal(), 1.0); + LLSpinCtrl* width_spinner = view->getChild("snapshot_width"); + LLSpinCtrl* height_spinner = view->getChild("snapshot_height"); + + S32 w = llround((F32)width_spinner->getValue().asReal(), 1.0f); + S32 h = llround((F32)height_spinner->getValue().asReal(), 1.0f); LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) @@ -2777,6 +2752,33 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat if (w != curw || h != curh) { + // Enforce multiple of 32 if the step was 32. + Dout(dc::snapshot, "w = " << w << "; curw = " << curw); + if (llabs(w - curw) == 32) + { + w = (w + 16) & -32; + Dout(dc::snapshot, "w = (w + 16) & -32 = " << w); + } + if (llabs(h - curh) == 32) + { + h = (h + 16) & -32; + } + if (gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + F32 aspect = previewp->getAspect(); + if (h == curh) + { + // Width was changed. Change height to keep aspect constant. + h = llround(w / aspect); + } + else + { + // Height was changed. Change width to keep aspect constant. + w = llround(h * aspect); + } + } + width_spinner->forceSetValue(LLSD::Real(w)); + height_spinner->forceSetValue(LLSD::Real(h)); previewp->setMaxImageSize((S32)((LLSpinCtrl *)ctrl)->getMaxValue()) ; previewp->setSize(w,h); checkAutoSnapshot(previewp, FALSE); @@ -2794,6 +2796,30 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat } } +char const* LLSnapshotLivePreview::resolutionComboName() const +{ + char const* result; + switch(mSnapshotType) + { + case SNAPSHOT_FEED: + result = "feed_size_combo"; + break; + case SNAPSHOT_POSTCARD: + result = "postcard_size_combo"; + break; + case SNAPSHOT_TEXTURE: + result = "texture_size_combo"; + break; + case SNAPSHOT_LOCAL: + result = "local_size_combo"; + break; + default: + result= ""; + break; + } + return result; +} + char const* LLSnapshotLivePreview::aspectComboName() const { char const* result; @@ -2841,6 +2867,11 @@ void LLFloaterSnapshot::Impl::onCommitCustomAspect(LLUICtrl *ctrl, void* data) gSavedSettings.setF32(lastSnapshotAspectName(), a); + if (gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(view, a); + } + updateControls(view, true); } } @@ -2902,6 +2933,7 @@ BOOL LLFloaterSnapshot::postBuild() childSetCommitCallback("snapshot_height", Impl::onCommitCustomResolution, this); childSetCommitCallback("aspect_ratio", Impl::onCommitCustomAspect, this); + childSetCommitCallback("keep_aspect", Impl::onClickKeepAspect, this); childSetCommitCallback("ui_check", Impl::onClickUICheck, this); childSetValue("ui_check", gSavedSettings.getBOOL("RenderUIInSnapshot")); @@ -2952,8 +2984,9 @@ BOOL LLFloaterSnapshot::postBuild() Impl::sPreviewHandle = previewp->getHandle(); - impl.updateControls(this); + impl.keepAspect(sInstance, gSavedSettings.getBOOL(snapshotKeepAspectName()), true); impl.freezeTime(gSavedSettings.getBOOL("SnapshotOpenFreezeTime")); + impl.updateControls(this); return TRUE; } diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 82e12f525..e474dec9e 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1513,12 +1513,6 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) LLMenuGL::sMenuContainer->hideMenus(); } - LLView *item = NULL; - if (getChildCount() > 0) - { - item = *(getChildList()->begin()); - } - switch( key ) { case KEY_F2: @@ -2074,6 +2068,12 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr } } +void LLFolderView::setScrollContainer(LLScrollableContainerView* parent) +{ + mScrollContainer = parent; + parent->setPassBackToChildren(false); +} + LLRect LLFolderView::getVisibleRect() { S32 visible_height = mScrollContainer->getRect().getHeight(); diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 0d36bfe3b..c2771ed77 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -217,7 +217,7 @@ public: void scrollToShowSelection(); void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); - void setScrollContainer( LLScrollableContainerView* parent ) { mScrollContainer = parent; } + void setScrollContainer(LLScrollableContainerView* parent); LLRect getVisibleRect(); BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); diff --git a/indra/newview/llpanelmediahud.cpp b/indra/newview/llpanelmediahud.cpp index 27d1473d6..e56e976f5 100644 --- a/indra/newview/llpanelmediahud.cpp +++ b/indra/newview/llpanelmediahud.cpp @@ -424,8 +424,8 @@ void LLPanelMediaHUD::setAlpha(F32 alpha) LLViewQuery query; LLView* query_view = mMediaFocus ? getChildView("media_focused_controls") : getChildView("media_hover_controls"); - child_list_t children = query(query_view); - for (child_list_iter_t child_iter = children.begin(); + viewList_t children = query(query_view); + for (viewList_t::iterator child_iter = children.begin(); child_iter != children.end(); ++child_iter) { LLUICtrl* ctrl = dynamic_cast(*child_iter); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 0a0eb5f08..e57fbc3a2 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -101,7 +101,7 @@ public: // Used for sorting struct sort { - bool operator()(const LLView* i1, const LLView* i2) + bool operator()(const LLView* i1, const LLView* i2) const { LLTextureBar* bar1p = (LLTextureBar*)i1; LLTextureBar* bar2p = (LLTextureBar*)i2; @@ -120,7 +120,7 @@ public: struct sort_fetch { - bool operator()(const LLView* i1, const LLView* i2) + bool operator()(const LLView* i1, const LLView* i2) const { LLTextureBar* bar1p = (LLTextureBar*)i1; LLTextureBar* bar2p = (LLTextureBar*)i2; diff --git a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml index 8142c0f2b..cb9d8cfda 100644 --- a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml @@ -53,16 +53,16 @@