MAINT-5232: merge LLError::Log::demangle() to tip

master
Nat Goodspeed 2015-06-26 15:27:34 -04:00
commit 3f52cefcc4
83 changed files with 1963 additions and 612 deletions

View File

@ -501,3 +501,4 @@ d3d0101e980ec95043e0af9b7903045d3bc447e4 3.7.24-release
000e9dda4162cbf0a83ba88558b19473654a09a9 3.7.26-release
afd8d4756e8eda3c8f760625d1c17a2ad40ad6c8 3.7.27-release
566874eb5ab26c003ef7fb0e22ce40c5fa0013f4 3.7.28-release
d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release

View File

@ -42,6 +42,7 @@
#include "lldiriterator.h"
#include "v4coloru.h"
#include "llsdserialize.h"
#include "llcleanup.h"
// system libraries
#include <iostream>
@ -634,7 +635,7 @@ int main(int argc, char** argv)
}
// Cleanup and exit
LLImage::cleanupClass();
SUBSYSTEM_CLEANUP(LLImage);
if (fast_timer_log_thread)
{
fast_timer_log_thread->shutdown();

View File

@ -92,7 +92,7 @@ void LLWearableData::setWearable(const LLWearableType::EType type, U32 index, LL
}
}
U32 LLWearableData::pushWearable(const LLWearableType::EType type,
void LLWearableData::pushWearable(const LLWearableType::EType type,
LLWearable *wearable,
bool trigger_updated /* = true */)
{
@ -100,9 +100,8 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type,
{
// no null wearables please!
LL_WARNS() << "Null wearable sent for type " << type << LL_ENDL;
return MAX_CLOTHING_PER_TYPE;
}
if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE)
if (canAddWearable(type))
{
mWearableDatas[type].push_back(wearable);
if (trigger_updated)
@ -110,9 +109,7 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type,
const BOOL removed = FALSE;
wearableUpdated(wearable, removed);
}
return mWearableDatas[type].size()-1;
}
return MAX_CLOTHING_PER_TYPE;
}
// virtual
@ -125,7 +122,7 @@ void LLWearableData::wearableUpdated(LLWearable *wearable, BOOL removed)
}
}
void LLWearableData::popWearable(LLWearable *wearable)
void LLWearableData::eraseWearable(LLWearable *wearable)
{
if (wearable == NULL)
{
@ -133,16 +130,16 @@ void LLWearableData::popWearable(LLWearable *wearable)
return;
}
U32 index = getWearableIndex(wearable);
const LLWearableType::EType type = wearable->getType();
if (index < MAX_CLOTHING_PER_TYPE && index < getWearableCount(type))
U32 index;
if (getWearableIndex(wearable,index))
{
popWearable(type, index);
eraseWearable(type, index);
}
}
void LLWearableData::popWearable(const LLWearableType::EType type, U32 index)
void LLWearableData::eraseWearable(const LLWearableType::EType type, U32 index)
{
LLWearable *wearable = getWearable(type, index);
if (wearable)
@ -204,11 +201,11 @@ void LLWearableData::pullCrossWearableValues(const LLWearableType::EType type)
}
U32 LLWearableData::getWearableIndex(const LLWearable *wearable) const
BOOL LLWearableData::getWearableIndex(const LLWearable *wearable, U32& index_found) const
{
if (wearable == NULL)
{
return MAX_CLOTHING_PER_TYPE;
return FALSE;
}
const LLWearableType::EType type = wearable->getType();
@ -216,18 +213,50 @@ U32 LLWearableData::getWearableIndex(const LLWearable *wearable) const
if (wearable_iter == mWearableDatas.end())
{
LL_WARNS() << "tried to get wearable index with an invalid type!" << LL_ENDL;
return MAX_CLOTHING_PER_TYPE;
return FALSE;
}
const wearableentry_vec_t& wearable_vec = wearable_iter->second;
for(U32 index = 0; index < wearable_vec.size(); index++)
{
if (wearable_vec[index] == wearable)
{
return index;
index_found = index;
return TRUE;
}
}
return MAX_CLOTHING_PER_TYPE;
return FALSE;
}
U32 LLWearableData::getClothingLayerCount() const
{
U32 count = 0;
for (S32 i = 0; i < LLWearableType::WT_COUNT; i++)
{
LLWearableType::EType type = (LLWearableType::EType)i;
if (LLWearableType::getAssetType(type)==LLAssetType::AT_CLOTHING)
{
count += getWearableCount(type);
}
}
return count;
}
BOOL LLWearableData::canAddWearable(const LLWearableType::EType type) const
{
LLAssetType::EType a_type = LLWearableType::getAssetType(type);
if (a_type==LLAssetType::AT_CLOTHING)
{
return (getClothingLayerCount() < MAX_CLOTHING_LAYERS);
}
else if (a_type==LLAssetType::AT_BODYPART)
{
return (getWearableCount(type) < 1);
}
else
{
return FALSE;
}
}
BOOL LLWearableData::isOnTop(LLWearable* wearable) const

14
indra/llappearance/llwearabledata.h Normal file → Executable file
View File

@ -60,11 +60,13 @@ public:
const LLWearable* getBottomWearable(const LLWearableType::EType type) const;
U32 getWearableCount(const LLWearableType::EType type) const;
U32 getWearableCount(const U32 tex_index) const;
U32 getWearableIndex(const LLWearable *wearable) const;
BOOL getWearableIndex(const LLWearable *wearable, U32& index) const;
U32 getClothingLayerCount() const;
BOOL canAddWearable(const LLWearableType::EType type) const;
BOOL isOnTop(LLWearable* wearable) const;
static const U32 MAX_CLOTHING_PER_TYPE = 5;
static const U32 MAX_CLOTHING_LAYERS = 60;
//--------------------------------------------------------------------
// Setters
@ -72,11 +74,11 @@ public:
protected:
// Low-level data structure setter - public access is via setWearableItem, etc.
void setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable);
U32 pushWearable(const LLWearableType::EType type, LLWearable *wearable,
void pushWearable(const LLWearableType::EType type, LLWearable *wearable,
bool trigger_updated = true);
virtual void wearableUpdated(LLWearable *wearable, BOOL removed);
void popWearable(LLWearable *wearable);
void popWearable(const LLWearableType::EType type, U32 index);
void eraseWearable(LLWearable *wearable);
void eraseWearable(const LLWearableType::EType type, U32 index);
void clearWearableType(const LLWearableType::EType type);
bool swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b);

9
indra/llappearance/llwearabletype.cpp Normal file → Executable file
View File

@ -27,6 +27,7 @@
#include "linden_common.h"
#include "llwearabletype.h"
#include "llinventorytype.h"
#include "llinventorydefines.h"
static LLTranslationBridge* sTrans = NULL;
@ -160,7 +161,7 @@ BOOL LLWearableType::getDisableCameraSwitch(LLWearableType::EType type)
return entry->mDisableCameraSwitch;
}
// static
// static
BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)
{
const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
@ -169,3 +170,9 @@ BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)
return entry->mAllowMultiwear;
}
// static
LLWearableType::EType LLWearableType::inventoryFlagsToWearableType(U32 flags)
{
return (LLWearableType::EType)(flags & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK);
}

1
indra/llappearance/llwearabletype.h Normal file → Executable file
View File

@ -80,6 +80,7 @@ public:
static LLInventoryType::EIconName getIconName(EType type);
static BOOL getDisableCameraSwitch(EType type);
static BOOL getAllowMultiwear(EType type);
static EType inventoryFlagsToWearableType(U32 flags);
protected:
LLWearableType() {}

View File

@ -162,6 +162,7 @@ set(llcommon_HEADER_FILES
llhash.h
llheartbeat.h
llindexedvector.h
llinitdestroyclass.h
llinitparam.h
llinstancetracker.h
llkeythrottle.h
@ -178,6 +179,7 @@ set(llcommon_HEADER_FILES
llmortician.h
llnametable.h
llpointer.h
llpounceable.h
llpredicate.h
llpreprocessor.h
llpriqueuemap.h
@ -310,6 +312,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)

View File

@ -48,6 +48,7 @@
#include "lleventtimer.h"
#include "google_breakpad/exception_handler.h"
#include "stringize.h"
#include "llcleanup.h"
//
// Signal handling
@ -177,7 +178,7 @@ LLApp::~LLApp()
if(mExceptionHandler != 0) delete mExceptionHandler;
LLCommon::cleanupClass();
SUBSYSTEM_CLEANUP(LLCommon);
}
// static

View File

@ -0,0 +1,30 @@
/**
* @file llcleanup.h
* @author Nat Goodspeed
* @date 2015-05-20
* @brief Mechanism for cleaning up subsystem resources
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLCLEANUP_H)
#define LL_LLCLEANUP_H
#include "llerror.h"
// Instead of directly calling SomeClass::cleanupClass(), use
// SUBSYSTEM_CLEANUP(SomeClass);
// This logs the call as well as performing it. That gives us a baseline
// subsystem shutdown order against which to compare subsequent dynamic
// shutdown schemes.
#define SUBSYSTEM_CLEANUP(CLASSNAME) \
do { \
LL_INFOS("Cleanup") << "Calling " #CLASSNAME "::cleanupClass()" << LL_ENDL; \
CLASSNAME::cleanupClass(); \
} while (0)
// Use ancient do { ... } while (0) macro trick to permit a block of
// statements with the same syntax as a single statement.
#endif /* ! defined(LL_LLCLEANUP_H) */

View File

@ -31,6 +31,7 @@
#include "llthread.h"
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "llcleanup.h"
//static
BOOL LLCommon::sAprInitialized = FALSE;
@ -63,11 +64,11 @@ void LLCommon::cleanupClass()
sMasterThreadRecorder = NULL;
LLTrace::set_master_thread_recorder(NULL);
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
LLTimer::cleanupClass();
SUBSYSTEM_CLEANUP(LLTimer);
if (sAprInitialized)
{
ll_cleanup_apr();
sAprInitialized = FALSE;
}
LLMemory::cleanupClass();
SUBSYSTEM_CLEANUP(LLMemory);
}

View File

@ -413,6 +413,11 @@ namespace
namespace LLError
{
bool is_available()
{
return Globals::instanceExists();
}
class SettingsConfig : public LLRefCount
{
friend class Settings;

View File

@ -189,6 +189,11 @@ namespace LLError
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
LL_COMMON_API int shouldLogCallCount();
// Check whether Globals exists. This should only be used by LLSingleton
// infrastructure to avoid trying to log when our internal LLSingleton is
// unavailable -- circularity ensues.
LL_COMMON_API bool is_available();
};
#endif // LL_LLERRORCONTROL_H

View File

@ -0,0 +1,190 @@
/**
* @file llinitdestroyclass.h
* @author Nat Goodspeed
* @date 2015-05-27
* @brief LLInitClass / LLDestroyClass mechanism
*
* The LLInitClass template, extracted from llui.h, ensures that control will
* reach a static initClass() method. LLDestroyClass does the same for a
* static destroyClass() method.
*
* The distinguishing characteristics of these templates are:
*
* - All LLInitClass<T>::initClass() methods are triggered by an explicit call
* to LLInitClassList::instance().fireCallbacks(). Presumably this call
* happens sometime after all static objects in the program have been
* initialized. In other words, each LLInitClass<T>::initClass() method
* should be able to make some assumptions about global program state.
*
* - Similarly, LLDestroyClass<T>::destroyClass() methods are triggered by
* LLDestroyClassList::instance().fireCallbacks(). Again, presumably this
* happens at a well-defined moment in the program's shutdown sequence.
*
* - The initClass() calls happen in an unspecified sequence. You may not rely
* on the relative ordering of LLInitClass<T>::initClass() versus another
* LLInitClass<U>::initClass() method. If you need such a guarantee, use
* LLSingleton instead and make the dependency explicit.
*
* - Similarly, LLDestroyClass<T>::destroyClass() may happen either before or
* after LLDestroyClass<U>::destroyClass(). You cannot rely on that order.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLINITDESTROYCLASS_H)
#define LL_LLINITDESTROYCLASS_H
#include "llerror.h"
#include "llsingleton.h"
#include <boost/function.hpp>
#include <boost/signals2/signal.hpp>
#include <typeinfo>
/**
* LLCallbackRegistry is an implementation detail base class for
* LLInitClassList and LLDestroyClassList. It's a very thin wrapper around a
* Boost.Signals2 signal object.
*/
class LLCallbackRegistry
{
public:
typedef boost::signals2::signal<void()> callback_signal_t;
void registerCallback(const callback_signal_t::slot_type& slot)
{
mCallbacks.connect(slot);
}
void fireCallbacks()
{
mCallbacks();
}
private:
callback_signal_t mCallbacks;
};
/**
* LLInitClassList is the LLCallbackRegistry for LLInitClass. It stores the
* registered initClass() methods. It must be an LLSingleton because
* LLInitClass registers its initClass() method at static construction time
* (before main()), requiring LLInitClassList to be fully constructed on
* demand regardless of module initialization order.
*/
class LLInitClassList :
public LLCallbackRegistry,
public LLSingleton<LLInitClassList>
{
friend class LLSingleton<LLInitClassList>;
private:
LLInitClassList() {}
};
/**
* LLDestroyClassList is the LLCallbackRegistry for LLDestroyClass. It stores
* the registered destroyClass() methods. It must be an LLSingleton because
* LLDestroyClass registers its destroyClass() method at static construction
* time (before main()), requiring LLDestroyClassList to be fully constructed
* on demand regardless of module initialization order.
*/
class LLDestroyClassList :
public LLCallbackRegistry,
public LLSingleton<LLDestroyClassList>
{
friend class LLSingleton<LLDestroyClassList>;
private:
LLDestroyClassList() {}
};
/**
* LLRegisterWith is an implementation detail for LLInitClass and
* LLDestroyClass. It is intended to be used as a static class member whose
* constructor registers the specified callback with the LLMumbleClassList
* singleton registry specified as the template argument.
*/
template<typename T>
class LLRegisterWith
{
public:
LLRegisterWith(boost::function<void ()> func)
{
T::instance().registerCallback(func);
}
// this avoids a MSVC bug where non-referenced static members are "optimized" away
// even if their constructors have side effects
S32 reference()
{
S32 dummy;
dummy = 0;
return dummy;
}
};
/**
* Derive MyClass from LLInitClass<MyClass> (the Curiously Recurring Template
* Pattern) to ensure that the static method MyClass::initClass() will be
* called (along with all other LLInitClass<T> subclass initClass() methods)
* when someone calls LLInitClassList::instance().fireCallbacks(). This gives
* the application specific control over the timing of all such
* initializations, without having to insert calls for every such class into
* generic application code.
*/
template<typename T>
class LLInitClass
{
public:
LLInitClass() { sRegister.reference(); }
// When this static member is initialized, the subclass initClass() method
// is registered on LLInitClassList. See sRegister definition below.
static LLRegisterWith<LLInitClassList> sRegister;
private:
// Provide a default initClass() method in case subclass misspells (or
// omits) initClass(). This turns a potential build error into a fatal
// runtime error.
static void initClass()
{
LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
/**
* Derive MyClass from LLDestroyClass<MyClass> (the Curiously Recurring
* Template Pattern) to ensure that the static method MyClass::destroyClass()
* will be called (along with other LLDestroyClass<T> subclass destroyClass()
* methods) when someone calls LLDestroyClassList::instance().fireCallbacks().
* This gives the application specific control over the timing of all such
* cleanup calls, without having to insert calls for every such class into
* generic application code.
*/
template<typename T>
class LLDestroyClass
{
public:
LLDestroyClass() { sRegister.reference(); }
// When this static member is initialized, the subclass destroyClass()
// method is registered on LLInitClassList. See sRegister definition
// below.
static LLRegisterWith<LLDestroyClassList> sRegister;
private:
// Provide a default destroyClass() method in case subclass misspells (or
// omits) destroyClass(). This turns a potential build error into a fatal
// runtime error.
static void destroyClass()
{
LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
// Here's where LLInitClass<T> specifies the subclass initClass() method.
template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass);
// Here's where LLDestroyClass<T> specifies the subclass destroyClass() method.
template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass);
#endif /* ! defined(LL_LLINITDESTROYCLASS_H) */

View File

@ -0,0 +1,215 @@
/**
* @file llpounceable.h
* @author Nat Goodspeed
* @date 2015-05-22
* @brief LLPounceable is tangentially related to a future: it's a holder for
* a value that may or may not exist yet. Unlike a future, though,
* LLPounceable freely allows reading the held value. (If the held
* type T does not have a distinguished "empty" value, consider using
* LLPounceable<boost::optional<T>>.)
*
* LLPounceable::callWhenReady() is this template's claim to fame. It
* allows its caller to "pounce" on the held value as soon as it
* becomes non-empty. Call callWhenReady() with any C++ callable
* accepting T. If the held value is already non-empty, callWhenReady()
* will immediately call the callable with the held value. If the held
* value is empty, though, callWhenReady() will enqueue the callable
* for later. As soon as LLPounceable is assigned a non-empty held
* value, it will flush the queue of deferred callables.
*
* Consider a global LLMessageSystem* gMessageSystem. Message system
* initialization happens at a very specific point during viewer
* initialization. Other subsystems want to register callbacks on the
* LLMessageSystem instance as soon as it's initialized, but their own
* initialization may precede that. If we define gMessageSystem to be
* an LLPounceable<LLMessageSystem*>, a subsystem can use
* callWhenReady() to either register immediately (if gMessageSystem
* is already up and runnning) or register as soon as gMessageSystem
* is set with a new, initialized instance.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLPOUNCEABLE_H)
#define LL_LLPOUNCEABLE_H
#include "llsingleton.h"
#include <boost/noncopyable.hpp>
#include <boost/call_traits.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/unordered_map.hpp>
#include <boost/signals2/signal.hpp>
// Forward declare the user template, since we want to be able to point to it
// in some of its implementation classes.
template <typename T, class TAG>
class LLPounceable;
template <typename T, typename TAG>
struct LLPounceableTraits
{
// Our "queue" is a signal object with correct signature.
typedef boost::signals2::signal<void (typename boost::call_traits<T>::param_type)> signal_t;
// Call callWhenReady() with any callable accepting T.
typedef typename signal_t::slot_type func_t;
// owner pointer type
typedef LLPounceable<T, TAG>* owner_ptr;
};
// Tag types distinguish the two different implementations of LLPounceable's
// queue.
struct LLPounceableQueue {};
struct LLPounceableStatic {};
// generic LLPounceableQueueImpl deliberately omitted: only the above tags are
// legal
template <typename T, class TAG>
class LLPounceableQueueImpl;
// The implementation selected by LLPounceableStatic uses an LLSingleton
// because we can't count on a data member queue being initialized at the time
// we start getting callWhenReady() calls. This is that LLSingleton.
template <typename T>
class LLPounceableQueueSingleton:
public LLSingleton<LLPounceableQueueSingleton<T> >
{
private:
typedef LLPounceableTraits<T, LLPounceableStatic> traits;
typedef typename traits::owner_ptr owner_ptr;
typedef typename traits::signal_t signal_t;
// For a given held type T, every LLPounceable<T, LLPounceableStatic>
// instance will call on the SAME LLPounceableQueueSingleton instance --
// given how class statics work. We must keep a separate queue for each
// LLPounceable instance. Use a hash map for that.
typedef boost::unordered_map<owner_ptr, signal_t> map_t;
public:
// Disambiguate queues belonging to different LLPounceables.
signal_t& get(owner_ptr owner)
{
// operator[] has find-or-create semantics -- just what we want!
return mMap[owner];
}
private:
map_t mMap;
};
// LLPounceableQueueImpl that uses the above LLSingleton
template <typename T>
class LLPounceableQueueImpl<T, LLPounceableStatic>
{
public:
typedef LLPounceableTraits<T, LLPounceableStatic> traits;
typedef typename traits::owner_ptr owner_ptr;
typedef typename traits::signal_t signal_t;
signal_t& get(owner_ptr owner) const
{
// this Impl contains nothing; it delegates to the Singleton
return LLPounceableQueueSingleton<T>::instance().get(owner);
}
};
// The implementation selected by LLPounceableQueue directly contains the
// queue of interest, suitable for an LLPounceable we can trust to be fully
// initialized when it starts getting callWhenReady() calls.
template <typename T>
class LLPounceableQueueImpl<T, LLPounceableQueue>
{
public:
typedef LLPounceableTraits<T, LLPounceableQueue> traits;
typedef typename traits::owner_ptr owner_ptr;
typedef typename traits::signal_t signal_t;
signal_t& get(owner_ptr)
{
return mQueue;
}
private:
signal_t mQueue;
};
// LLPounceable<T> is for an LLPounceable instance on the heap or the stack.
// LLPounceable<T, LLPounceableStatic> is for a static LLPounceable instance.
template <typename T, class TAG=LLPounceableQueue>
class LLPounceable: public boost::noncopyable
{
private:
typedef LLPounceableTraits<T, TAG> traits;
typedef typename traits::owner_ptr owner_ptr;
typedef typename traits::signal_t signal_t;
public:
typedef typename traits::func_t func_t;
// By default, both the initial value and the distinguished empty value
// are a default-constructed T instance. However you can explicitly
// specify each.
LLPounceable(typename boost::call_traits<T>::value_type init =boost::value_initialized<T>(),
typename boost::call_traits<T>::param_type empty=boost::value_initialized<T>()):
mHeld(init),
mEmpty(empty)
{}
// make read access to mHeld as cheap and transparent as possible
operator T () const { return mHeld; }
typename boost::remove_pointer<T>::type operator*() const { return *mHeld; }
typename boost::call_traits<T>::value_type operator->() const { return mHeld; }
// uncomment 'explicit' as soon as we allow C++11 compilation
/*explicit*/ operator bool() const { return bool(mHeld); }
bool operator!() const { return ! mHeld; }
// support both assignment (dumb ptr idiom) and reset() (smart ptr)
void operator=(typename boost::call_traits<T>::param_type value)
{
reset(value);
}
void reset(typename boost::call_traits<T>::param_type value)
{
mHeld = value;
// If this new value is non-empty, flush anything pending in the queue.
if (mHeld != mEmpty)
{
signal_t& signal(get_signal());
signal(mHeld);
signal.disconnect_all_slots();
}
}
// our claim to fame
void callWhenReady(const func_t& func)
{
if (mHeld != mEmpty)
{
// If the held value is already non-empty, immediately call func()
func(mHeld);
}
else
{
// Held value still empty, queue func() for later. By default,
// connect() enqueues slots in FIFO order.
get_signal().connect(func);
}
}
private:
signal_t& get_signal() { return mQueue.get(this); }
// Store both the current and the empty value.
// MAYBE: Might be useful to delegate to LLPounceableTraits the meaning of
// testing for "empty." For some types we want operator!(); for others we
// want to compare to a distinguished value.
typename boost::call_traits<T>::value_type mHeld, mEmpty;
// This might either contain the queue (LLPounceableQueue) or delegate to
// an LLSingleton (LLPounceableStatic).
LLPounceableQueueImpl<T, TAG> mQueue;
};
#endif /* ! defined(LL_LLPOUNCEABLE_H) */

View File

@ -269,7 +269,7 @@ public:
~ScopedRegistrar()
{
if (!singleton_t::destroyed())
if (singleton_t::instanceExists())
{
popScope();
}

View File

@ -25,7 +25,326 @@
*/
#include "linden_common.h"
#include "llsingleton.h"
#include "llerror.h"
#include "llerrorcontrol.h" // LLError::is_available()
#include "lldependencies.h"
#include <boost/foreach.hpp>
#include <algorithm>
#include <iostream> // std::cerr in dire emergency
#include <sstream>
#include <stdexcept>
// Our master list of all LLSingletons is itself an LLSingleton. We used to
// store it in a function-local static, but that could get destroyed before
// the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to
// remove itself from the master list. Since the whole point of this master
// list is to help track inter-LLSingleton dependencies, and since we have
// this implicit dependency from every LLSingleton to the master list, make it
// an LLSingleton.
class LLSingletonBase::MasterList:
public LLSingleton<LLSingletonBase::MasterList>
{
private:
friend class LLSingleton<LLSingletonBase::MasterList>;
public:
// No need to make this private with accessors; nobody outside this source
// file can see it.
LLSingletonBase::list_t mList;
};
//static
LLSingletonBase::list_t& LLSingletonBase::get_master()
{
return LLSingletonBase::MasterList::instance().mList;
}
void LLSingletonBase::add_master()
{
// As each new LLSingleton is constructed, add to the master list.
get_master().push_back(this);
}
void LLSingletonBase::remove_master()
{
// When an LLSingleton is destroyed, remove from master list.
// add_master() used to capture the iterator to the newly-added list item
// so we could directly erase() it from the master list. Unfortunately
// that runs afoul of destruction-dependency order problems. So search the
// master list, and remove this item IF FOUND. We have few enough
// LLSingletons, and they are so rarely destroyed (once per run), that the
// cost of a linear search should not be an issue.
get_master().remove(this);
}
// Wrapping our initializing list in a static method ensures that it will be
// constructed on demand. This list doesn't also need to be in an LLSingleton
// because (a) it should be empty by program shutdown and (b) none of our
// destructors reference it.
//static
LLSingletonBase::list_t& LLSingletonBase::get_initializing()
{
static list_t sList;
return sList;
}
LLSingletonBase::LLSingletonBase():
mCleaned(false),
mDeleteSingleton(NULL)
{
// Make this the currently-initializing LLSingleton.
push_initializing();
}
LLSingletonBase::~LLSingletonBase() {}
void LLSingletonBase::push_initializing()
{
get_initializing().push_back(this);
}
void LLSingletonBase::pop_initializing()
{
list_t& list(get_initializing());
if (list.empty())
{
logerrs("Underflow in stack of currently-initializing LLSingletons at ",
typeid(*this).name(), "::getInstance()");
}
if (list.back() != this)
{
logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
typeid(*this).name(), "::getInstance() trying to pop ",
typeid(*list.back()).name());
}
// Here we're sure that list.back() == this. Whew, pop it.
list.pop_back();
}
void LLSingletonBase::capture_dependency(EInitState initState)
{
// Did this getInstance() call come from another LLSingleton, or from
// vanilla application code? Note that although this is a nontrivial
// method, the vast majority of its calls arrive here with initializing
// empty().
list_t& initializing(get_initializing());
if (! initializing.empty())
{
// getInstance() is being called by some other LLSingleton. But -- is
// this a circularity? That is, does 'this' already appear in the
// initializing stack?
// For what it's worth, normally 'initializing' should contain very
// few elements.
list_t::const_iterator found =
std::find(initializing.begin(), initializing.end(), this);
if (found != initializing.end())
{
// Report the circularity. Requiring the coder to dig through the
// logic to diagnose exactly how we got here is less than helpful.
std::ostringstream out;
for ( ; found != initializing.end(); ++found)
{
// 'found' is an iterator; *found is an LLSingletonBase*; **found
// is the actual LLSingletonBase instance.
out << typeid(**found).name() << " -> ";
}
// We promise to capture dependencies from both the constructor
// and the initSingleton() method, so an LLSingleton's instance
// pointer is on the initializing list during both. Now that we've
// detected circularity, though, we must distinguish the two. If
// the recursive call is from the constructor, we CAN'T honor it:
// otherwise we'd be returning a pointer to a partially-
// constructed object! But from initSingleton() is okay: that
// method exists specifically to support circularity.
// Decide which log helper to call based on initState. They have
// identical signatures.
((initState == CONSTRUCTING)? logerrs : logwarns)
("LLSingleton circularity: ", out.str().c_str(), typeid(*this).name(), "");
}
else
{
// Here 'this' is NOT already in the 'initializing' stack. Great!
// Record the dependency.
// initializing.back() is the LLSingletonBase* currently being
// initialized. Store 'this' in its mDepends set.
initializing.back()->mDepends.insert(this);
}
}
}
//static
LLSingletonBase::vec_t LLSingletonBase::dep_sort()
{
// While it would theoretically be possible to maintain a static
// SingletonDeps through the life of the program, dynamically adding and
// removing LLSingletons as they are created and destroyed, in practice
// it's less messy to construct it on demand. The overhead of doing so
// should happen basically twice: once for cleanupAll(), once for
// deleteAll().
typedef LLDependencies<LLSingletonBase*> SingletonDeps;
SingletonDeps sdeps;
list_t& master(get_master());
BOOST_FOREACH(LLSingletonBase* sp, master)
{
// Build the SingletonDeps structure by adding, for each
// LLSingletonBase* sp in the master list, sp itself. It has no
// associated value type in our SingletonDeps, hence the 0. We don't
// record the LLSingletons it must follow; rather, we record the ones
// it must precede. Copy its mDepends to a KeyList to express that.
sdeps.add(sp, 0,
SingletonDeps::KeyList(),
SingletonDeps::KeyList(sp->mDepends.begin(), sp->mDepends.end()));
}
vec_t ret;
ret.reserve(master.size());
// We should be able to effect this with a transform_iterator that
// extracts just the first (key) element from each sorted_iterator, then
// uses vec_t's range constructor... but frankly this is more
// straightforward, as long as we remember the above reserve() call!
BOOST_FOREACH(SingletonDeps::sorted_iterator::value_type pair, sdeps.sort())
{
ret.push_back(pair.first);
}
// The master list is not itself pushed onto the master list. Add it as
// the very last entry -- it is the LLSingleton on which ALL others
// depend! -- so our caller will process it.
ret.push_back(MasterList::getInstance());
return ret;
}
//static
void LLSingletonBase::cleanupAll()
{
// It's essential to traverse these in dependency order.
BOOST_FOREACH(LLSingletonBase* sp, dep_sort())
{
// Call cleanupSingleton() only if we haven't already done so for this
// instance.
if (! sp->mCleaned)
{
sp->mCleaned = true;
try
{
sp->cleanupSingleton();
}
catch (const std::exception& e)
{
logwarns("Exception in ", typeid(*sp).name(),
"::cleanupSingleton(): ", e.what());
}
catch (...)
{
logwarns("Unknown exception in ", typeid(*sp).name(),
"::cleanupSingleton()");
}
}
}
}
//static
void LLSingletonBase::deleteAll()
{
// It's essential to traverse these in dependency order.
BOOST_FOREACH(LLSingletonBase* sp, dep_sort())
{
// Capture the class name first: in case of exception, don't count on
// being able to extract it later.
const char* name = typeid(*sp).name();
try
{
// Call static method through instance function pointer.
if (! sp->mDeleteSingleton)
{
// This Should Not Happen... but carry on.
logwarns(name, "::mDeleteSingleton not initialized!");
}
else
{
// properly initialized: call it.
// From this point on, DO NOT DEREFERENCE sp!
sp->mDeleteSingleton();
}
}
catch (const std::exception& e)
{
logwarns("Exception in ", name, "::deleteSingleton(): ", e.what());
}
catch (...)
{
logwarns("Unknown exception in ", name, "::deleteSingleton()");
}
}
}
/*------------------------ Final cleanup management ------------------------*/
class LLSingletonBase::MasterRefcount
{
public:
// store a POD int so it will be statically initialized to 0
int refcount;
};
static LLSingletonBase::MasterRefcount sMasterRefcount;
LLSingletonBase::ref_ptr_t LLSingletonBase::get_master_refcount()
{
// Calling this method constructs a new ref_ptr_t, which implicitly calls
// intrusive_ptr_add_ref(MasterRefcount*).
return &sMasterRefcount;
}
void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount* mrc)
{
// Count outstanding SingletonLifetimeManager instances.
++mrc->refcount;
}
void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc)
{
// Notice when each SingletonLifetimeManager instance is destroyed.
if (! --mrc->refcount)
{
// The last instance was destroyed. Time to kill any remaining
// LLSingletons -- but in dependency order.
LLSingletonBase::deleteAll();
}
}
/*---------------------------- Logging helpers -----------------------------*/
//static
void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
{
// Check LLError::is_available() because some of LLError's infrastructure
// is itself an LLSingleton. If that LLSingleton has not yet been
// initialized, trying to log will engage LLSingleton machinery... and
// around and around we go.
if (LLError::is_available())
{
LL_ERRS() << p1 << p2 << p3 << p4 << LL_ENDL;
}
else
{
// Caller may be a test program, or something else whose stderr is
// visible to the user.
std::cerr << p1 << p2 << p3 << p4 << std::endl;
// The other important side effect of LL_ERRS() is
// https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
LLError::crashAndLoop(std::string());
}
}
//static
void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
{
// See logerrs() remarks about is_available().
if (LLError::is_available())
{
LL_WARNS() << p1 << p2 << p3 << p4 << LL_ENDL;
}
else
{
std::cerr << p1 << p2 << p3 << p4 << std::endl;
}
}

View File

@ -25,185 +25,385 @@
#ifndef LLSINGLETON_H
#define LLSINGLETON_H
#include "llerror.h" // *TODO: eliminate this
#include <typeinfo>
#include <boost/noncopyable.hpp>
#include <boost/unordered_set.hpp>
#include <boost/intrusive_ptr.hpp>
#include <list>
#include <vector>
#include <typeinfo>
// LLSingleton implements the getInstance() method part of the Singleton
// pattern. It can't make the derived class constructors protected, though, so
// you have to do that yourself.
//
// There are two ways to use LLSingleton. The first way is to inherit from it
// while using the typename that you'd like to be static as the template
// parameter, like so:
//
// class Foo: public LLSingleton<Foo>{};
//
// Foo& instance = Foo::instance();
//
// The second way is to use the singleton class directly, without inheritance:
//
// typedef LLSingleton<Foo> FooSingleton;
//
// Foo& instance = FooSingleton::instance();
//
// In this case, the class being managed as a singleton needs to provide an
// initSingleton() method since the LLSingleton virtual method won't be
// available
//
// As currently written, it is not thread-safe.
template <typename DERIVED_TYPE>
class LLSingleton : private boost::noncopyable
// TODO:
// Tests for all this!
class LLSingletonBase: private boost::noncopyable
{
public:
class MasterList;
class MasterRefcount;
typedef boost::intrusive_ptr<MasterRefcount> ref_ptr_t;
private:
// All existing LLSingleton instances are tracked in this master list.
typedef std::list<LLSingletonBase*> list_t;
static list_t& get_master();
// This, on the other hand, is a stack whose top indicates the LLSingleton
// currently being initialized.
static list_t& get_initializing();
// Produce a vector<LLSingletonBase*> of master list, in dependency order.
typedef std::vector<LLSingletonBase*> vec_t;
static vec_t dep_sort();
bool mCleaned; // cleanupSingleton() has been called
// we directly depend on these other LLSingletons
typedef boost::unordered_set<LLSingletonBase*> set_t;
set_t mDepends;
protected:
typedef enum e_init_state
{
UNINITIALIZED = 0, // must be default-initialized state
CONSTRUCTING,
INITIALIZING,
INITIALIZED,
DELETED
} EInitState;
// Base-class constructor should only be invoked by the DERIVED_TYPE
// constructor.
LLSingletonBase();
virtual ~LLSingletonBase();
// Every new LLSingleton should be added to/removed from the master list
void add_master();
void remove_master();
// with a little help from our friends.
template <class T> friend struct LLSingleton_manage_master;
// Maintain a stack of the LLSingleton subclass instance currently being
// initialized. We use this to notice direct dependencies: we want to know
// if A requires B. We deduce that if while initializing A, control
// reaches B::getInstance().
// We want &A to be at the top of that stack during both A::A() and
// A::initSingleton(), since a call to B::getInstance() might occur during
// either.
// Unfortunately the desired timespan does not correspond neatly with a
// single C++ scope, else we'd use RAII to track it. But we do know that
// LLSingletonBase's constructor definitely runs just before
// LLSingleton's, which runs just before the specific subclass's.
void push_initializing();
// LLSingleton is, and must remain, the only caller to initSingleton().
// That being the case, we control exactly when it happens -- and we can
// pop the stack immediately thereafter.
void pop_initializing();
// If a given call to B::getInstance() happens during either A::A() or
// A::initSingleton(), record that A directly depends on B.
void capture_dependency(EInitState);
// delegate LL_ERRS() logging to llsingleton.cpp
static void logerrs(const char* p1, const char* p2="",
const char* p3="", const char* p4="");
// delegate LL_WARNS() logging to llsingleton.cpp
static void logwarns(const char* p1, const char* p2="",
const char* p3="", const char* p4="");
// obtain canonical ref_ptr_t
static ref_ptr_t get_master_refcount();
// Default methods in case subclass doesn't declare them.
virtual void initSingleton() {}
virtual void cleanupSingleton() {}
// deleteSingleton() isn't -- and shouldn't be -- a virtual method. It's a
// class static. However, given only Foo*, deleteAll() does need to be
// able to reach Foo::deleteSingleton(). Make LLSingleton (which declares
// deleteSingleton()) store a pointer here. Since we know it's a static
// class method, a classic-C function pointer will do.
void (*mDeleteSingleton)();
public:
/**
* Call this to call the cleanupSingleton() method for every LLSingleton
* constructed since the start of the last cleanupAll() call. (Any
* LLSingleton constructed DURING a cleanupAll() call won't be cleaned up
* until the next cleanupAll() call.) cleanupSingleton() neither deletes
* nor destroys its LLSingleton; therefore it's safe to include logic that
* might take significant realtime or even throw an exception.
*
* The most important property of cleanupAll() is that cleanupSingleton()
* methods are called in dependency order, leaf classes last. Thus, given
* two LLSingleton subclasses A and B, if A's dependency on B is properly
* expressed as a B::getInstance() or B::instance() call during either
* A::A() or A::initSingleton(), B will be cleaned up after A.
*
* If a cleanupSingleton() method throws an exception, the exception is
* logged, but cleanupAll() attempts to continue calling the rest of the
* cleanupSingleton() methods.
*/
static void cleanupAll();
/**
* Call this to call the deleteSingleton() method for every LLSingleton
* constructed since the start of the last deleteAll() call. (Any
* LLSingleton constructed DURING a deleteAll() call won't be cleaned up
* until the next deleteAll() call.) deleteSingleton() deletes and
* destroys its LLSingleton. Any cleanup logic that might take significant
* realtime -- or throw an exception -- must not be placed in your
* LLSingleton's destructor, but rather in its cleanupSingleton() method.
*
* The most important property of deleteAll() is that deleteSingleton()
* methods are called in dependency order, leaf classes last. Thus, given
* two LLSingleton subclasses A and B, if A's dependency on B is properly
* expressed as a B::getInstance() or B::instance() call during either
* A::A() or A::initSingleton(), B will be cleaned up after A.
*
* If a deleteSingleton() method throws an exception, the exception is
* logged, but deleteAll() attempts to continue calling the rest of the
* deleteSingleton() methods.
*/
static void deleteAll();
};
// support ref_ptr_t
void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount*);
void intrusive_ptr_release(LLSingletonBase::MasterRefcount*);
// Most of the time, we want LLSingleton_manage_master() to forward its
// methods to LLSingletonBase::add_master() and remove_master().
template <class T>
struct LLSingleton_manage_master
{
void add(LLSingletonBase* sb) { sb->add_master(); }
void remove(LLSingletonBase* sb) { sb->remove_master(); }
};
// But for the specific case of LLSingletonBase::MasterList, don't.
template <>
struct LLSingleton_manage_master<LLSingletonBase::MasterList>
{
void add(LLSingletonBase*) {}
void remove(LLSingletonBase*) {}
};
/**
* LLSingleton implements the getInstance() method part of the Singleton
* pattern. It can't make the derived class constructors protected, though, so
* you have to do that yourself.
*
* Derive your class from LLSingleton, passing your subclass name as
* LLSingleton's template parameter, like so:
*
* class Foo: public LLSingleton<Foo>{};
*
* Foo& instance = Foo::instance();
*
* LLSingleton recognizes a couple special methods in your derived class.
*
* If you override LLSingleton<T>::initSingleton(), your method will be called
* immediately after the instance is constructed. This is useful for breaking
* circular dependencies: if you find that your LLSingleton subclass
* constructor references other LLSingleton subclass instances in a chain
* leading back to yours, move the instance reference from your constructor to
* your initSingleton() method.
*
* If you override LLSingleton<T>::cleanupSingleton(), your method will be
* called if someone calls LLSingletonBase::cleanupAll(). The significant part
* of this promise is that cleanupAll() will call individual
* cleanupSingleton() methods in reverse dependency order.
*
* That is, consider LLSingleton subclasses C, B and A. A depends on B, which
* in turn depends on C. These dependencies are expressed as calls to
* B::instance() or B::getInstance(), and C::instance() or C::getInstance().
* It shouldn't matter whether these calls appear in A::A() or
* A::initSingleton(), likewise B::B() or B::initSingleton().
*
* We promise that if you later call LLSingletonBase::cleanupAll():
* 1. A::cleanupSingleton() will be called before
* 2. B::cleanupSingleton(), which will be called before
* 3. C::cleanupSingleton().
* Put differently, if your LLSingleton subclass constructor or
* initSingleton() method explicitly depends on some other LLSingleton
* subclass, you may continue to rely on that other subclass in your
* cleanupSingleton() method.
*
* We introduce a special cleanupSingleton() method because cleanupSingleton()
* operations can involve nontrivial realtime, or might throw an exception. A
* destructor should do neither!
*
* If your cleanupSingleton() method throws an exception, we log that
* exception but proceed with the remaining cleanupSingleton() calls.
*
* Similarly, if at some point you call LLSingletonBase::deleteAll(), all
* remaining LLSingleton instances will be destroyed in dependency order. (Or
* call MySubclass::deleteSingleton() to specifically destroy the canonical
* MySubclass instance.)
*
* As currently written, LLSingleton is not thread-safe.
*/
template <typename DERIVED_TYPE>
class LLSingleton : public LLSingletonBase
{
private:
typedef enum e_init_state
{
UNINITIALIZED,
CONSTRUCTING,
INITIALIZING,
INITIALIZED,
DELETED
} EInitState;
static DERIVED_TYPE* constructSingleton()
{
return new DERIVED_TYPE();
}
// stores pointer to singleton instance
struct SingletonLifetimeManager
{
SingletonLifetimeManager()
{
construct();
}
static void construct()
{
sData.mInitState = CONSTRUCTING;
sData.mInstance = constructSingleton();
sData.mInitState = INITIALIZING;
}
// stores pointer to singleton instance
struct SingletonLifetimeManager
{
SingletonLifetimeManager():
mMasterRefcount(LLSingletonBase::get_master_refcount())
{
construct();
}
static void construct()
{
sData.mInitState = CONSTRUCTING;
sData.mInstance = constructSingleton();
sData.mInitState = INITIALIZING;
}
~SingletonLifetimeManager()
{
// The dependencies between LLSingletons, and the arbitrary order
// of static-object destruction, mean that we DO NOT WANT this
// destructor to delete this LLSingleton. This destructor will run
// without regard to any other LLSingleton whose cleanup might
// depend on its existence. What we really want is to count the
// runtime's attempts to cleanup LLSingleton static data -- and on
// the very last one, call LLSingletonBase::deleteAll(). That
// method will properly honor cross-LLSingleton dependencies. This
// is why we store an intrusive_ptr to a MasterRefcount: our
// ref_ptr_t member counts SingletonLifetimeManager instances.
// Once the runtime destroys the last of these, THEN we can delete
// every remaining LLSingleton.
}
LLSingletonBase::ref_ptr_t mMasterRefcount;
};
protected:
LLSingleton()
{
// populate base-class function pointer with the static
// deleteSingleton() function for this particular specialization
mDeleteSingleton = &deleteSingleton;
// add this new instance to the master list
LLSingleton_manage_master<DERIVED_TYPE>().add(this);
}
~SingletonLifetimeManager()
{
if (sData.mInitState != DELETED)
{
deleteSingleton();
}
}
};
public:
virtual ~LLSingleton()
{
sData.mInstance = NULL;
sData.mInitState = DELETED;
}
virtual ~LLSingleton()
{
// remove this instance from the master list
LLSingleton_manage_master<DERIVED_TYPE>().remove(this);
sData.mInstance = NULL;
sData.mInitState = DELETED;
}
/**
* @brief Immediately delete the singleton.
*
* A subsequent call to LLProxy::getInstance() will construct a new
* instance of the class.
*
* LLSingletons are normally destroyed after main() has exited and the C++
* runtime is cleaning up statically-constructed objects. Some classes
* derived from LLSingleton have objects that are part of a runtime system
* that is terminated before main() exits. Calling the destructor of those
* objects after the termination of their respective systems can cause
* crashes and other problems during termination of the project. Using this
* method to destroy the singleton early can prevent these crashes.
*
* An example where this is needed is for a LLSingleton that has an APR
* object as a member that makes APR calls on destruction. The APR system is
* shut down explicitly before main() exits. This causes a crash on exit.
* Using this method before the call to apr_terminate() and NOT calling
* getInstance() again will prevent the crash.
*/
static void deleteSingleton()
{
delete sData.mInstance;
sData.mInstance = NULL;
sData.mInitState = DELETED;
}
/**
* @brief Immediately delete the singleton.
*
* A subsequent call to LLProxy::getInstance() will construct a new
* instance of the class.
*
* Without an explicit call to LLSingletonBase::deleteAll(), LLSingletons
* are implicitly destroyed after main() has exited and the C++ runtime is
* cleaning up statically-constructed objects. Some classes derived from
* LLSingleton have objects that are part of a runtime system that is
* terminated before main() exits. Calling the destructor of those objects
* after the termination of their respective systems can cause crashes and
* other problems during termination of the project. Using this method to
* destroy the singleton early can prevent these crashes.
*
* An example where this is needed is for a LLSingleton that has an APR
* object as a member that makes APR calls on destruction. The APR system is
* shut down explicitly before main() exits. This causes a crash on exit.
* Using this method before the call to apr_terminate() and NOT calling
* getInstance() again will prevent the crash.
*/
static void deleteSingleton()
{
delete sData.mInstance;
sData.mInstance = NULL;
sData.mInitState = DELETED;
}
static DERIVED_TYPE* getInstance()
{
static SingletonLifetimeManager sLifeTimeMgr;
static DERIVED_TYPE* getInstance()
{
static SingletonLifetimeManager sLifeTimeMgr;
switch (sData.mInitState)
{
case UNINITIALIZED:
// should never be uninitialized at this point
logerrs("Uninitialized singleton ", typeid(DERIVED_TYPE).name());
return NULL;
switch (sData.mInitState)
{
case UNINITIALIZED:
// should never be uninitialized at this point
llassert(false);
return NULL;
case CONSTRUCTING:
LL_ERRS() << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << LL_ENDL;
return NULL;
case INITIALIZING:
// go ahead and flag ourselves as initialized so we can be reentrant during initialization
sData.mInitState = INITIALIZED;
// initialize singleton after constructing it so that it can reference other singletons which in turn depend on it,
// thus breaking cyclic dependencies
sData.mInstance->initSingleton();
return sData.mInstance;
case INITIALIZED:
return sData.mInstance;
case DELETED:
LL_WARNS() << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << LL_ENDL;
SingletonLifetimeManager::construct();
// same as first time construction
sData.mInitState = INITIALIZED;
sData.mInstance->initSingleton();
return sData.mInstance;
}
case CONSTRUCTING:
logerrs("Tried to access singleton ", typeid(DERIVED_TYPE).name(),
" from singleton constructor!");
return NULL;
return NULL;
}
case INITIALIZING:
// go ahead and flag ourselves as initialized so we can be
// reentrant during initialization
sData.mInitState = INITIALIZED;
// initialize singleton after constructing it so that it can
// reference other singletons which in turn depend on it, thus
// breaking cyclic dependencies
sData.mInstance->initSingleton();
// pop this off stack of initializing singletons
sData.mInstance->pop_initializing();
break;
static DERIVED_TYPE* getIfExists()
{
return sData.mInstance;
}
case INITIALIZED:
break;
// Reference version of getInstance()
// Preferred over getInstance() as it disallows checking for NULL
static DERIVED_TYPE& instance()
{
return *getInstance();
}
// Has this singleton been created uet?
// Use this to avoid accessing singletons before the can safely be constructed
static bool instanceExists()
{
return sData.mInitState == INITIALIZED;
}
// Has this singleton already been deleted?
// Use this to avoid accessing singletons from a static object's destructor
static bool destroyed()
{
return sData.mInitState == DELETED;
}
case DELETED:
logwarns("Trying to access deleted singleton ", typeid(DERIVED_TYPE).name(),
" -- creating new instance");
SingletonLifetimeManager::construct();
// same as first time construction
sData.mInitState = INITIALIZED;
sData.mInstance->initSingleton();
// pop this off stack of initializing singletons
sData.mInstance->pop_initializing();
break;
}
// By this point, if DERIVED_TYPE was pushed onto the initializing
// stack, it has been popped off. So the top of that stack, if any, is
// an LLSingleton that directly depends on DERIVED_TYPE. If this call
// came from another LLSingleton, rather than from vanilla application
// code, record the dependency.
sData.mInstance->capture_dependency(sData.mInitState);
return sData.mInstance;
}
// Reference version of getInstance()
// Preferred over getInstance() as it disallows checking for NULL
static DERIVED_TYPE& instance()
{
return *getInstance();
}
// Has this singleton been created yet?
// Use this to avoid accessing singletons before they can safely be constructed.
static bool instanceExists()
{
return sData.mInitState == INITIALIZED;
}
private:
virtual void initSingleton() {}
struct SingletonData
{
// explicitly has a default constructor so that member variables are zero initialized in BSS
// and only changed by singleton logic, not constructor running during startup
EInitState mInitState;
DERIVED_TYPE* mInstance;
};
static SingletonData sData;
struct SingletonData
{
// explicitly has a default constructor so that member variables are zero initialized in BSS
// and only changed by singleton logic, not constructor running during startup
EInitState mInitState;
DERIVED_TYPE* mInstance;
};
static SingletonData sData;
};
template<typename T>

View File

@ -0,0 +1,230 @@
/**
* @file llpounceable_test.cpp
* @author Nat Goodspeed
* @date 2015-05-22
* @brief Test for llpounceable.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llpounceable.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "../test/lltut.h"
/*----------------------------- string testing -----------------------------*/
void append(std::string* dest, const std::string& src)
{
dest->append(src);
}
/*-------------------------- Data-struct testing ---------------------------*/
struct Data
{
Data(const std::string& data):
mData(data)
{}
const std::string mData;
};
void setter(Data** dest, Data* ptr)
{
*dest = ptr;
}
static Data* static_check = 0;
// Set up an extern pointer to an LLPounceableStatic so the linker will fill
// in the forward reference from below, before runtime.
extern LLPounceable<Data*, LLPounceableStatic> gForward;
struct EnqueueCall
{
EnqueueCall()
{
// Intentionally use a forward reference to an LLPounceableStatic that
// we believe is NOT YET CONSTRUCTED. This models the scenario in
// which a constructor in another translation unit runs before
// constructors in this one. We very specifically want callWhenReady()
// to work even in that case: we need the LLPounceableQueueImpl to be
// initialized even if the LLPounceable itself is not.
gForward.callWhenReady(boost::bind(setter, &static_check, _1));
}
} nqcall;
// When this declaration is processed, we should enqueue the
// setter(&static_check, _1) call for when gForward is set non-NULL. Needless
// to remark, we want this call not to crash.
// Now declare gForward. Its constructor should not run until after nqcall's.
LLPounceable<Data*, LLPounceableStatic> gForward;
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llpounceable_data
{
};
typedef test_group<llpounceable_data> llpounceable_group;
typedef llpounceable_group::object object;
llpounceable_group llpounceablegrp("llpounceable");
template<> template<>
void object::test<1>()
{
set_test_name("LLPounceableStatic out-of-order test");
// LLPounceable<T, LLPounceableStatic>::callWhenReady() must work even
// before LLPounceable's constructor runs. That's the whole point of
// implementing it with an LLSingleton queue. This models (say)
// LLPounceableStatic<LLMessageSystem*, LLPounceableStatic>.
ensure("static_check should still be null", ! static_check);
Data myData("test<1>");
gForward = &myData; // should run setter
ensure_equals("static_check should be &myData", static_check, &myData);
}
template<> template<>
void object::test<2>()
{
set_test_name("LLPounceableQueue different queues");
// We expect that LLPounceable<T, LLPounceableQueue> should have
// different queues because that specialization stores the queue
// directly in the LLPounceable instance.
Data *aptr = 0, *bptr = 0;
LLPounceable<Data*> a, b;
a.callWhenReady(boost::bind(setter, &aptr, _1));
b.callWhenReady(boost::bind(setter, &bptr, _1));
ensure("aptr should be null", ! aptr);
ensure("bptr should be null", ! bptr);
Data adata("a"), bdata("b");
a = &adata;
ensure_equals("aptr should be &adata", aptr, &adata);
// but we haven't yet set b
ensure("bptr should still be null", !bptr);
b = &bdata;
ensure_equals("bptr should be &bdata", bptr, &bdata);
}
template<> template<>
void object::test<3>()
{
set_test_name("LLPounceableStatic different queues");
// LLPounceable<T, LLPounceableStatic> should also have a distinct
// queue for each instance, but that engages an additional map lookup
// because there's only one LLSingleton for each T.
Data *aptr = 0, *bptr = 0;
LLPounceable<Data*, LLPounceableStatic> a, b;
a.callWhenReady(boost::bind(setter, &aptr, _1));
b.callWhenReady(boost::bind(setter, &bptr, _1));
ensure("aptr should be null", ! aptr);
ensure("bptr should be null", ! bptr);
Data adata("a"), bdata("b");
a = &adata;
ensure_equals("aptr should be &adata", aptr, &adata);
// but we haven't yet set b
ensure("bptr should still be null", !bptr);
b = &bdata;
ensure_equals("bptr should be &bdata", bptr, &bdata);
}
template<> template<>
void object::test<4>()
{
set_test_name("LLPounceable<T> looks like T");
// We want LLPounceable<T, TAG> to be drop-in replaceable for a plain
// T for read constructs. In particular, it should behave like a dumb
// pointer -- and with zero abstraction cost for such usage.
Data* aptr = 0;
Data a("a");
// should be able to initialize a pounceable (when its constructor
// runs)
LLPounceable<Data*> pounceable(&a);
// should be able to pass LLPounceable<T> to function accepting T
setter(&aptr, pounceable);
ensure_equals("aptr should be &a", aptr, &a);
// should be able to dereference with *
ensure_equals("deref with *", (*pounceable).mData, "a");
// should be able to dereference with ->
ensure_equals("deref with ->", pounceable->mData, "a");
// bool operations
ensure("test with operator bool()", pounceable);
ensure("test with operator !()", ! (! pounceable));
}
template<> template<>
void object::test<5>()
{
set_test_name("Multiple callWhenReady() queue items");
Data *p1 = 0, *p2 = 0, *p3 = 0;
Data a("a");
LLPounceable<Data*> pounceable;
// queue up a couple setter() calls for later
pounceable.callWhenReady(boost::bind(setter, &p1, _1));
pounceable.callWhenReady(boost::bind(setter, &p2, _1));
// should still be pending
ensure("p1 should be null", !p1);
ensure("p2 should be null", !p2);
ensure("p3 should be null", !p3);
pounceable = 0;
// assigning a new empty value shouldn't flush the queue
ensure("p1 should still be null", !p1);
ensure("p2 should still be null", !p2);
ensure("p3 should still be null", !p3);
// using whichever syntax
pounceable.reset(0);
// try to make ensure messages distinct... tough to pin down which
// ensure() failed if multiple ensure() calls in the same test<n> have
// the same message!
ensure("p1 should again be null", !p1);
ensure("p2 should again be null", !p2);
ensure("p3 should again be null", !p3);
pounceable.reset(&a); // should flush queue
ensure_equals("p1 should be &a", p1, &a);
ensure_equals("p2 should be &a", p2, &a);
ensure("p3 still not set", !p3);
// immediate call
pounceable.callWhenReady(boost::bind(setter, &p3, _1));
ensure_equals("p3 should be &a", p3, &a);
}
template<> template<>
void object::test<6>()
{
set_test_name("queue order");
std::string data;
LLPounceable<std::string*> pounceable;
pounceable.callWhenReady(boost::bind(append, _1, "a"));
pounceable.callWhenReady(boost::bind(append, _1, "b"));
pounceable.callWhenReady(boost::bind(append, _1, "c"));
pounceable = &data;
ensure_equals("callWhenReady() must preserve chronological order",
data, "abc");
std::string data2;
pounceable = NULL;
pounceable.callWhenReady(boost::bind(append, _1, "d"));
pounceable.callWhenReady(boost::bind(append, _1, "e"));
pounceable.callWhenReady(boost::bind(append, _1, "f"));
pounceable = &data2;
ensure_equals("LLPounceable must reset queue when fired",
data2, "def");
}
template<> template<>
void object::test<7>()
{
set_test_name("compile-fail test, uncomment to check");
// The following declaration should fail: only LLPounceableQueue and
// LLPounceableStatic should work as tags.
// LLPounceable<Data*, int> pounceable;
}
} // namespace tut

View File

@ -65,7 +65,6 @@ namespace tut
//Delete the instance
LLSingletonTest::deleteSingleton();
ensure(LLSingletonTest::destroyed());
ensure(!LLSingletonTest::instanceExists());
//Construct it again.

View File

@ -46,6 +46,7 @@
#include "test_httprequestqueue.hpp"
#include "llproxy.h"
#include "llcleanup.h"
unsigned long ssl_thread_id_callback(void);
void ssl_locking_callback(int mode, int type, const char * file, int line);
@ -101,7 +102,7 @@ void init_curl()
void term_curl()
{
LLProxy::cleanupClass();
SUBSYSTEM_CLEANUP(LLProxy);
CRYPTO_set_locking_callback(NULL);
for (int i(0); i < ssl_mutex_count; ++i)

View File

@ -45,6 +45,7 @@
#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llproxy.h"
#include "llcleanup.h"
LLPumpIO* gServicePump = NULL;
BOOL gBreak = false;
@ -587,5 +588,5 @@ bool LLCrashLogger::init()
void LLCrashLogger::commonCleanup()
{
LLError::logToFile(""); //close crashreport.log
LLProxy::cleanupClass();
SUBSYSTEM_CLEANUP(LLProxy);
}

View File

@ -42,19 +42,12 @@ public:
virtual void onEconomyDataChange() = 0;
};
class LLGlobalEconomy
class LLGlobalEconomy: public LLSingleton<LLGlobalEconomy>
{
public:
LLGlobalEconomy();
virtual ~LLGlobalEconomy();
// This class defines its singleton internally as a typedef instead of inheriting from
// LLSingleton like most others because the LLRegionEconomy sub-class might also
// become a singleton and this pattern will more easily disambiguate them.
typedef LLSingleton<LLGlobalEconomy> Singleton;
void initSingleton() { }
virtual void print();
void addObserver(LLEconomyObserver* observer);

View File

@ -28,9 +28,8 @@
#define LL_HTTPCLIENTADAPTER_H
#include "llhttpclientinterface.h"
#include "llsingleton.h" // LLSingleton<>
class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter>
class LLHTTPClientAdapter : public LLHTTPClientInterface
{
public:
virtual ~LLHTTPClientAdapter();

View File

@ -77,6 +77,7 @@
#include "v3math.h"
#include "v4math.h"
#include "lltransfertargetvfile.h"
#include "llpounceable.h"
// Constants
//const char* MESSAGE_LOG_FILENAME = "message.log";
@ -1776,7 +1777,9 @@ std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg)
return s;
}
LLMessageSystem *gMessageSystem = NULL;
// LLPounceable supports callWhenReady(), to permit clients to queue up (e.g.)
// callback registrations for when gMessageSystem is first assigned
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
// update appropriate ping info
void process_complete_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
@ -2693,7 +2696,7 @@ void end_messaging_system(bool print_summary)
LL_INFOS("Messaging") << str.str().c_str() << LL_ENDL;
}
delete gMessageSystem;
delete static_cast<LLMessageSystem*>(gMessageSystem);
gMessageSystem = NULL;
}
}

View File

@ -60,6 +60,7 @@
#include "llmessagesenderinterface.h"
#include "llstoredmessage.h"
#include "llpounceable.h"
const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
@ -830,7 +831,7 @@ private:
// external hook into messaging system
extern LLMessageSystem *gMessageSystem;
extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
// Must specific overall system version, which is used to determine
// if a patch is available in the message template checksum verification.

View File

@ -42,6 +42,7 @@
#include "lliosocket.h"
#include "stringize.h"
#include "llcleanup.h"
namespace tut
{
@ -66,7 +67,7 @@ namespace tut
~HTTPClientTestData()
{
delete mClientPump;
LLProxy::cleanupClass();
SUBSYSTEM_CLEANUP(LLProxy);
apr_pool_destroy(mPool);
}

View File

@ -31,11 +31,12 @@
#include "llhost.h"
#include "message.h"
#include "llsd.h"
#include "llpounceable.h"
#include "llhost.cpp" // Needed for copy operator
#include "net.cpp" // Needed by LLHost.
LLMessageSystem * gMessageSystem = NULL;
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
// sensor test doubles
bool gClearRecvWasCalled = false;

View File

@ -33,8 +33,9 @@
#include "message.h"
#include "llmessageconfig.h"
#include "llhttpnode_stub.cpp"
#include "llpounceable.h"
LLMessageSystem* gMessageSystem = NULL;
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
LLMessageConfig::SenderTrust
LLMessageConfig::getSenderTrustedness(const std::string& msg_name)

View File

@ -170,7 +170,8 @@ LLFolderView::LLFolderView(const Params& p)
mDraggingOverItem(NULL),
mStatusTextBox(NULL),
mShowItemLinkOverlays(p.show_item_link_overlays),
mViewModel(p.view_model)
mViewModel(p.view_model),
mGroupedItemModel(p.grouped_item_model)
{
claimMem(mViewModel);
LLPanel* panel = p.parent_panel;
@ -1810,7 +1811,6 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)
}
// Successively filter out invalid options
U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0);
U32 flags = multi_select_flag | FIRST_SELECTED_ITEM;
for (selected_items_t::iterator item_itor = mSelectedItems.begin();
@ -1822,6 +1822,14 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)
flags = multi_select_flag;
}
// This adds a check for restrictions based on the entire
// selection set - for example, any one wearable may not push you
// over the limit, but all wearables together still might.
if (getFolderViewGroupedItemModel())
{
getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems,*menu);
}
addNoOptions(menu);
}

View File

@ -45,6 +45,7 @@
#include "llscrollcontainer.h"
class LLFolderViewModelInterface;
class LLFolderViewGroupedItemModel;
class LLFolderViewFolder;
class LLFolderViewItem;
class LLFolderViewFilter;
@ -93,6 +94,7 @@ public:
use_ellipses,
show_item_link_overlays;
Mandatory<LLFolderViewModelInterface*> view_model;
Optional<LLFolderViewGroupedItemModel*> grouped_item_model;
Mandatory<std::string> options_menu;
@ -100,7 +102,7 @@ public:
};
friend class LLFolderViewScrollContainer;
typedef std::deque<LLFolderViewItem*> selected_items_t;
typedef folder_view_item_deque selected_items_t;
LLFolderView(const Params&);
virtual ~LLFolderView( void );
@ -113,6 +115,9 @@ public:
LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; }
const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; }
LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; }
const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; }
typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)> signal_t;
void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); }
void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); }
@ -300,6 +305,7 @@ protected:
LLHandle<LLPanel> mParentPanel;
LLFolderViewModelInterface* mViewModel;
LLFolderViewGroupedItemModel* mGroupedItemModel;
/**
* Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll.

7
indra/llui/llfolderviewitem.h Normal file → Executable file
View File

@ -454,5 +454,12 @@ public:
template<typename SORT_FUNC> void sortItems(const SORT_FUNC& func) { mItems.sort(func); }
};
typedef std::deque<LLFolderViewItem*> folder_view_item_deque;
class LLFolderViewGroupedItemModel: public LLRefCount
{
public:
virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0;
};
#endif // LLFOLDERVIEWITEM_H

View File

@ -29,6 +29,7 @@
#include "llsingleton.h"
#include "llui.h"
#include "llinitdestroyclass.h"
#include <boost/signals2.hpp>
class Hunspell;

View File

@ -60,6 +60,7 @@
#include "llflyoutbutton.h"
#include "llsearcheditor.h"
#include "lltoolbar.h"
#include "llcleanup.h"
// for XUIParse
#include "llquaternion.h"
@ -208,7 +209,7 @@ void LLUI::initClass(const settings_map_t& settings,
void LLUI::cleanupClass()
{
LLRender2D::cleanupClass();
SUBSYSTEM_CLEANUP(LLRender2D);
}
void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)

View File

@ -344,95 +344,6 @@ private:
// Moved LLLocalClipRect to lllocalcliprect.h
class LLCallbackRegistry
{
public:
typedef boost::signals2::signal<void()> callback_signal_t;
void registerCallback(const callback_signal_t::slot_type& slot)
{
mCallbacks.connect(slot);
}
void fireCallbacks()
{
mCallbacks();
}
private:
callback_signal_t mCallbacks;
};
class LLInitClassList :
public LLCallbackRegistry,
public LLSingleton<LLInitClassList>
{
friend class LLSingleton<LLInitClassList>;
private:
LLInitClassList() {}
};
class LLDestroyClassList :
public LLCallbackRegistry,
public LLSingleton<LLDestroyClassList>
{
friend class LLSingleton<LLDestroyClassList>;
private:
LLDestroyClassList() {}
};
template<typename T>
class LLRegisterWith
{
public:
LLRegisterWith(boost::function<void ()> func)
{
T::instance().registerCallback(func);
}
// this avoids a MSVC bug where non-referenced static members are "optimized" away
// even if their constructors have side effects
S32 reference()
{
S32 dummy;
dummy = 0;
return dummy;
}
};
template<typename T>
class LLInitClass
{
public:
LLInitClass() { sRegister.reference(); }
static LLRegisterWith<LLInitClassList> sRegister;
private:
static void initClass()
{
LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
template<typename T>
class LLDestroyClass
{
public:
LLDestroyClass() { sRegister.reference(); }
static LLRegisterWith<LLDestroyClassList> sRegister;
private:
static void destroyClass()
{
LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass);
template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass);
// useful parameter blocks
struct TimeIntervalParam : public LLInitParam::ChoiceBlock<TimeIntervalParam>
{

View File

@ -31,6 +31,7 @@
#include "llcachename.h"
#include "lluuid.h"
#include "message.h"
#include "llpounceable.h"
#include <string>
@ -167,7 +168,7 @@ char const* const _PREHASH_AgentID = (char *)"AgentID";
LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
LLMessageSystem* gMessageSystem = NULL;
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
//
// Stub implementation for LLMessageSystem

View File

@ -102,7 +102,7 @@ int yyerror(const char *fmt, ...);
"event" { count(); return(EVENT); }
"jump" { count(); return(JUMP); }
"return" { count(); return(RETURN); }
"if" { count(); return(IF); }
"if" { count(); return(IFF); }
"else" { count(); return(ELSE); }
"for" { count(); return(FOR); }
"do" { count(); return(DO); }

View File

@ -117,7 +117,7 @@
%token SHIFT_LEFT
%token SHIFT_RIGHT
%token IF
%token IFF /* IF used by a helper library */
%token ELSE
%token FOR
%token DO
@ -1312,13 +1312,13 @@ statement
{
$$ = $1;
}
| IF '(' expression ')' statement %prec LOWER_THAN_ELSE
| IFF '(' expression ')' statement %prec LOWER_THAN_ELSE
{
$$ = new LLScriptIf(gLine, gColumn, $3, $5);
$5->mAllowDeclarations = FALSE;
gAllocationManager->addAllocation($$);
}
| IF '(' expression ')' statement ELSE statement
| IFF '(' expression ')' statement ELSE statement
{
$$ = new LLScriptIfElse(gLine, gColumn, $3, $5, $7);
$5->mAllowDeclarations = FALSE;

View File

@ -1 +1 @@
3.7.29
3.7.30

View File

@ -633,10 +633,13 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed)
// the versions themselves are compatible. This code can be removed before release.
if( wearable->getDefinitionVersion() == 24 )
{
wearable->setDefinitionVersion(22);
U32 index = getWearableIndex(wearable);
LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL;
saveWearable(wearable->getType(),index);
U32 index;
if (getWearableIndex(wearable,index))
{
LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL;
wearable->setDefinitionVersion(22);
saveWearable(wearable->getType(),index);
}
}
checkWearableAgainstInventory(viewer_wearable);
@ -949,7 +952,7 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo
LLViewerWearable* old_wearable = getViewerWearable(type,i);
if (old_wearable)
{
popWearable(old_wearable);
eraseWearable(old_wearable);
old_wearable->removeFromAvatar();
}
}
@ -961,7 +964,7 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo
if (old_wearable)
{
popWearable(old_wearable);
eraseWearable(old_wearable);
old_wearable->removeFromAvatar();
}
}
@ -1163,7 +1166,13 @@ bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD&
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
LLInventoryItem* new_item = gInventory.getItem(notification["payload"]["item_id"].asUUID());
U32 index = gAgentWearables.getWearableIndex(wearable);
U32 index;
if (!gAgentWearables.getWearableIndex(wearable,index))
{
LL_WARNS() << "Wearable not found" << LL_ENDL;
delete wearable;
return false;
}
if (!new_item)
{
delete wearable;

View File

@ -38,6 +38,7 @@
#include "llviewerinventory.h"
#include "llavatarappearancedefines.h"
#include "llwearabledata.h"
#include "llinitdestroyclass.h"
class LLInventoryItem;
class LLVOAvatarSelf;

View File

@ -1338,90 +1338,113 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)
}
}
bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,
void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
bool do_update,
bool replace,
LLPointer<LLInventoryCallback> cb)
{
bool first = true;
LLInventoryObject::const_object_list_t items_to_link;
for (uuid_vec_t::const_iterator it = item_ids_to_wear.begin();
it != item_ids_to_wear.end();
++it)
{
replace = first && replace;
first = false;
const LLUUID& item_id_to_wear = *it;
if (item_id_to_wear.isNull()) continue;
LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
if (!item_to_wear) continue;
if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
{
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
continue;
}
else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
{
continue; // not in library and not in agent's inventory
}
else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
{
LLNotificationsUtil::add("CannotWearTrash");
continue;
}
else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911
{
continue;
}
switch (item_to_wear->getType())
{
case LLAssetType::AT_CLOTHING:
{
if (gAgentWearables.areWearablesLoaded())
{
if (!cb && do_update)
{
cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
}
LLWearableType::EType type = item_to_wear->getWearableType();
S32 wearable_count = gAgentWearables.getWearableCount(type);
if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
{
LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
wearable_count-1);
removeCOFItemLinks(item_id, cb);
}
items_to_link.push_back(item_to_wear);
}
}
break;
case LLAssetType::AT_BODYPART:
{
// TODO: investigate wearables may not be loaded at this point EXT-8231
// Remove the existing wearables of the same type.
// Remove existing body parts anyway because we must not be able to wear e.g. two skins.
removeCOFLinksOfType(item_to_wear->getWearableType());
if (!cb && do_update)
{
cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
}
items_to_link.push_back(item_to_wear);
}
break;
case LLAssetType::AT_OBJECT:
{
rez_attachment(item_to_wear, NULL, replace);
}
break;
default: continue;
}
}
// Batch up COF link creation - more efficient if using AIS.
if (items_to_link.size())
{
link_inventory_array(getCOF(), items_to_link, cb);
}
}
void LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,
bool do_update,
bool replace,
LLPointer<LLInventoryCallback> cb)
{
if (item_id_to_wear.isNull()) return false;
// *TODO: issue with multi-wearable should be fixed:
// in this case this method will be called N times - loading started for each item
// and than N times will be called - loading completed for each item.
// That means subscribers will be notified that loading is done after first item in a batch is worn.
// (loading indicator disappears for example before all selected items are worn)
// Have not fix this issue for 2.1 because of stability reason. EXT-7777.
// Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times
// gAgentWearables.notifyLoadingStarted();
LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
if (!item_to_wear) return false;
if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
{
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
return false;
}
else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
{
return false; // not in library and not in agent's inventory
}
else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
{
LLNotificationsUtil::add("CannotWearTrash");
return false;
}
else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911
{
return false;
}
switch (item_to_wear->getType())
{
case LLAssetType::AT_CLOTHING:
if (gAgentWearables.areWearablesLoaded())
{
if (!cb && do_update)
{
cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
}
S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType());
if ((replace && wearable_count != 0) ||
(wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
{
LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
wearable_count-1);
removeCOFItemLinks(item_id, cb);
}
addCOFItemLink(item_to_wear, cb);
}
break;
case LLAssetType::AT_BODYPART:
// TODO: investigate wearables may not be loaded at this point EXT-8231
// Remove the existing wearables of the same type.
// Remove existing body parts anyway because we must not be able to wear e.g. two skins.
removeCOFLinksOfType(item_to_wear->getWearableType());
if (!cb && do_update)
{
cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
}
addCOFItemLink(item_to_wear, cb);
break;
case LLAssetType::AT_OBJECT:
rez_attachment(item_to_wear, NULL, replace);
break;
default: return false;;
}
return true;
uuid_vec_t ids;
ids.push_back(item_id_to_wear);
wearItemsOnAvatar(ids, do_update, replace, cb);
}
// Update appearance from outfit folder.
@ -1782,6 +1805,49 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
return items.size() > 0;
}
// Moved from LLWearableList::ContextMenu for wider utility.
bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids)
{
// TODO: investigate wearables may not be loaded at this point EXT-8231
U32 n_objects = 0;
U32 n_clothes = 0;
// Count given clothes (by wearable type) and objects.
for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
{
LLViewerInventoryItem* item = gInventory.getItem(*it);
if (!item)
{
return false;
}
if (item->getType() == LLAssetType::AT_OBJECT)
{
++n_objects;
}
else if (item->getType() == LLAssetType::AT_CLOTHING)
{
++n_clothes;
}
else
{
LL_WARNS() << "Unexpected wearable type" << LL_ENDL;
return false;
}
}
// Check whether we can add all the objects.
if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects))
{
return false;
}
// Check whether we can add all the clothes.
U32 sum_clothes = n_clothes + gAgentWearables.getClothingLayerCount();
return sum_clothes <= LLAgentWearables::MAX_CLOTHING_LAYERS;
}
void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t cats;
@ -1804,25 +1870,39 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLIn
// Keep the last N wearables of each type. For viewer 2.0, N is 1 for
// both body parts and clothing items.
void LLAppearanceMgr::filterWearableItems(
LLInventoryModel::item_array_t& items, S32 max_per_type)
LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total)
{
// Divvy items into arrays by wearable type.
std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
divvyWearablesByType(items, items_by_type);
// Restrict by max total items first.
if ((max_total > 0) && (items.size() > max_total))
{
LLInventoryModel::item_array_t items_to_keep;
for (S32 i=0; i<max_total; i++)
{
items_to_keep.push_back(items[i]);
}
items = items_to_keep;
}
// rebuild items list, retaining the last max_per_type of each array
items.clear();
for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
{
S32 size = items_by_type[i].size();
if (size <= 0)
continue;
S32 start_index = llmax(0,size-max_per_type);
for (S32 j = start_index; j<size; j++)
{
items.push_back(items_by_type[i][j]);
}
}
if (max_per_type > 0)
{
// Divvy items into arrays by wearable type.
std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
divvyWearablesByType(items, items_by_type);
// rebuild items list, retaining the last max_per_type of each array
items.clear();
for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
{
S32 size = items_by_type[i].size();
if (size <= 0)
continue;
S32 start_index = llmax(0,size-max_per_type);
for (S32 j = start_index; j<size; j++)
{
items.push_back(items_by_type[i][j]);
}
}
}
}
void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
@ -1864,7 +1944,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
reverse(body_items.begin(), body_items.end());
// Reduce body items to max of one per type.
removeDuplicateItems(body_items);
filterWearableItems(body_items, 1);
filterWearableItems(body_items, 1, 0);
// - Wearables: include COF contents only if appending.
LLInventoryModel::item_array_t wear_items;
@ -1873,7 +1953,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING);
// Reduce wearables to max of one per type.
removeDuplicateItems(wear_items);
filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS);
// - Attachments: include COF contents only if appending.
LLInventoryModel::item_array_t obj_items;
@ -2062,7 +2142,8 @@ void item_array_diff(LLInventoryModel::item_array_t& full_list,
S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
LLAssetType::EType type,
S32 max_items,
S32 max_items_per_type,
S32 max_items_total,
LLInventoryObject::object_list_t& items_to_kill)
{
S32 to_kill_count = 0;
@ -2071,9 +2152,9 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
getDescendentsOfAssetType(cat_id, items, type);
LLInventoryModel::item_array_t curr_items = items;
removeDuplicateItems(items);
if (max_items > 0)
if (max_items_per_type > 0 || max_items_total > 0)
{
filterWearableItems(items, max_items);
filterWearableItems(items, max_items_per_type, max_items_total);
}
LLInventoryModel::item_array_t kill_items;
item_array_diff(curr_items,items,kill_items);
@ -2092,11 +2173,11 @@ void LLAppearanceMgr::findAllExcessOrDuplicateItems(const LLUUID& cat_id,
LLInventoryObject::object_list_t& items_to_kill)
{
findExcessOrDuplicateItems(cat_id,LLAssetType::AT_BODYPART,
1, items_to_kill);
1, 0, items_to_kill);
findExcessOrDuplicateItems(cat_id,LLAssetType::AT_CLOTHING,
LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
0, LLAgentWearables::MAX_CLOTHING_LAYERS, items_to_kill);
findExcessOrDuplicateItems(cat_id,LLAssetType::AT_OBJECT,
-1, items_to_kill);
0, 0, items_to_kill);
}
void LLAppearanceMgr::enforceCOFItemRestrictions(LLPointer<LLInventoryCallback> cb)
@ -2588,7 +2669,6 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
bool linked_already = false;
U32 count = 0;
for (S32 i=0; i<item_array.size(); i++)
{
// Are these links to the same object?
@ -2608,14 +2688,13 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,
// type? If so, new item will replace old.
else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type))
{
++count;
if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type))
if (is_body_part && inv_item->getIsLinkType())
{
remove_inventory_item(inv_item->getUUID(), cb);
}
else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE)
else if (!gAgentWearables.canAddWearable(wearable_type))
{
// MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE
// MULTI-WEARABLES: make sure we don't go over clothing limits
remove_inventory_item(inv_item->getUUID(), cb);
}
}
@ -4071,16 +4150,7 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
void wear_multiple(const uuid_vec_t& ids, bool replace)
{
LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
bool first = true;
uuid_vec_t::const_iterator it;
for (it = ids.begin(); it != ids.end(); ++it)
{
// if replace is requested, the first item worn will replace the current top
// item, and others will be added.
LLAppearanceMgr::instance().wearItemOnAvatar(*it,false,first && replace,cb);
first = false;
}
LLAppearanceMgr::instance().wearItemsOnAvatar(ids, false, replace, cb);
}
// SLapp for easy-wearing of a stock (library) avatar

View File

@ -67,7 +67,8 @@ public:
void addCategoryToCurrentOutfit(const LLUUID& cat_id);
S32 findExcessOrDuplicateItems(const LLUUID& cat_id,
LLAssetType::EType type,
S32 max_items,
S32 max_items_per_type,
S32 max_items_total,
LLInventoryObject::object_list_t& items_to_kill);
void findAllExcessOrDuplicateItems(const LLUUID& cat_id,
LLInventoryObject::object_list_t& items_to_kill);
@ -99,6 +100,9 @@ public:
// Determine whether we can replace current outfit with the given one.
bool getCanReplaceCOF(const LLUUID& outfit_cat_id);
// Can we add all referenced items to the avatar?
bool canAddWearables(const uuid_vec_t& item_ids);
// Copy all items in a category.
void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
LLPointer<LLInventoryCallback> cb);
@ -117,8 +121,13 @@ public:
// find the UUID of the currently worn outfit (Base Outfit)
const LLUUID getBaseOutfitUUID();
void wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
bool do_update,
bool replace,
LLPointer<LLInventoryCallback> cb = NULL);
// Wear/attach an item (from a user's inventory) on the agent
bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false,
void wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false,
LLPointer<LLInventoryCallback> cb = NULL);
// Update the displayed outfit name in UI.
@ -235,7 +244,7 @@ protected:
private:
void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type);
void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total);
void getDescendentsOfAssetType(const LLUUID& category,
LLInventoryModel::item_array_t& items,

View File

@ -226,6 +226,7 @@
#include "llsecapi.h"
#include "llmachineid.h"
#include "llmainlooprepeater.h"
#include "llcleanup.h"
#include "llviewereventrecorder.h"
@ -1764,7 +1765,7 @@ bool LLAppViewer::cleanup()
gTransferManager.cleanup();
#endif
LLLocalBitmapMgr::cleanupClass();
SUBSYSTEM_CLEANUP(LLLocalBitmapMgr);
// Note: this is where gWorldMap used to be deleted.
@ -1872,11 +1873,11 @@ bool LLAppViewer::cleanup()
LLViewerObject::cleanupVOClasses();
LLAvatarAppearance::cleanupClass();
SUBSYSTEM_CLEANUP(LLAvatarAppearance);
LLAvatarAppearance::cleanupClass();
SUBSYSTEM_CLEANUP(LLAvatarAppearance);
LLPostProcess::cleanupClass();
SUBSYSTEM_CLEANUP(LLPostProcess);
LLTracker::cleanupInstance();
@ -1902,12 +1903,12 @@ bool LLAppViewer::cleanup()
//end_messaging_system();
LLFollowCamMgr::cleanupClass();
//LLVolumeMgr::cleanupClass();
SUBSYSTEM_CLEANUP(LLFollowCamMgr);
//SUBSYSTEM_CLEANUP(LLVolumeMgr);
LLPrimitive::cleanupVolumeManager();
LLWorldMapView::cleanupClass();
LLFolderViewItem::cleanupClass();
LLUI::cleanupClass();
SUBSYSTEM_CLEANUP(LLWorldMapView);
SUBSYSTEM_CLEANUP(LLFolderViewItem);
SUBSYSTEM_CLEANUP(LLUI);
//
// Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles).
@ -1916,7 +1917,7 @@ bool LLAppViewer::cleanup()
//
LL_INFOS() << "Cleaning up VFS" << LL_ENDL;
LLVFile::cleanupClass();
SUBSYSTEM_CLEANUP(LLVFile);
LL_INFOS() << "Saving Data" << LL_ENDL;
@ -2020,7 +2021,7 @@ bool LLAppViewer::cleanup()
// *NOTE:Mani - The following call is not thread safe.
LL_CHECK_MEMORY
LLCurl::cleanupClass();
SUBSYSTEM_CLEANUP(LLCurl);
LL_CHECK_MEMORY
// Non-LLCurl libcurl library
@ -2029,9 +2030,9 @@ bool LLAppViewer::cleanup()
// NOTE The following call is not thread safe.
ll_cleanup_ares();
LLFilePickerThread::cleanupClass();
SUBSYSTEM_CLEANUP(LLFilePickerThread);
//MUST happen AFTER LLCurl::cleanupClass
//MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl)
delete sTextureCache;
sTextureCache = NULL;
delete sTextureFetch;
@ -2060,17 +2061,17 @@ bool LLAppViewer::cleanup()
LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL;
//Note:
//LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown()
//SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown()
//because some new image might be generated during cleaning up media. --bao
LLViewerMedia::cleanupClass();
LLViewerParcelMedia::cleanupClass();
SUBSYSTEM_CLEANUP(LLViewerMedia);
SUBSYSTEM_CLEANUP(LLViewerParcelMedia);
gTextureList.shutdown(); // shutdown again in case a callback added something
LLUIImageList::getInstance()->cleanUp();
// This should eventually be done in LLAppViewer
LLImage::cleanupClass();
LLVFSThread::cleanupClass();
LLLFSThread::cleanupClass();
SUBSYSTEM_CLEANUP(LLImage);
SUBSYSTEM_CLEANUP(LLVFSThread);
SUBSYSTEM_CLEANUP(LLLFSThread);
#ifndef LL_RELEASE_FOR_DOWNLOAD
LL_INFOS() << "Auditing VFS" << LL_ENDL;
@ -2113,9 +2114,9 @@ bool LLAppViewer::cleanup()
LL_INFOS() << "File launched." << LL_ENDL;
}
LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL;
LLProxy::cleanupClass();
SUBSYSTEM_CLEANUP(LLProxy);
LLWearableType::cleanupClass();
SUBSYSTEM_CLEANUP(LLWearableType);
LLMainLoopRepeater::instance().stop();

View File

@ -334,7 +334,7 @@ void LLAssetUploadResponder::uploadFailure(const LLSD& content)
// deal with L$ errors
if (reason == "insufficient funds")
{
S32 price = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 price = LLGlobalEconomy::getInstance()->getPriceUpload();
LLStringUtil::format_map_t args;
args["AMOUNT"] = llformat("%d", price);
LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("uploading_costs", args), price );
@ -405,7 +405,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)
asset_type == LLAssetType::AT_MESH)
{
expected_upload_cost =
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
LLGlobalEconomy::getInstance()->getPriceUpload();
}
on_new_single_inventory_upload_complete(
@ -467,7 +467,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)
everyone_perms,
display_name,
callback,
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(),
LLGlobalEconomy::getInstance()->getPriceUpload(),
userdata);
}

View File

@ -34,6 +34,7 @@
#include "llinventoryobserver.h"
#include "llinventorymodel.h"
#include "llviewerinventory.h"
#include "llinitdestroyclass.h"
class LLMenuItemCallGL;
class LLToggleableMenu;

View File

@ -993,7 +993,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata)
std::string name = floaterp->getChild<LLUICtrl>("name_form")->getValue().asString();
std::string desc = floaterp->getChild<LLUICtrl>("description_form")->getValue().asString();
LLAssetStorage::LLStoreAssetCallback callback = NULL;
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
void *userdata = NULL;
upload_new_resource(floaterp->mTransactionID, // tid
LLAssetType::AT_ANIMATION,

View File

@ -101,7 +101,7 @@ LLFloaterIMContainer::~LLFloaterIMContainer()
gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());
gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded());
if (!LLSingleton<LLIMMgr>::destroyed())
if (LLIMMgr::instanceExists())
{
LLIMMgr::getInstance()->removeSessionObserver(this);
}

View File

@ -121,7 +121,7 @@ BOOL LLFloaterNameDesc::postBuild()
// Cancel button
getChild<LLUICtrl>("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnCancel, this));
getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() ));
getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload() ));
setDefaultBtn("ok_btn");
@ -160,7 +160,7 @@ void LLFloaterNameDesc::onBtnOK( )
getChildView("ok_btn")->setEnabled(FALSE); // don't allow inadvertent extra uploads
LLAssetStorage::LLStoreAssetCallback callback = NULL;
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass).
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass).
void *nruserdata = NULL;
std::string display_name = LLStringUtil::null;

View File

@ -29,6 +29,7 @@
#include "llpanel.h"
#include "llnotifications.h"
#include "llinitdestroyclass.h"
class LLHints : public LLInitClass<LLHints>

View File

@ -33,6 +33,7 @@
#include "lllogchat.h"
#include "llvoicechannel.h"
#include "llinitdestroyclass.h"
class LLAvatarName;
class LLFriendObserver;

View File

@ -74,6 +74,7 @@
#include "llviewerwindow.h"
#include "llvoavatarself.h"
#include "llwearablelist.h"
#include "llwearableitemslist.h"
#include "lllandmarkactions.h"
#include "llpanellandmarks.h"
@ -557,6 +558,46 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
return TRUE;
}
void disable_context_entries_if_present(LLMenuGL& menu,
const menuentry_vec_t &disabled_entries)
{
const LLView::child_list_t *list = menu.getChildList();
for (LLView::child_list_t::const_iterator itor = list->begin();
itor != list->end();
++itor)
{
LLView *menu_item = (*itor);
std::string name = menu_item->getName();
// descend into split menus:
LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item);
if ((name == "More") && branchp)
{
disable_context_entries_if_present(*branchp->getBranch(), disabled_entries);
}
bool found = false;
menuentry_vec_t::const_iterator itor2;
for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
{
if (*itor2 == name)
{
found = true;
break;
}
}
if (found)
{
menu_item->setVisible(TRUE);
// A bit of a hack so we can remember that some UI element explicitly set this to be visible
// so that some other UI element from multi-select doesn't later set this invisible.
menu_item->pushVisible(TRUE);
menu_item->setEnabled(FALSE);
}
}
}
void hide_context_entries(LLMenuGL& menu,
const menuentry_vec_t &entries_to_show,
const menuentry_vec_t &disabled_entries)
@ -765,6 +806,31 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
hide_context_entries(menu, items, disabled_items);
}
bool get_selection_item_uuids(LLFolderView::selected_items_t& selected_items, uuid_vec_t& ids)
{
uuid_vec_t results;
S32 non_item = 0;
for(LLFolderView::selected_items_t::iterator it = selected_items.begin(); it != selected_items.end(); ++it)
{
LLItemBridge *view_model = dynamic_cast<LLItemBridge *>((*it)->getViewModelItem());
if(view_model && view_model->getUUID().notNull())
{
results.push_back(view_model->getUUID());
}
else
{
non_item++;
}
}
if (non_item == 0)
{
ids = results;
return true;
}
return false;
}
void LLInvFVBridge::addTrashContextMenuOptions(menuentry_vec_t &items,
menuentry_vec_t &disabled_items)
{
@ -1120,7 +1186,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
{
LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
}
new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, (LLWearableType::EType)flags);
new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, LLWearableType::inventoryFlagsToWearableType(flags));
break;
case LLAssetType::AT_CATEGORY:
if (actual_asset_type == LLAssetType::AT_LINK_FOLDER)
@ -3585,7 +3651,7 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if(!model) return;
buildContextMenuOptions(flags, items, disabled_items);
hide_context_entries(menu, items, disabled_items);
hide_context_entries(menu, items, disabled_items);
// Reposition the menu, in case we're adding items to an existing menu.
menu.needsArrange();
@ -5770,7 +5836,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (LLWearableType::getAllowMultiwear(mWearableType))
{
items.push_back(std::string("Wearable Add"));
if (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE)
if (!gAgentWearables.canAddWearable(mWearableType))
{
disabled_items.push_back(std::string("Wearable Add"));
}
@ -6439,4 +6505,22 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge(
return new_listener;
}
LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge()
{
}
void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu)
{
uuid_vec_t ids;
menuentry_vec_t disabled_items;
if (get_selection_item_uuids(selected_items, ids))
{
if (!LLAppearanceMgr::instance().canAddWearables(ids))
{
disabled_items.push_back(std::string("Wearable Add"));
}
}
disable_context_entries_if_present(menu, disabled_items);
}
// EOF

View File

@ -37,6 +37,7 @@
#include "llviewerwearable.h"
#include "lltooldraganddrop.h"
#include "lllandmarklist.h"
#include "llfolderviewitem.h"
class LLInventoryFilter;
class LLInventoryPanel;
@ -689,4 +690,11 @@ void hide_context_entries(LLMenuGL& menu,
const menuentry_vec_t &entries_to_show,
const menuentry_vec_t &disabled_entries);
class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel
{
public:
LLFolderViewGroupedItemBridge();
virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu);
};
#endif // LL_LLINVENTORYBRIDGE_H

View File

@ -45,6 +45,7 @@
// newview includes
#include "llappearancemgr.h"
#include "llappviewer.h"
#include "llavataractions.h"
#include "llclipboard.h"
#include "lldonotdisturbnotificationstorage.h"
#include "llfloaterinventory.h"
@ -1114,16 +1115,35 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
LLFloater::setFloaterHost(multi_propertiesp);
}
std::set<LLFolderViewItem*>::iterator set_iter;
for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter)
{
LLFolderViewItem* folder_item = *set_iter;
if(!folder_item) continue;
LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem();
if(!bridge) continue;
bridge->performAction(model, action);
}
std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs();
uuid_vec_t ids;
std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids));
// Check for actions that get handled in bulk
if (action == "wear")
{
wear_multiple(ids, true);
}
else if (action == "wear_add")
{
wear_multiple(ids, false);
}
else if (action == "take_off" || action == "detach")
{
LLAppearanceMgr::instance().removeItemsFromAvatar(ids);
}
else
{
std::set<LLFolderViewItem*>::iterator set_iter;
for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter)
{
LLFolderViewItem* folder_item = *set_iter;
if(!folder_item) continue;
LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem();
if(!bridge) continue;
bridge->performAction(model, action);
}
}
LLFloater::setFloaterHost(NULL);
if (multi_previewp)

View File

@ -183,6 +183,6 @@ const std::string& LLInventoryIcon::getIconName(LLInventoryType::EIconName idx)
LLInventoryType::EIconName LLInventoryIcon::assignWearableIcon(U32 misc_flag)
{
const LLWearableType::EType wearable_type = LLWearableType::EType(LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK & misc_flag);
const LLWearableType::EType wearable_type = LLWearableType::inventoryFlagsToWearableType(misc_flag);
return LLWearableType::getIconName(wearable_type);
}

View File

@ -146,7 +146,8 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
mShowEmptyMessage(p.show_empty_message),
mViewsInitialized(false),
mInvFVBridgeBuilder(NULL),
mInventoryViewModel(p.name)
mInventoryViewModel(p.name),
mGroupedItemBridge(new LLFolderViewGroupedItemBridge)
{
mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
@ -186,6 +187,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
NULL,
root_id);
p.view_model = &mInventoryViewModel;
p.grouped_item_model = mGroupedItemBridge;
p.use_label_suffix = mParams.use_label_suffix;
p.allow_multiselect = mAllowMultiSelect;
p.show_empty_message = mShowEmptyMessage;

View File

@ -43,6 +43,7 @@ class LLInvFVBridge;
class LLInventoryFolderViewModelBuilder;
class LLInvPanelComplObserver;
class LLFolderViewModelInventory;
class LLFolderViewGroupedItemBridge;
namespace LLInitParam
{
@ -240,6 +241,7 @@ protected:
LLScrollContainer* mScroller;
LLFolderViewModelInventory mInventoryViewModel;
LLPointer<LLFolderViewGroupedItemBridge> mGroupedItemBridge;
Params mParams; // stored copy of parameter block
std::map<LLUUID, LLFolderViewItem*> mItemMap;

View File

@ -545,12 +545,14 @@ void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableTyp
LLAvatarAppearanceDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);
if (reg_texind != LLAvatarAppearanceDefines::TEX_NUM_INDICES)
{
U32 index = gAgentWearables.getWearableIndex(wearable);
gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
gAgentAvatarp->wearableUpdated(type);
/* telling the manager to rebake once update cycle is fully done */
LLLocalBitmapMgr::setNeedsRebake();
U32 index;
if (gAgentWearables.getWearableIndex(wearable,index))
{
gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
gAgentAvatarp->wearableUpdated(type);
/* telling the manager to rebake once update cycle is fully done */
LLLocalBitmapMgr::setNeedsRebake();
}
}
}

View File

@ -47,6 +47,7 @@
#include "pipeline.h"
#include <boost/tokenizer.hpp>
#include <boost/bind.hpp>
#include "lldispatcher.h"
#include "llxfermanager.h"
@ -146,22 +147,6 @@ std::string LLMute::getDisplayType() const
}
}
/* static */
LLMuteList* LLMuteList::getInstance()
{
// Register callbacks at the first time that we find that the message system has been created.
static BOOL registered = FALSE;
if( !registered && gMessageSystem != NULL)
{
registered = TRUE;
// Register our various callbacks
gMessageSystem->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate);
gMessageSystem->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList);
}
return LLSingleton<LLMuteList>::getInstance(); // Call the "base" implementation.
}
//-----------------------------------------------------------------------------
// LLMuteList()
//-----------------------------------------------------------------------------
@ -169,6 +154,18 @@ LLMuteList::LLMuteList() :
mIsLoaded(FALSE)
{
gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
// Register our callbacks. We may be constructed before gMessageSystem, so
// use callWhenReady() to register them as soon as gMessageSystem becomes
// available.
// When using bind(), must be explicit about default arguments such as
// that last NULL.
gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
_PREHASH_MuteListUpdate, processMuteListUpdate,
static_cast<void**>(NULL)));
gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
_PREHASH_UseCachedMuteList, processUseCachedMuteList,
static_cast<void**>(NULL)));
}
//-----------------------------------------------------------------------------

View File

@ -84,11 +84,6 @@ public:
LLMuteList();
~LLMuteList();
// Implemented locally so that we can perform some delayed initialization.
// Callers should be careful to call this one and not LLSingleton<LLMuteList>::getInstance()
// which would circumvent that mechanism. -MG
static LLMuteList* getInstance();
void addObserver(LLMuteListObserver* observer);
void removeObserver(LLMuteListObserver* observer);

View File

@ -29,6 +29,7 @@
#include "llpanel.h"
#include "llbutton.h"
#include "llinitdestroyclass.h"
class LLLocationInputCtrl;
class LLMenuGL;

View File

@ -925,17 +925,17 @@ void LLPanelEditWearable::onCommitSexChange()
if (!isAgentAvatarValid()) return;
LLWearableType::EType type = mWearablePtr->getType();
U32 index = gAgentWearables.getWearableIndex(mWearablePtr);
if( !gAgentWearables.isWearableModifiable(type, index))
U32 index;
if( !gAgentWearables.getWearableIndex(mWearablePtr, index) ||
!gAgentWearables.isWearableModifiable(type, index))
{
return;
return;
}
LLViewerVisualParam* param = static_cast<LLViewerVisualParam*>(gAgentAvatarp->getVisualParam( "male" ));
if( !param )
{
return;
return;
}
bool is_new_sex_male = (gSavedSettings.getU32("AvatarSex") ? SEX_MALE : SEX_FEMALE) == SEX_MALE;
@ -978,10 +978,17 @@ void LLPanelEditWearable::onTexturePickerCommit(const LLUICtrl* ctrl)
}
if (getWearable())
{
U32 index = gAgentWearables.getWearableIndex(getWearable());
gAgentAvatarp->setLocalTexture(entry->mTextureIndex, image, FALSE, index);
LLVisualParamHint::requestHintUpdates();
gAgentAvatarp->wearableUpdated(type);
U32 index;
if (gAgentWearables.getWearableIndex(getWearable(), index))
{
gAgentAvatarp->setLocalTexture(entry->mTextureIndex, image, FALSE, index);
LLVisualParamHint::requestHintUpdates();
gAgentAvatarp->wearableUpdated(type);
}
else
{
LL_WARNS() << "wearable not found in gAgentWearables" << LL_ENDL;
}
}
}
else
@ -1058,7 +1065,12 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)
return;
}
U32 index = gAgentWearables.getWearableIndex(mWearablePtr);
U32 index;
if (!gAgentWearables.getWearableIndex(mWearablePtr, index))
{
LL_WARNS() << "wearable not found" << LL_ENDL;
return;
}
std::string new_name = mNameEditor->getText();
@ -1574,6 +1586,12 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL
LL_INFOS() << "onInvisibilityCommit, self " << this << " checkbox_ctrl " << checkbox_ctrl << LL_ENDL;
U32 index;
if (!gAgentWearables.getWearableIndex(getWearable(),index))
{
LL_WARNS() << "wearable not found" << LL_ENDL;
return;
}
bool new_invis_state = checkbox_ctrl->get();
if (new_invis_state)
{
@ -1581,9 +1599,8 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL
mPreviousAlphaTexture[te] = lto->getID();
LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture( IMG_INVISIBLE );
U32 index = gAgentWearables.getWearableIndex(getWearable());
gAgentAvatarp->setLocalTexture(te, image, FALSE, index);
gAgentAvatarp->wearableUpdated(getWearable()->getType());
gAgentAvatarp->setLocalTexture(te, image, FALSE, index);
gAgentAvatarp->wearableUpdated(getWearable()->getType());
}
else
{
@ -1598,7 +1615,6 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL
LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(prev_id);
if (!image) return;
U32 index = gAgentWearables.getWearableIndex(getWearable());
gAgentAvatarp->setLocalTexture(te, image, FALSE, index);
gAgentAvatarp->wearableUpdated(getWearable()->getType());
}

View File

@ -1278,7 +1278,7 @@ void LLPanelMainInventory::setUploadCostIfNeeded()
LLMenuItemBranchGL* upload_menu = mMenuAdd->findChild<LLMenuItemBranchGL>("upload");
if(upload_menu)
{
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
std::string cost_str;
// getPriceUpload() returns -1 if no data available yet.

View File

@ -78,7 +78,7 @@ BOOL LLPanelSnapshotInventory::postBuild()
// virtual
void LLPanelSnapshotInventory::onOpen(const LLSD& key)
{
getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()));
getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload()));
LLPanelSnapshot::onOpen(key);
}

View File

@ -75,12 +75,12 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions()
mCommitCallbackRegistrar.add("Snapshot.SendToFacebook", boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));
mCommitCallbackRegistrar.add("Snapshot.SendToTwitter", boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));
mCommitCallbackRegistrar.add("Snapshot.SendToFlickr", boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this));
LLGlobalEconomy::Singleton::getInstance()->addObserver(this);
LLGlobalEconomy::getInstance()->addObserver(this);
}
LLPanelSnapshotOptions::~LLPanelSnapshotOptions()
{
LLGlobalEconomy::Singleton::getInstance()->removeObserver(this);
LLGlobalEconomy::getInstance()->removeObserver(this);
}
// virtual
@ -97,7 +97,7 @@ void LLPanelSnapshotOptions::onOpen(const LLSD& key)
void LLPanelSnapshotOptions::updateUploadCost()
{
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
getChild<LLUICtrl>("save_to_inventory_btn")->setLabelArg("[AMOUNT]", llformat("%d", upload_cost));
}

View File

@ -28,6 +28,7 @@
#define LLPANELTOPINFOBAR_H_
#include "llpanel.h"
#include "llinitdestroyclass.h"
class LLButton;
class LLTextBox;

View File

@ -28,6 +28,7 @@
#define LL_LLSEARCHHISTORY_H
#include "llsingleton.h"
#include "llinitdestroyclass.h"
#include "llui.h"
/**

View File

@ -212,7 +212,8 @@ void LLSidepanelAppearance::updateToVisibility(const LLSD &new_visibility)
}
if (is_wearable_edit_visible)
{
if (gAgentWearables.getWearableIndex(wearable_ptr) == LLAgentWearables::MAX_CLOTHING_PER_TYPE)
U32 index;
if (!gAgentWearables.getWearableIndex(wearable_ptr,index))
{
// we're no longer wearing the wearable we were last editing, switch back to outfit editor
showOutfitEditPanel();

View File

@ -1005,7 +1005,7 @@ void LLSnapshotLivePreview::saveTexture()
std::string who_took_it;
LLAgentUI::buildFullname(who_took_it);
LLAssetStorage::LLStoreAssetCallback callback = NULL;
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
void *userdata = NULL;
upload_new_resource(tid, // tid
LLAssetType::AT_TEXTURE,

View File

@ -194,6 +194,7 @@
#include "llevents.h"
#include "llstartuplistener.h"
#include "lltoolbarview.h"
#include "llcleanup.h"
#if LL_WINDOWS
#include "lldxhardware.h"
@ -2826,7 +2827,7 @@ void LLStartUp::initNameCache()
void LLStartUp::cleanupNameCache()
{
LLAvatarNameCache::cleanupClass();
SUBSYSTEM_CLEANUP(LLAvatarNameCache);
delete gCacheName;
gCacheName = NULL;

View File

@ -32,6 +32,7 @@
#include "llscreenchannel.h"
#include "llsyswellitem.h"
#include "lltransientdockablefloater.h"
#include "llinitdestroyclass.h"
class LLAvatarName;
class LLChiclet;

View File

@ -2106,7 +2106,7 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const
{
return LLWearableType::WT_INVALID;
}
return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK);
return LLWearableType::inventoryFlagsToWearableType(getFlags());
}

View File

@ -30,7 +30,7 @@
#include "llinventory.h"
#include "llframetimer.h"
#include "llwearable.h"
#include "llui.h" //for LLDestroyClass
#include "llinitdestroyclass.h" //for LLDestroyClass
#include <boost/signals2.hpp> // boost::signals2::trackable

View File

@ -130,6 +130,7 @@
#include "llpathfindingmanager.h"
#include "llstartup.h"
#include "boost/unordered_map.hpp"
#include "llcleanup.h"
using namespace LLAvatarAppearanceDefines;
@ -8416,7 +8417,7 @@ class LLWorldPostProcess : public view_listener_t
void handle_flush_name_caches()
{
LLAvatarNameCache::cleanupClass();
SUBSYSTEM_CLEANUP(LLAvatarNameCache);
if (gCacheName) gCacheName->clear();
}
@ -8460,7 +8461,7 @@ class LLToggleUIHints : public view_listener_t
void LLUploadCostCalculator::calculateCost()
{
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
// getPriceUpload() returns -1 if no data available yet.
if(upload_cost >= 0)

View File

@ -83,7 +83,7 @@ class LLFileEnableUpload : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload());
bool new_value = gStatusBar && LLGlobalEconomy::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::getInstance()->getPriceUpload());
return new_value;
}
};
@ -433,7 +433,7 @@ class LLFileUploadBulk : public view_listener_t
std::string display_name = LLStringUtil::null;
LLAssetStorage::LLStoreAssetCallback callback = NULL;
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
void *userdata = NULL;
upload_new_resource(

View File

@ -6222,9 +6222,9 @@ void process_frozen_message(LLMessageSystem *msgsystem, void **user_data)
// do some extra stuff once we get our economy data
void process_economy_data(LLMessageSystem *msg, void** /*user_data*/)
{
LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance());
LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::getInstance());
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL;

View File

@ -102,6 +102,7 @@
#include "llmediaentry.h"
#include "llfloaterperms.h"
#include "llvocache.h"
#include "llcleanup.h"
//#define DEBUG_UPDATE_TYPE
@ -527,11 +528,11 @@ void LLViewerObject::initVOClasses()
void LLViewerObject::cleanupVOClasses()
{
LLVOGrass::cleanupClass();
LLVOWater::cleanupClass();
LLVOTree::cleanupClass();
LLVOAvatar::cleanupClass();
LLVOVolume::cleanupClass();
SUBSYSTEM_CLEANUP(LLVOGrass);
SUBSYSTEM_CLEANUP(LLVOWater);
SUBSYSTEM_CLEANUP(LLVOTree);
SUBSYSTEM_CLEANUP(LLVOAvatar);
SUBSYSTEM_CLEANUP(LLVOVolume);
sObjectDataMap.clear();
}

View File

@ -208,6 +208,7 @@
#include "llwindowlistener.h"
#include "llviewerwindowlistener.h"
#include "llpaneltopinfobar.h"
#include "llcleanup.h"
#if LL_WINDOWS
#include <tchar.h> // For Unicode conversion methods
@ -2124,7 +2125,7 @@ void LLViewerWindow::shutdownGL()
// Shutdown GL cleanly. Order is very important here.
//--------------------------------------------------------
LLFontGL::destroyDefaultFonts();
LLFontManager::cleanupClass();
SUBSYSTEM_CLEANUP(LLFontManager);
stop_glerror();
gSky.cleanup();
@ -2147,7 +2148,7 @@ void LLViewerWindow::shutdownGL()
LLWorldMapView::cleanupTextures();
LLViewerTextureManager::cleanup() ;
LLImageGL::cleanupClass() ;
SUBSYSTEM_CLEANUP(LLImageGL) ;
LL_INFOS() << "All textures and llimagegl images are destroyed!" << LL_ENDL ;
@ -2160,7 +2161,7 @@ void LLViewerWindow::shutdownGL()
gGL.shutdown();
LLVertexBuffer::cleanupClass();
SUBSYSTEM_CLEANUP(LLVertexBuffer);
LL_INFOS() << "LLVertexBuffer cleaned." << LL_ENDL ;
}

View File

@ -1570,8 +1570,16 @@ BOOL LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex t
return LLVOAvatar::isTextureVisible(type);
}
U32 index = gAgentWearables.getWearableIndex(wearable);
return isTextureVisible(type,index);
U32 index;
if (gAgentWearables.getWearableIndex(wearable,index))
{
return isTextureVisible(type,index);
}
else
{
LL_WARNS() << "Wearable not found" << LL_ENDL;
return FALSE;
}
}
bool LLVOAvatarSelf::areTexturesCurrent() const

View File

@ -894,13 +894,13 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu
setMenuItemVisible(menu, "wear_wear", n_already_worn == 0 && n_worn == 0 && can_be_worn);
setMenuItemEnabled(menu, "wear_wear", n_already_worn == 0 && n_worn == 0);
setMenuItemVisible(menu, "wear_add", wear_add_visible);
setMenuItemEnabled(menu, "wear_add", canAddWearables(ids));
setMenuItemEnabled(menu, "wear_add", LLAppearanceMgr::instance().canAddWearables(ids));
setMenuItemVisible(menu, "wear_replace", n_worn == 0 && n_already_worn != 0 && can_be_worn);
//visible only when one item selected and this item is worn
setMenuItemVisible(menu, "edit", !standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1);
setMenuItemEnabled(menu, "edit", n_editable == 1 && n_worn == 1 && n_items == 1);
setMenuItemVisible(menu, "create_new", mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1);
setMenuItemEnabled(menu, "create_new", canAddWearables(ids));
setMenuItemEnabled(menu, "create_new", LLAppearanceMgr::instance().canAddWearables(ids));
setMenuItemVisible(menu, "show_original", !standalone);
setMenuItemEnabled(menu, "show_original", n_items == 1 && n_links == n_items);
setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && n_worn == n_items);
@ -1004,65 +1004,4 @@ void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id)
LLAgentWearables::createWearable(item->getWearableType(), true);
}
// Returns true if all the given objects and clothes can be added.
// static
bool LLWearableItemsList::ContextMenu::canAddWearables(const uuid_vec_t& item_ids)
{
// TODO: investigate wearables may not be loaded at this point EXT-8231
U32 n_objects = 0;
boost::unordered_map<LLWearableType::EType, U32> clothes_by_type;
// Count given clothes (by wearable type) and objects.
for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
{
LLViewerInventoryItem* item = gInventory.getItem(*it);
if (!item)
{
return false;
}
if (item->getType() == LLAssetType::AT_OBJECT)
{
++n_objects;
}
else if (item->getType() == LLAssetType::AT_CLOTHING)
{
++clothes_by_type[item->getWearableType()];
}
else
{
LL_WARNS() << "Unexpected wearable type" << LL_ENDL;
return false;
}
}
// Check whether we can add all the objects.
if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects))
{
return false;
}
// Check whether we can add all the clothes.
boost::unordered_map<LLWearableType::EType, U32>::const_iterator m_it;
for (m_it = clothes_by_type.begin(); m_it != clothes_by_type.end(); ++m_it)
{
LLWearableType::EType w_type = m_it->first;
U32 n_clothes = m_it->second;
U32 wearable_count = gAgentWearables.getWearableCount(w_type);
if ((wearable_count > 0) && !LLWearableType::getAllowMultiwear(w_type))
{
return false;
}
if ((wearable_count + n_clothes) > LLAgentWearables::MAX_CLOTHING_PER_TYPE)
{
return false;
}
}
return true;
}
// EOF

View File

@ -429,7 +429,6 @@ public:
static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val);
static void updateMask(U32& mask, LLAssetType::EType at);
static void createNewWearable(const LLUUID& item_id);
static bool canAddWearables(const uuid_vec_t& item_ids);
LLWearableItemsList* mParent;
};

View File

@ -115,6 +115,7 @@
#include "llpathfindingpathtool.h"
#include "llscenemonitor.h"
#include "llprogressview.h"
#include "llcleanup.h"
#ifdef _DEBUG
// Debug indices is disabled for now for debug performance - djs 4/24/02
@ -7373,7 +7374,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)
}
LLVOPartGroup::destroyGL();
LLVertexBuffer::cleanupClass();
SUBSYSTEM_CLEANUP(LLVertexBuffer);
//delete all name pool caches
LLGLNamePool::cleanupPools();

View File

@ -33,6 +33,7 @@
#include "../llagent.h"
#include "message.h"
#include "llurlentry.h"
#include "llpounceable.h"
namespace {
const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111");
@ -61,7 +62,7 @@ void LLMessageSystem::addUUID(char const *,LLUUID const &) { }
void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { }
void LLMessageSystem::nextBlockFast(char const *) { }
void LLMessageSystem::newMessage(char const *) { }
LLMessageSystem * gMessageSystem;
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
char const* const _PREHASH_AgentID = 0; // never dereferenced during this test
char const* const _PREHASH_AgentData = 0; // never dereferenced during this test
LLAgent gAgent;

View File

@ -103,7 +103,7 @@ namespace tut
~LLMessageSystemTestData()
{
// not end_messaging_system()
delete gMessageSystem;
delete static_cast<LLMessageSystem*>(gMessageSystem);
gMessageSystem = NULL;
// rm contents of temp dir