diff options
Diffstat (limited to 'indra')
255 files changed, 13264 insertions, 12897 deletions
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 5e566d6c7c..5ae2df3994 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -95,6 +95,7 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true)); addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false)); addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false)); + addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE)); }; diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index d538accbf7..69b01731e5 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -112,6 +112,9 @@ public: AT_WIDGET = 40, // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...) + AT_PERSON = 45, + // A user uuid which is not an inventory asset type, used in viewer only for adding a person to a chat via drag and drop. + AT_MESH = 49, // Mesh data in our proprietary SLM format diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp index db72aa19b9..bb160b3c0b 100644 --- a/indra/llcommon/llinitparam.cpp +++ b/indra/llcommon/llinitparam.cpp @@ -40,7 +40,9 @@ namespace LLInitParam { const U8* my_addr = reinterpret_cast<const U8*>(this); const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); - mEnclosingBlockOffset = 0x7FFFffff & (U32)(my_addr - block_addr); + U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr); + mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff; + mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16; } // @@ -112,6 +114,35 @@ namespace LLInitParam std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams)); } + void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name) + { + // create a copy of the param descriptor in mAllParams + // so other data structures can store a pointer to it + mAllParams.push_back(in_param); + ParamDescriptorPtr param(mAllParams.back()); + + std::string name(char_name); + if ((size_t)param->mParamHandle > mMaxParamOffset) + { + llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl; + } + + if (name.empty()) + { + mUnnamedParams.push_back(param); + } + else + { + // don't use insert, since we want to overwrite existing entries + mNamedParams[name] = param; + } + + if (param->mValidationFunc) + { + mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); + } + } + BlockDescriptor::BlockDescriptor() : mMaxParamOffset(0), mInitializationState(UNINITIALIZED), @@ -196,12 +227,7 @@ namespace LLInitParam if (serialize_func) { const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; - // each param descriptor remembers its serial number - // so we can inspect the same param under different names - // and see that it has the same number - name_stack.push_back(std::make_pair("", true)); serialize_func(*param, parser, name_stack, diff_param); - name_stack.pop_back(); } } @@ -358,36 +384,6 @@ namespace LLInitParam return false; } - //static - void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name) - { - // create a copy of the param descriptor in mAllParams - // so other data structures can store a pointer to it - block_data.mAllParams.push_back(in_param); - ParamDescriptorPtr param(block_data.mAllParams.back()); - - std::string name(char_name); - if ((size_t)param->mParamHandle > block_data.mMaxParamOffset) - { - llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl; - } - - if (name.empty()) - { - block_data.mUnnamedParams.push_back(param); - } - else - { - // don't use insert, since we want to overwrite existing entries - block_data.mNamedParams[name] = param; - } - - if (param->mValidationFunc) - { - block_data.mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); - } - } - void BaseBlock::addSynonym(Param& param, const std::string& synonym) { BlockDescriptor& block_data = mostDerivedBlockDescriptor(); @@ -460,7 +456,7 @@ namespace LLInitParam if (merge_func) { Param* paramp = getParamFromHandle((*it)->mParamHandle); - llassert(paramp->mEnclosingBlockOffset == (*it)->mParamHandle); + llassert(paramp->getEnclosingBlockOffset() == (*it)->mParamHandle); some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); } } diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index 99983a19cb..545f53ffe6 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -37,6 +37,71 @@ #include "llerror.h" #include "lltypeinfolookup.h" +namespace LLTypeTags +{ + template <typename INNER_TYPE, int _SORT_ORDER> + struct TypeTagBase + { + typedef void is_tag_t; + typedef INNER_TYPE inner_t; + static const int SORT_ORDER=_SORT_ORDER; + }; + + template <int VAL1, int VAL2> + struct GreaterThan + { + static const bool value = VAL1 > VAL2; + }; + + template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value > + struct Swap + { + typedef typename ITEM::template Cons<REST>::value_t value_t; + }; + + template<typename ITEM, typename REST> + struct Swap<ITEM, REST, true> + { + typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t; + }; + + template<typename T, typename SORTABLE = void> + struct IsSortable + { + static const bool value = false; + }; + + template<typename T> + struct IsSortable<T, typename T::is_tag_t> + { + static const bool value = true; + }; + + template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value> + struct InsertInto + { + typedef typename ITEM::template Cons<REST>::value_t value_t; + }; + + template<typename ITEM, typename REST> + struct InsertInto <ITEM, REST, true> + { + typedef typename Swap<ITEM, REST>::value_t value_t; + }; + + template<typename T, bool SORTABLE = IsSortable<T>::value> + struct Sorted + { + typedef T value_t; + }; + + template<typename T> + struct Sorted <T, true> + { + typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t; + }; +} + namespace LLInitParam { // used to indicate no matching value to a given name when parsing @@ -44,6 +109,8 @@ namespace LLInitParam template<typename T> const T& defaultValue() { static T value; return value; } + // wraps comparison operator between any 2 values of the same type + // specialize to handle cases where equality isn't defined well, or at all template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value > struct ParamCompare { @@ -78,24 +145,123 @@ namespace LLInitParam // helper functions and classes typedef ptrdiff_t param_handle_t; + struct IS_A_BLOCK {}; + struct NOT_BLOCK {}; + + // these templates allow us to distinguish between template parameters + // that derive from BaseBlock and those that don't + template<typename T, typename BLOCK_IDENTIFIER = void> + struct IsBlock + { + typedef NOT_BLOCK value_t; + }; + + template<typename T> + struct IsBlock<T, typename T::baseblock_base_class_t> + { + typedef IS_A_BLOCK value_t; + }; + + // ParamValue class directly manages the wrapped value + // by holding on to a copy (scalar params) + // or deriving from it (blocks) + // has specializations for custom value behavior + // and "tag" values like Lazy and Atomic + template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t> + class ParamValue + { + typedef ParamValue<T, VALUE_IS_BLOCK> self_t; + + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue(): mValue() {} + ParamValue(const default_value_t& other) : mValue(other) {} + + void setValue(const value_t& val) + { + mValue = val; + } + + const value_t& getValue() const + { + return mValue; + } + + T& getValue() + { + return mValue; + } + + protected: + T mValue; + }; + + template<typename T> + class ParamValue<T, IS_A_BLOCK> + : public T + { + typedef ParamValue<T, IS_A_BLOCK> self_t; + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue() + : T(), + mValidated(false) + {} + + ParamValue(const default_value_t& other) + : T(other), + mValidated(false) + {} + + void setValue(const value_t& val) + { + *this = val; + } + + const value_t& getValue() const + { + return *this; + } + + T& getValue() + { + return *this; + } + + protected: + mutable bool mValidated; // lazy validation flag + }; + // empty default implementation of key cache // leverages empty base class optimization template <typename T> class TypeValues + : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> { private: struct Inaccessable{}; public: typedef std::map<std::string, T> value_name_map_t; typedef Inaccessable name_t; + typedef TypeValues<T> type_value_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValues(const typename param_value_t::value_t& val) + : param_value_t(val) + {} void setValueName(const std::string& key) {} std::string getValueName() const { return ""; } - std::string calcValueName(const T& value) const { return ""; } + std::string calcValueName(const value_t& value) const { return ""; } void clearValueName() const {} - static bool getValueFromName(const std::string& name, T& value) + static bool getValueFromName(const std::string& name, value_t& value) { return false; } @@ -110,15 +276,39 @@ namespace LLInitParam return NULL; } + void assignNamedValue(const Inaccessable& name) + {} + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + static value_name_map_t* getValueNames() {return NULL;} }; - template <typename T, typename DERIVED_TYPE = TypeValues<T> > + // helper class to implement name value lookups + // and caching of last used name + template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true > class TypeValuesHelper + : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> { + typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t; public: typedef typename std::map<std::string, T> value_name_map_t; typedef std::string name_t; + typedef self_t type_value_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValuesHelper(const typename param_value_t::value_t& val) + : param_value_t(val) + {} //TODO: cache key by index to save on param block size void setValueName(const std::string& value_name) @@ -131,7 +321,7 @@ namespace LLInitParam return mValueName; } - std::string calcValueName(const T& value) const + std::string calcValueName(const value_t& value) const { value_name_map_t* map = getValueNames(); for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end(); @@ -152,7 +342,7 @@ namespace LLInitParam mValueName.clear(); } - static bool getValueFromName(const std::string& name, T& value) + static bool getValueFromName(const std::string& name, value_t& value) { value_name_map_t* map = getValueNames(); typename value_name_map_t::iterator found_it = map->find(name); @@ -194,18 +384,90 @@ namespace LLInitParam return &sValues; } - static void declare(const std::string& name, const T& value) + static void declare(const std::string& name, const value_t& value) { (*getValueNames())[name] = value; } + void operator ()(const std::string& name) + { + *this = name; + } + + void assignNamedValue(const std::string& name) + { + if (getValueFromName(name, param_value_t::getValue())) + { + setValueName(name); + } + } + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + protected: - static void getName(const std::string& name, const T& value) + static void getName(const std::string& name, const value_t& value) {} mutable std::string mValueName; }; + // string types can support custom named values, but need + // to disambiguate in code between a string that is a named value + // and a string that is a name + template <typename DERIVED_TYPE> + class TypeValuesHelper<std::string, DERIVED_TYPE, true> + : public TypeValuesHelper<std::string, DERIVED_TYPE, false> + { + public: + typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t; + typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t; + typedef std::string value_t; + typedef std::string name_t; + typedef self_t type_value_t; + + TypeValuesHelper(const std::string& val) + : TypeValuesHelper(val) + {} + + void operator ()(const std::string& name) + { + *this = name; + } + + self_t& operator =(const std::string& name) + { + if (base_t::getValueFromName(name, ParamValue<std::string>::getValue())) + { + base_t::setValueName(name); + } + else + { + ParamValue<std::string>::setValue(name); + } + return *this; + } + + operator const value_t&() const + { + return ParamValue<std::string>::getValue(); + } + + const value_t& operator()() const + { + return ParamValue<std::string>::getValue(); + } + + }; + + // parser base class with mechanisms for registering readers/writers/inspectors of different types class LL_COMMON_API Parser { LOG_CLASS(Parser); @@ -355,6 +617,7 @@ namespace LLInitParam } EInitializationState; void aggregateBlockData(BlockDescriptor& src_block_data); + void addParam(ParamDescriptorPtr param, const char* name); typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t; typedef std::vector<ParamDescriptorPtr> param_list_t; @@ -370,48 +633,58 @@ namespace LLInitParam class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed }; - class LL_COMMON_API BaseBlock - { - public: //TODO: implement in terms of owned_ptr template<typename T> - class Lazy + class LazyValue { public: - Lazy() + LazyValue() : mPtr(NULL) {} - ~Lazy() + ~LazyValue() { delete mPtr; } - Lazy(const Lazy& other) + LazyValue(const T& value) { - if (other.mPtr) + mPtr = new T(value); + } + + LazyValue(const LazyValue& other) + : mPtr(NULL) { - mPtr = new T(*other.mPtr); + *this = other; } - else + + LazyValue& operator = (const LazyValue& other) + { + if (!other.mPtr) { + delete mPtr; mPtr = NULL; } - } - - Lazy<T>& operator = (const Lazy<T>& other) + else { - if (other.mPtr) + if (!mPtr) { mPtr = new T(*other.mPtr); } else { - mPtr = NULL; + *mPtr = *(other.mPtr); + } } return *this; } + bool operator==(const LazyValue& other) const + { + if (empty() || other.empty()) return false; + return *mPtr == *other.mPtr; + } + bool empty() const { return mPtr == NULL; @@ -419,18 +692,29 @@ namespace LLInitParam void set(const T& other) { - delete mPtr; + if (!mPtr) + { mPtr = new T(other); } + else + { + *mPtr = other; + } + } const T& get() const { - return ensureInstance(); + return *ensureInstance(); } T& get() { - return ensureInstance(); + return *ensureInstance(); + } + + operator const T&() const + { + return get(); } private: @@ -445,13 +729,50 @@ namespace LLInitParam } private: - // if you get a compilation error with this, that means you are using a forward declared struct for T - // unfortunately, the type traits we rely on don't work with forward declared typed - //static const int dummy = sizeof(T); mutable T* mPtr; }; + // root class of all parameter blocks + + class LL_COMMON_API BaseBlock + { + public: + // lift block tags into baseblock namespace so derived classes do not need to qualify them + typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK; + typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK; + + template<typename T> + struct Sequential : public LLTypeTags::TypeTagBase<T, 2> + { + template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; }; + template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; }; + }; + + template<typename T> + struct Atomic : public LLTypeTags::TypeTagBase<T, 1> + { + template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; }; + template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; }; + }; + + template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t > + struct Lazy : public LLTypeTags::TypeTagBase<T, 0> + { + template <typename S> struct Cons + { + typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t; + }; + template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> > + { + typedef Lazy<S, IS_A_BLOCK> value_t; + }; + template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> > + { + typedef Lazy<S, BLOCK_T> value_t; + }; + }; + // "Multiple" constraint types, put here in root class to avoid ambiguity during use struct AnyAmount { @@ -521,8 +842,8 @@ namespace LLInitParam void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const; - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } // take all provided params from other and apply to self bool overwriteFrom(const BaseBlock& other) @@ -536,10 +857,17 @@ namespace LLInitParam return false; } - static void addParam(BlockDescriptor& block_data, ParamDescriptorPtr param, const char* name); - ParamDescriptorPtr findParamDescriptor(const Param& param); + // take all provided params from other and apply to self + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); + + static BlockDescriptor& getBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + protected: void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); @@ -548,25 +876,11 @@ namespace LLInitParam { return mergeBlock(block_data, source, overwrite); } - // take all provided params from other and apply to self - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); - - static BlockDescriptor& selfBlockDescriptor() - { - static BlockDescriptor sBlockDescriptor; - return sBlockDescriptor; - } private: const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; }; - template<typename T> - struct ParamCompare<BaseBlock::Lazy<T>, false > - { - static bool equals(const BaseBlock::Lazy<T>& a, const BaseBlock::Lazy<T>& b) { return !a.empty() || !b.empty(); } - }; - class LL_COMMON_API Param { public: @@ -595,251 +909,63 @@ namespace LLInitParam // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class return *const_cast<BaseBlock*> (reinterpret_cast<const BaseBlock*> - (my_addr - (ptrdiff_t)(S32)mEnclosingBlockOffset)); + (my_addr - (ptrdiff_t)getEnclosingBlockOffset())); } - private: - friend class BaseBlock; - - U32 mEnclosingBlockOffset:31; - U32 mIsProvided:1; - - }; - - // these templates allow us to distinguish between template parameters - // that derive from BaseBlock and those that don't - template<typename T, typename Void = void> - struct IsBlock - { - static const bool value = false; - struct EmptyBase {}; - typedef EmptyBase base_class_t; - }; - - template<typename T> - struct IsBlock<T, typename T::baseblock_base_class_t> + U32 getEnclosingBlockOffset() const { - static const bool value = true; - typedef BaseBlock base_class_t; - }; - - template<typename T> - struct IsBlock<BaseBlock::Lazy<T>, typename T::baseblock_base_class_t > - { - static const bool value = true; - typedef BaseBlock base_class_t; - }; - - template<typename T, typename NAME_VALUE_LOOKUP, bool VALUE_IS_BLOCK = IsBlock<T>::value> - class ParamValue : public NAME_VALUE_LOOKUP - { - public: - typedef const T& value_assignment_t; - typedef T value_t; - typedef ParamValue<T, NAME_VALUE_LOOKUP, VALUE_IS_BLOCK> self_t; - - ParamValue(): mValue() {} - ParamValue(value_assignment_t other) : mValue(other) {} - - void setValue(value_assignment_t val) - { - mValue = val; - } - - value_assignment_t getValue() const - { - return mValue; - } - - T& getValue() - { - return mValue; - } - - operator value_assignment_t() const - { - return mValue; - } - - value_assignment_t operator()() const - { - return mValue; - } - - void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) - { - *this = name; - } - - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) - { - if (NAME_VALUE_LOOKUP::getValueFromName(name, mValue)) - { - setValueName(name); - } - - return *this; - } - - protected: - T mValue; - }; - - template<typename T, typename NAME_VALUE_LOOKUP> - class ParamValue<T, NAME_VALUE_LOOKUP, true> - : public T, - public NAME_VALUE_LOOKUP - { - public: - typedef const T& value_assignment_t; - typedef T value_t; - typedef ParamValue<T, NAME_VALUE_LOOKUP, true> self_t; - - ParamValue() - : T(), - mValidated(false) - {} - - ParamValue(value_assignment_t other) - : T(other), - mValidated(false) - {} - - void setValue(value_assignment_t val) - { - *this = val; - } - - value_assignment_t getValue() const - { - return *this; + return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow; } - T& getValue() - { - return *this; - } - - operator value_assignment_t() const - { - return *this; - } - - value_assignment_t operator()() const - { - return *this; - } - - void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) - { - *this = name; - } - - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) - { - if (NAME_VALUE_LOOKUP::getValueFromName(name, *this)) - { - setValueName(name); - } - - return *this; - } - - protected: - mutable bool mValidated; // lazy validation flag - }; - - template<typename NAME_VALUE_LOOKUP> - class ParamValue<std::string, NAME_VALUE_LOOKUP, false> - : public NAME_VALUE_LOOKUP - { - public: - typedef const std::string& value_assignment_t; - typedef std::string value_t; - typedef ParamValue<std::string, NAME_VALUE_LOOKUP, false> self_t; - - ParamValue(): mValue() {} - ParamValue(value_assignment_t other) : mValue(other) {} - - void setValue(value_assignment_t val) - { - if (NAME_VALUE_LOOKUP::getValueFromName(val, mValue)) - { - NAME_VALUE_LOOKUP::setValueName(val); - } - else - { - mValue = val; - } - } - - value_assignment_t getValue() const - { - return mValue; - } + private: + friend class BaseBlock; - std::string& getValue() - { - return mValue; - } + //24 bits for member offset field and 1 bit for provided flag + U16 mEnclosingBlockOffsetLow; + U8 mEnclosingBlockOffsetHigh:7; + U8 mIsProvided:1; - operator value_assignment_t() const - { - return mValue; - } - - value_assignment_t operator()() const - { - return mValue; - } - - protected: - std::string mValue; }; - template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > struct ParamIterator { - typedef typename std::vector<ParamValue<T, NAME_VALUE_LOOKUP> >::const_iterator const_iterator; - typedef typename std::vector<ParamValue<T, NAME_VALUE_LOOKUP> >::iterator iterator; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator const_iterator; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator iterator; }; - // specialize for custom parsing/decomposition of specific classes - // e.g. TypedParam<LLRect> has left, top, right, bottom, etc... + // wrapper for parameter with a known type + // specialized to handle 4 cases: + // simple "scalar" value + // parameter that is itself a block + // multiple scalar values, stored in a vector + // multiple blocks, stored in a vector template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T>, bool HAS_MULTIPLE_VALUES = false, - bool VALUE_IS_BLOCK = IsBlock<ParamValue<T, NAME_VALUE_LOOKUP> >::value> + typename VALUE_IS_BLOCK = typename IsBlock<ParamValue<typename LLTypeTags::Sorted<T>::value_t> >::value_t> class TypedParam : public Param, - public ParamValue<T, NAME_VALUE_LOOKUP> + public NAME_VALUE_LOOKUP::type_value_t { + protected: + typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; public: - typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t; - typedef ParamValue<T, NAME_VALUE_LOOKUP> param_value_t; - typedef typename param_value_t::value_assignment_t value_assignment_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; + typedef typename param_value_t::value_t value_t; - using param_value_t::operator(); + using named_value_t::operator(); - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + named_value_t(value) { if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } - - setValue(value); } bool isProvided() const { return Param::anyProvided(); } @@ -858,14 +984,15 @@ namespace LLInitParam } // try to parse a known named value - if(name_value_lookup_t::valueNamesExist()) + if(named_value_t::valueNamesExist()) { // try to parse a known named value std::string name; if (parser.readValue(name)) { // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + + if (named_value_t::getValueFromName(name, typed_param.getValue())) { typed_param.setValueName(name); typed_param.setProvided(); @@ -905,7 +1032,9 @@ namespace LLInitParam if (!parser.writeValue(typed_param.getValue(), name_stack)) { std::string calculated_key = typed_param.calcValueName(typed_param.getValue()); - if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key)) + if (calculated_key.size() + && (!diff_param + || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key))) { parser.writeValue(calculated_key, name_stack); } @@ -918,22 +1047,23 @@ namespace LLInitParam // tell parser about our actual type parser.inspectValue<T>(name_stack, min_count, max_count, NULL); // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) - if (name_value_lookup_t::getPossibleValues()) + if (named_value_t::getPossibleValues()) { - parser.inspectValue<std::string>(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } } - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const value_t& val, bool flag_as_provided = true) { - param_value_t::clearValueName(); + named_value_t::clearValueName(); setValue(val); setProvided(flag_as_provided); } - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + self_t& operator =(const typename named_value_t::name_t& name) { - return static_cast<self_t&>(param_value_t::operator =(name)); + named_value_t::assignNamedValue(name); + return *this; } protected: @@ -958,37 +1088,43 @@ namespace LLInitParam } return false; } + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; // parameter that is a block template <typename T, typename NAME_VALUE_LOOKUP> - class TypedParam<T, NAME_VALUE_LOOKUP, false, true> + class TypedParam<T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> : public Param, - public ParamValue<T, NAME_VALUE_LOOKUP> + public NAME_VALUE_LOOKUP::type_value_t { + protected: + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; public: - typedef ParamValue<T, NAME_VALUE_LOOKUP> param_value_t; - typedef typename param_value_t::value_assignment_t value_assignment_t; - typedef TypedParam<T, NAME_VALUE_LOOKUP, false, true> self_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; + using named_value_t::operator(); + typedef typename param_value_t::value_t value_t; - using param_value_t::operator(); - - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr), - param_value_t(value) + named_value_t(value) { if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } } @@ -1003,14 +1139,14 @@ namespace LLInitParam return true; } - if(name_value_lookup_t::valueNamesExist()) + if(named_value_t::valueNamesExist()) { // try to parse a known named value std::string name; if (parser.readValue(name)) { // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + if (named_value_t::getValueFromName(name, typed_param.getValue())) { typed_param.setValueName(name); typed_param.setProvided(); @@ -1035,9 +1171,9 @@ namespace LLInitParam std::string key = typed_param.getValueName(); if (!key.empty()) { - if (!parser.writeValue(key, name_stack)) + if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key)) { - return; + parser.writeValue(key, name_stack); } } else @@ -1048,8 +1184,16 @@ namespace LLInitParam static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { - // I am a param that is also a block, so just recurse into my contents const self_t& typed_param = static_cast<const self_t&>(param); + + // tell parser about our actual type + parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + typed_param.inspectBlock(parser, name_stack, min_count, max_count); } @@ -1067,32 +1211,34 @@ namespace LLInitParam } // assign block contents to this param-that-is-a-block - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const value_t& val, bool flag_as_provided = true) { setValue(val); - param_value_t::clearValueName(); + named_value_t::clearValueName(); // force revalidation of block // next call to isProvided() will update provision status based on validity param_value_t::mValidated = false; setProvided(flag_as_provided); } - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + self_t& operator =(const typename named_value_t::name_t& name) { - return static_cast<self_t&>(param_value_t::operator =(name)); + named_value_t::assignNamedValue(name); + return *this; } // propagate changed status up to enclosing block /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) { param_value_t::paramChanged(changed_param, user_provided); + if (user_provided) { // a child param has been explicitly changed // so *some* aspect of this block is now provided param_value_t::mValidated = false; setProvided(); - param_value_t::clearValueName(); + named_value_t::clearValueName(); } else { @@ -1116,7 +1262,7 @@ namespace LLInitParam if (src_typed_param.anyProvided()) { - if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::selfBlockDescriptor(), src_typed_param, overwrite)) + if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite)) { dst_typed_param.clearValueName(); dst_typed_param.setProvided(true); @@ -1125,38 +1271,46 @@ namespace LLInitParam } return false; } + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; // container of non-block parameters template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP> - class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, false> + class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK> : public Param { + protected: + typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<VALUE_TYPE>::value_t> param_value_t; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; + typedef container_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + public: - typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, false> self_t; - typedef ParamValue<VALUE_TYPE, NAME_VALUE_LOOKUP> param_value_t; - typedef typename std::vector<param_value_t> container_t; - typedef const container_t& value_assignment_t; - typedef typename param_value_t::value_t value_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr) { std::copy(value.begin(), value.end(), std::back_inserter(mValues)); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); + } } @@ -1164,10 +1318,18 @@ namespace LLInitParam static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); self_t& typed_param = static_cast<self_t&>(param); value_t value; + + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + ++new_name_stack_range.first; + } + // no further names in stack, attempt to parse value now - if (name_stack_range.first == name_stack_range.second) + if (new_name_stack_range.first == new_name_stack_range.second) { // attempt to read value directly if (parser.readValue(value)) @@ -1177,14 +1339,14 @@ namespace LLInitParam } // try to parse a known named value - if(name_value_lookup_t::valueNamesExist()) + if(named_value_t::valueNamesExist()) { // try to parse a known named value std::string name; if (parser.readValue(name)) { // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) + if (named_value_t::getValueFromName(name, value)) { typed_param.add(value); typed_param.mValues.back().setValueName(name); @@ -1200,14 +1362,14 @@ namespace LLInitParam static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) { const self_t& typed_param = static_cast<const self_t&>(param); - if (!typed_param.isProvided() || name_stack.empty()) return; + if (!typed_param.isProvided()) return; for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); it != end_it; ++it) { std::string key = it->getValueName(); - name_stack.back().second = true; + name_stack.push_back(std::make_pair(std::string(), true)); if(key.empty()) // not parsed via name values, write out value directly @@ -1229,19 +1391,21 @@ namespace LLInitParam break; } } + + name_stack.pop_back(); } } static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL); - if (name_value_lookup_t::getPossibleValues()) + if (named_value_t::getPossibleValues()) { - parser.inspectValue<std::string>(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } } - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const container_t& val, bool flag_as_provided = true) { mValues = val; setProvided(flag_as_provided); @@ -1249,26 +1413,24 @@ namespace LLInitParam param_value_t& add() { - mValues.push_back(param_value_t(value_t())); + mValues.push_back(value_t()); Param::setProvided(); return mValues.back(); } self_t& add(const value_t& item) { - param_value_t param_value; - param_value.setValue(item); - mValues.push_back(param_value); + mValues.push_back(item); setProvided(); return *this; } - self_t& add(const typename name_value_lookup_t::name_t& name) + self_t& add(const typename named_value_t::name_t& name) { value_t value; // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) + if (named_value_t::getValueFromName(name, value)) { add(value); mValues.back().setValueName(name); @@ -1278,9 +1440,9 @@ namespace LLInitParam } // implicit conversion - operator value_assignment_t() const { return mValues; } + operator const container_t&() const { return mValues; } // explicit conversion - value_assignment_t operator()() const { return mValues; } + const container_t& operator()() const { return mValues; } typedef typename container_t::iterator iterator; typedef typename container_t::const_iterator const_iterator; @@ -1321,37 +1483,46 @@ namespace LLInitParam } container_t mValues; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; // container of block parameters template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP> - class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, true> + class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> : public Param { + protected: + typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<VALUE_TYPE>::value_t> param_value_t; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + typedef container_t default_value_t; + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; public: - typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, true> self_t; - typedef ParamValue<VALUE_TYPE, NAME_VALUE_LOOKUP> param_value_t; - typedef typename std::vector<param_value_t> container_t; - typedef const container_t& value_assignment_t; typedef typename param_value_t::value_t value_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr) { std::copy(value.begin(), value.end(), back_inserter(mValues)); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } } @@ -1359,9 +1530,16 @@ namespace LLInitParam static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); self_t& typed_param = static_cast<self_t&>(param); bool new_value = false; + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + new_value |= new_name_stack_range.first->second; + ++new_name_stack_range.first; + } if (new_name || typed_param.mValues.empty()) { new_value = true; @@ -1371,19 +1549,19 @@ namespace LLInitParam param_value_t& value = typed_param.mValues.back(); // attempt to parse block... - if(value.deserializeBlock(parser, name_stack_range, new_name)) + if(value.deserializeBlock(parser, new_name_stack_range, new_name)) { typed_param.setProvided(); return true; } - else if(name_value_lookup_t::valueNamesExist()) + else if(named_value_t::valueNamesExist()) { // try to parse a known named value std::string name; if (parser.readValue(name)) { // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value.getValue())) + if (named_value_t::getValueFromName(name, value.getValue())) { typed_param.mValues.back().setValueName(name); typed_param.setProvided(); @@ -1404,13 +1582,13 @@ namespace LLInitParam static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) { const self_t& typed_param = static_cast<const self_t&>(param); - if (!typed_param.isProvided() || name_stack.empty()) return; + if (!typed_param.isProvided()) return; for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); it != end_it; ++it) { - name_stack.back().second = true; + name_stack.push_back(std::make_pair(std::string(), true)); std::string key = it->getValueName(); if (!key.empty()) @@ -1423,16 +1601,27 @@ namespace LLInitParam { it->serializeBlock(parser, name_stack, NULL); } + + name_stack.pop_back(); } } static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { - // I am a vector of blocks, so describe my contents recursively - param_value_t(value_t()).inspectBlock(parser, name_stack, min_count, max_count); + const param_value_t& value_param = param_value_t(value_t()); + + // tell parser about our actual type + parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } - void set(value_assignment_t val, bool flag_as_provided = true) + value_param.inspectBlock(parser, name_stack, min_count, max_count); + } + + void set(const container_t& val, bool flag_as_provided = true) { mValues = val; setProvided(flag_as_provided); @@ -1452,12 +1641,12 @@ namespace LLInitParam return *this; } - self_t& add(const typename name_value_lookup_t::name_t& name) + self_t& add(const typename named_value_t::name_t& name) { value_t value; // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) + if (named_value_t::getValueFromName(name, value)) { add(value); mValues.back().setValueName(name); @@ -1466,12 +1655,10 @@ namespace LLInitParam } // implicit conversion - operator value_assignment_t() const { return mValues; } + operator const container_t&() const { return mValues; } // explicit conversion - value_assignment_t operator()() const { return mValues; } + const container_t& operator()() const { return mValues; } - typedef typename container_t::iterator iterator; - typedef typename container_t::const_iterator const_iterator; iterator begin() { return mValues.begin(); } iterator end() { return mValues.end(); } const_iterator begin() const { return mValues.begin(); } @@ -1518,6 +1705,20 @@ namespace LLInitParam } container_t mValues; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> @@ -1532,13 +1733,13 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(selfBlockDescriptor(), other, true); + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(selfBlockDescriptor(), other, false); + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); } bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) @@ -1556,7 +1757,7 @@ namespace LLInitParam bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite) { mCurChoice = other.mCurChoice; - return base_block_t::mergeBlock(selfBlockDescriptor(), other, overwrite); + return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite); } // clear out old choice when param has changed @@ -1577,38 +1778,38 @@ namespace LLInitParam base_block_t::paramChanged(changed_param, user_provided); } - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } protected: ChoiceBlock() : mCurChoice(0) { - BaseBlock::init(selfBlockDescriptor(), base_block_t::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // Alternatives are mutually exclusive wrt other Alternatives in the same block. // One alternative in a block will always have isChosen() == true. // At most one alternative in a block will have isProvided() == true. - template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false> { + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: friend class ChoiceBlock<DERIVED_BLOCK>; - typedef Alternative<T, NAME_VALUE_LOOKUP> self_t; - typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBlock<ParamValue<T, NAME_VALUE_LOOKUP> >::value> super_t; - typedef typename super_t::value_assignment_t value_assignment_t; - using super_t::operator =; - explicit Alternative(const char* name = "", value_assignment_t val = defaultValue<T>()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1), + explicit Alternative(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1), mOriginalValue(val) { // assign initial choice to first declared option - DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr); - if (LL_UNLIKELY(DERIVED_BLOCK::selfBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr); + if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) { if(blockp->mCurChoice == 0) { @@ -1622,27 +1823,27 @@ namespace LLInitParam static_cast<enclosing_block_t&>(Param::enclosingBlock()).paramChanged(*this, true); } - void chooseAs(value_assignment_t val) + void chooseAs(const value_t& val) { super_t::set(val); } - void operator =(value_assignment_t val) + void operator =(const value_t& val) { super_t::set(val); } - void operator()(typename super_t::value_assignment_t val) + void operator()(const value_t& val) { super_t::set(val); } - operator value_assignment_t() const + operator const value_t&() const { return (*this)(); } - value_assignment_t operator()() const + const value_t& operator()() const { if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this) { @@ -1657,11 +1858,11 @@ namespace LLInitParam } private: - T mOriginalValue; + default_value_t mOriginalValue; }; - protected: - static BlockDescriptor& selfBlockDescriptor() + public: + static BlockDescriptor& getBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; @@ -1681,6 +1882,8 @@ namespace LLInitParam : public BASE_BLOCK { typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t; + + protected: typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t; public: @@ -1689,80 +1892,82 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(selfBlockDescriptor(), other, true); + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(selfBlockDescriptor(), other, false); + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); } - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } protected: Block() { //#pragma message("Parsing LLInitParam::Block") - BaseBlock::init(selfBlockDescriptor(), BASE_BLOCK::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // // Nested classes for declaring parameters // - template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false> { - public: - typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBlock<ParamValue<T, NAME_VALUE_LOOKUP> >::value> super_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: using super_t::operator(); using super_t::operator =; - explicit Optional(const char* name = "", value_assignment_t val = defaultValue<T>()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1) + explicit Optional(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1) { //#pragma message("Parsing LLInitParam::Block::Optional") } - Optional& operator =(value_assignment_t val) + Optional& operator =(const value_t& val) { set(val); return *this; } - DERIVED_BLOCK& operator()(value_assignment_t val) + DERIVED_BLOCK& operator()(const value_t& val) { super_t::set(val); return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); } }; - template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false> { - public: - typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IsBlock<ParamValue<T, NAME_VALUE_LOOKUP> >::value> super_t; + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: using super_t::operator(); using super_t::operator =; // mandatory parameters require a name to be parseable - explicit Mandatory(const char* name = "", value_assignment_t val = defaultValue<T>()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, 1, 1) + explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1) {} - Mandatory& operator =(value_assignment_t val) + Mandatory& operator =(const value_t& val) { set(val); return *this; } - DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + DERIVED_BLOCK& operator()(const value_t& val) { super_t::set(val); return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); @@ -1776,28 +1981,29 @@ namespace LLInitParam }; - template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> > + template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true> { - public: - typedef TypedParam<T, NAME_VALUE_LOOKUP, true, IsBlock<ParamValue<T, NAME_VALUE_LOOKUP> >::value> super_t; + typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t; typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t; typedef typename super_t::container_t container_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef typename super_t::value_t value_t; + + public: typedef typename super_t::iterator iterator; typedef typename super_t::const_iterator const_iterator; explicit Multiple(const char* name = "") - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) {} - Multiple& operator =(value_assignment_t val) + Multiple& operator =(const container_t& val) { set(val); return *this; } - DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + DERIVED_BLOCK& operator()(const container_t& val) { super_t::set(val); return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); @@ -1810,13 +2016,15 @@ namespace LLInitParam } }; - class Deprecated : public Param + // can appear in data files, but will ignored during parsing + // cannot read or write in code + class Ignored : public Param { public: - explicit Deprecated(const char* name) - : Param(DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr) + explicit Ignored(const char* name) + : Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr) { - BlockDescriptor& block_descriptor = DERIVED_BLOCK::selfBlockDescriptor(); + BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor(); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( @@ -1827,7 +2035,7 @@ namespace LLInitParam NULL, NULL, 0, S32_MAX)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + block_descriptor.addParam(param_descriptor, name); } } @@ -1844,19 +2052,46 @@ namespace LLInitParam } }; - // different semantics for documentation purposes, but functionally identical - typedef Deprecated Ignored; + // can appear in data files, or be written to in code, but data will be ignored + // cannot be read in code + class Deprecated : public Ignored + { + public: + explicit Deprecated(const char* name) : Ignored(name) {} - protected: - static BlockDescriptor& selfBlockDescriptor() + // dummy writer interfaces + template<typename T> + Deprecated& operator =(const T& val) + { + // do nothing + return *this; + } + + template<typename T> + DERIVED_BLOCK& operator()(const T& val) + { + // do nothing + return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); + } + + template<typename T> + void set(const T& val, bool flag_as_provided = true) + { + // do nothing + } + }; + + public: + static BlockDescriptor& getBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; } - template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, bool is_block> + protected: + template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, typename is_block> void changeDefault(TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>& param, - typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_assignment_t value) + const typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_t& value) { if (!param.isProvided()) { @@ -1866,202 +2101,414 @@ namespace LLInitParam }; - template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> - class BatchBlock - : public Block<DERIVED_BLOCK, BASE_BLOCK> + template<typename T, typename BLOCK_T> + struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::IS_A_BLOCK>, BLOCK_T >, void> { + typedef IS_A_BLOCK value_t; + }; + + template<typename T, typename BLOCK_T> + struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::NOT_A_BLOCK>, BLOCK_T >, void> + { + typedef NOT_BLOCK value_t; + }; + + template<typename T, typename BLOCK_IDENTIFIER> + struct IsBlock<ParamValue<BaseBlock::Atomic<T>, typename IsBlock<BaseBlock::Atomic<T> >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock<T>::value_t value_t; + }; + + template<typename T, typename BLOCK_IDENTIFIER> + struct IsBlock<ParamValue<BaseBlock::Sequential<T>, typename IsBlock<BaseBlock::Sequential<T> >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock<T>::value_t value_t; + }; + + + template<typename T> + struct InnerMostType + { + typedef T value_t; + }; + + template<typename T> + struct InnerMostType<ParamValue<T, NOT_BLOCK> > + { + typedef typename InnerMostType<T>::value_t value_t; + }; + + template<typename T> + struct InnerMostType<ParamValue<T, IS_A_BLOCK> > + { + typedef typename InnerMostType<T>::value_t value_t; + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Atomic<T>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Atomic<T>, BLOCK_T> self_t; + public: - typedef BatchBlock<DERIVED_BLOCK, BASE_BLOCK> self_t; - typedef Block<DERIVED_BLOCK, BASE_BLOCK> super_t; + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; + + ParamValue() + : mValue(), + mValidated(false) + {} - BatchBlock() + ParamValue(const default_value_t& value) + : mValue(value), + mValidated(false) {} + void setValue(const value_t& val) + { + mValue.setValue(val); + } + + const value_t& getValue() const + { + return mValue.getValue(); + } + + value_t& getValue() + { + return mValue.getValue(); + } + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) { if (new_name) { - // reset block - *static_cast<DERIVED_BLOCK*>(this) = defaultBatchValue(); + resetToDefault(); } - return super_t::deserializeBlock(p, name_stack_range, new_name); + return mValue.deserializeBlock(p, name_stack_range, new_name); + } + + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const + { + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + mValue.serializeBlock(p, name_stack, base_block); } - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) { if (overwrite) { - *static_cast<DERIVED_BLOCK*>(this) = defaultBatchValue(); - // merge individual parameters into destination - return super_t::mergeBlock(super_t::selfBlockDescriptor(), other, overwrite); + resetToDefault(); + return mValue.mergeBlock(block_data, source.getValue(), overwrite); } return false; } - protected: - static const DERIVED_BLOCK& defaultBatchValue() + + bool validateBlock(bool emit_errors = true) const { - static DERIVED_BLOCK default_value; - return default_value; + return mValue.validateBlock(emit_errors); } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + + mutable bool mValidated; // lazy validation flag + + private: + void resetToDefault() + { + static T default_value; + mValue = default_value; + } + + T mValue; }; - // FIXME: this specialization is not currently used, as it only matches against the BatchBlock base class - // and not the derived class with the actual params - template<typename DERIVED_BLOCK, - typename BASE_BLOCK, - typename NAME_VALUE_LOOKUP> - class ParamValue <BatchBlock<DERIVED_BLOCK, BASE_BLOCK>, - NAME_VALUE_LOOKUP, - true> - : public NAME_VALUE_LOOKUP, - protected BatchBlock<DERIVED_BLOCK, BASE_BLOCK> + template<typename T> + class ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> { + typedef ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> self_t; + public: - typedef BatchBlock<DERIVED_BLOCK, BASE_BLOCK> block_t; - typedef const BatchBlock<DERIVED_BLOCK, BASE_BLOCK>& value_assignment_t; - typedef block_t value_t; + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; ParamValue() - : block_t(), + : mValue(), mValidated(false) - {} + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } - ParamValue(value_assignment_t other) - : block_t(other), + ParamValue(const default_value_t& value) + : mValue(value), mValidated(false) { + mCurParam = getBlockDescriptor().mAllParams.begin(); } - void setValue(value_assignment_t val) + void setValue(const value_t& val) { - *this = val; + mValue.setValue(val); } - value_assignment_t getValue() const + const value_t& getValue() const { - return *this; + return mValue.getValue(); } - BatchBlock<DERIVED_BLOCK, BASE_BLOCK>& getValue() + value_t& getValue() { - return *this; + return mValue.getValue(); } - operator value_assignment_t() const + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) { - return *this; + if (new_name) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); } + if (name_stack_range.first == name_stack_range.second + && mCurParam != getBlockDescriptor().mAllParams.end()) + { + // deserialize to mCurParam + ParamDescriptor& pd = *(*mCurParam); + ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc; + Param* paramp = mValue.getParamFromHandle(pd.mParamHandle); - value_assignment_t operator()() const + if (deserialize_func + && paramp + && deserialize_func(*paramp, p, name_stack_range, new_name)) { - return *this; + ++mCurParam; + return true; + } + else + { + return false; + } + } + else + { + return mValue.deserializeBlock(p, name_stack_range, new_name); + } + } + + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const + { + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + mValue.serializeBlock(p, name_stack, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.validateBlock(emit_errors); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); } - protected: mutable bool mValidated; // lazy validation flag + + private: + + BlockDescriptor::all_params_list_t::iterator mCurParam; + T mValue; }; - template<typename T, bool IS_BLOCK> - class ParamValue <BaseBlock::Lazy<T>, - TypeValues<T>, - IS_BLOCK> - : public IsBlock<T>::base_class_t + template<typename T> + class ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> + : public T { + typedef ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> self_t; + public: - typedef ParamValue <BaseBlock::Lazy<T>, TypeValues<T>, false> self_t; - typedef const T& value_assignment_t; - typedef T value_t; + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; + + ParamValue() + : T(), + mValidated(false) + {} + + ParamValue(const default_value_t& value) + : T(value.getValue()), + mValidated(false) + {} + + mutable bool mValidated; // lazy validation flag + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef LazyValue<T> default_value_t; ParamValue() : mValue(), mValidated(false) {} - ParamValue(value_assignment_t other) + ParamValue(const default_value_t& other) : mValue(other), mValidated(false) {} - void setValue(value_assignment_t val) + ParamValue(const T& value) + : mValue(value), + mValidated(false) + {} + + void setValue(const value_t& val) { mValue.set(val); } - value_assignment_t getValue() const + const value_t& getValue() const { - return mValue.get(); + return mValue.get().getValue(); } - T& getValue() + value_t& getValue() { - return mValue.get(); + return mValue.get().getValue(); } - operator value_assignment_t() const + bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) { - return mValue.get(); + return mValue.get().deserializeBlock(p, name_stack_range, new_name); } - value_assignment_t operator()() const + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const { - return mValue.get(); + if (mValue.empty()) return; + + const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty()) + ? &(diff_block->mValue.get().getValue()) + : NULL; + mValue.get().serializeBlock(p, name_stack, base_block); } - bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const { - return mValue.get().deserializeBlock(p, name_stack_range, new_name); + return mValue.get().inspectBlock(p, name_stack, min_count, max_count); } - void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) { - if (mValue.empty()) return; + return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite); + } - mValue.get().serializeBlock(p, name_stack, diff_block); + bool validateBlock(bool emit_errors = true) const + { + return mValue.empty() || mValue.get().validateBlock(emit_errors); } - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + static BlockDescriptor& getBlockDescriptor() { - if (mValue.empty()) return false; + return value_t::getBlockDescriptor(); + } - return mValue.get().inspectBlock(p, name_stack, min_count, max_count); + mutable bool mValidated; // lazy validation flag + + private: + LazyValue<T> mValue; + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef LazyValue<T> default_value_t; + + ParamValue() + : mValue(), + mValidated(false) + {} + + ParamValue(const default_value_t& other) + : mValue(other), + mValidated(false) + {} + + ParamValue(const T& value) + : mValue(value), + mValidated(false) + {} + + void setValue(const value_t& val) + { + mValue.set(val); + } + + const value_t& getValue() const + { + return mValue.get().getValue(); + } + + value_t& getValue() + { + return mValue.get().getValue(); } - protected: mutable bool mValidated; // lazy validation flag private: - BaseBlock::Lazy<T> mValue; + LazyValue<T> mValue; }; template <> - class ParamValue <LLSD, - TypeValues<LLSD>, - false> - : public TypeValues<LLSD>, - public BaseBlock + class ParamValue <LLSD, NOT_BLOCK> + : public BaseBlock { public: - typedef ParamValue<LLSD, TypeValues<LLSD>, false> self_t; - typedef const LLSD& value_assignment_t; + typedef LLSD value_t; + typedef LLSD default_value_t; ParamValue() : mValidated(false) {} - ParamValue(value_assignment_t other) + ParamValue(const default_value_t& other) : mValue(other), mValidated(false) {} - void setValue(value_assignment_t val) { mValue = val; } + void setValue(const value_t& val) { mValue = val; } - value_assignment_t getValue() const { return mValue; } + const value_t& getValue() const { return mValue; } LLSD& getValue() { return mValue; } - operator value_assignment_t() const { return mValue; } - value_assignment_t operator()() const { return mValue; } - - // block param interface LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name); LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; @@ -2082,8 +2529,7 @@ namespace LLInitParam template<typename T> class CustomParamValue - : public Block<ParamValue<T, TypeValues<T> > >, - public TypeValues<T> + : public Block<ParamValue<T> > { public: typedef enum e_value_age @@ -2093,14 +2539,15 @@ namespace LLInitParam BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative } EValueAge; - typedef ParamValue<T, TypeValues<T> > derived_t; + typedef ParamValue<T> derived_t; typedef CustomParamValue<T> self_t; typedef Block<derived_t> block_t; - typedef const T& value_assignment_t; + typedef T default_value_t; typedef T value_t; + typedef void baseblock_base_class_t; - CustomParamValue(const T& value = T()) + CustomParamValue(const default_value_t& value = T()) : mValue(value), mValueAge(VALUE_AUTHORITATIVE), mValidated(false) @@ -2117,8 +2564,6 @@ namespace LLInitParam typed_param.mValueAge = VALUE_AUTHORITATIVE; typed_param.updateBlockFromValue(false); - typed_param.clearValueName(); - return true; } } @@ -2132,18 +2577,8 @@ namespace LLInitParam const derived_t& typed_param = static_cast<const derived_t&>(*this); const derived_t* diff_param = static_cast<const derived_t*>(diff_block); - std::string key = typed_param.getValueName(); - - // first try to write out name of name/value pair - if (!key.empty()) - { - if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key)) - { - parser.writeValue(key, name_stack); - } - } // then try to serialize value directly - else if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue())) + if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue())) { if (!parser.writeValue(typed_param.getValue(), name_stack)) @@ -2173,19 +2608,6 @@ namespace LLInitParam } } - bool inspectBlock(Parser& parser, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - // first, inspect with actual type... - parser.inspectValue<T>(name_stack, min_count, max_count, NULL); - if (TypeValues<T>::getPossibleValues()) - { - //...then inspect with possible string values... - parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues()); - } - // then recursively inspect contents... - return block_t::inspectBlock(parser, name_stack, min_count, max_count); - } - bool validateBlock(bool emit_errors = true) const { if (mValueAge == VALUE_NEEDS_UPDATE) @@ -2193,7 +2615,6 @@ namespace LLInitParam if (block_t::validateBlock(emit_errors)) { // clear stale keyword associated with old value - TypeValues<T>::clearValueName(); mValueAge = BLOCK_AUTHORITATIVE; static_cast<derived_t*>(const_cast<self_t*>(this))->updateValueFromBlock(); return true; @@ -2223,17 +2644,15 @@ namespace LLInitParam } } - void setValue(value_assignment_t val) + void setValue(const value_t& val) { - derived_t& typed_param = static_cast<derived_t&>(*this); // set param version number to be up to date, so we ignore block contents mValueAge = VALUE_AUTHORITATIVE; mValue = val; - typed_param.clearValueName(); static_cast<derived_t*>(this)->updateBlockFromValue(false); } - value_assignment_t getValue() const + const value_t& getValue() const { validateBlock(true); return mValue; @@ -2245,20 +2664,10 @@ namespace LLInitParam return mValue; } - operator value_assignment_t() const - { - return getValue(); - } - - value_assignment_t operator()() const - { - return getValue(); - } - protected: // use this from within updateValueFromBlock() to set the value without making it authoritative - void updateValue(value_assignment_t value) + void updateValue(const value_t& value) { mValue = value; } diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 403df08990..1eab270e3c 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -43,7 +43,7 @@ * semantics: one instance per process, rather than one instance per module as * sometimes happens with data simply declared static. */ -class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable +class LL_COMMON_API LLInstanceTrackerBase { protected: /// Get a process-unique void* pointer slot for the specified type_info @@ -210,6 +210,9 @@ protected: virtual const KEY& getKey() const { return mInstanceKey; } private: + LLInstanceTracker( const LLInstanceTracker& ); + const LLInstanceTracker& operator=( const LLInstanceTracker& ); + void add_(KEY key) { mInstanceKey = key; diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 8eb5d53f3f..32ae15435a 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -27,6 +27,7 @@ #define LLREFCOUNT_H #include <boost/noncopyable.hpp> +#include <boost/intrusive_ptr.hpp> #define LL_REF_COUNT_DEBUG 0 #if LL_REF_COUNT_DEBUG @@ -86,4 +87,22 @@ private: #endif }; +/** + * intrusive pointer support + * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type + */ +namespace boost +{ + inline void intrusive_ptr_add_ref(LLRefCount* p) + { + p->ref(); + } + + inline void intrusive_ptr_release(LLRefCount* p) + { + p->unref(); + } +}; + + #endif diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 36d7f7a44c..3c3b8ac29e 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -321,6 +321,10 @@ public: virtual ~StaticRegistrar() {} StaticRegistrar(ref_const_key_t key, ref_const_value_t value) { + if (singleton_t::instance().exists(key)) + { + llerrs << "Duplicate registry entry under key \"" << key << "\"" << llendl; + } singleton_t::instance().mStaticScope->add(key, value); } }; diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp index 0e29873bb0..54c8389772 100644 --- a/indra/llcommon/llsdparam.cpp +++ b/indra/llcommon/llsdparam.cpp @@ -223,10 +223,14 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser: { bool new_traversal = it->second; - LLSD* child_sd = it->first.empty() ? sd_to_write : &(*sd_to_write)[it->first]; - - if (child_sd->isArray()) + LLSD* child_sd; + if (it->first.empty()) { + child_sd = sd_to_write; + if (child_sd->isUndefined()) + { + *child_sd = LLSD::emptyArray(); + } if (new_traversal) { // write to new element at end @@ -240,22 +244,7 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser: } else { - if (new_traversal - && child_sd->isDefined() - && !child_sd->isArray()) - { - // copy child contents into first element of an array - LLSD new_array = LLSD::emptyArray(); - new_array.append(*child_sd); - // assign array to slot that previously held the single value - *child_sd = new_array; - // return next element in that array - sd_to_write = &((*child_sd)[1]); - } - else - { - sd_to_write = child_sd; - } + sd_to_write = &(*sd_to_write)[it->first]; } it->second = false; } @@ -283,8 +272,9 @@ void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLI it != sd.endArray(); ++it) { - stack.back().second = true; + stack.push_back(make_pair(std::string(), true)); readSDValues(cb, *it, stack); + stack.pop_back(); } } else if (sd.isUndefined()) @@ -313,8 +303,14 @@ namespace LLInitParam { // LLSD specialization // block param interface - bool ParamValue<LLSD, TypeValues<LLSD>, false>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name) + bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name) { + if (name_stack.first == name_stack.second + && p.readValue<LLSD>(mValue)) + { + return true; + } + LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); LLSD::String string; @@ -328,15 +324,18 @@ namespace LLInitParam } //static - void ParamValue<LLSD, TypeValues<LLSD>, false>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) + void ParamValue<LLSD, NOT_BLOCK>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) { p.writeValue<LLSD::String>(sd.asString(), name_stack); } - void ParamValue<LLSD, TypeValues<LLSD>, false>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const + void ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const { - // read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) - Parser::name_stack_t stack; - LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, stack); + // attempt to write LLSD out directly + if (!p.writeValue<LLSD>(mValue, name_stack)) + { + // otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) + LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack); + } } } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b52e70ab2e..cf39696b4f 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -30,6 +30,7 @@ #include "llapp.h" #include "llapr.h" #include "apr_thread_cond.h" +#include "boost/intrusive_ptr.hpp" class LLThread; class LLMutex; @@ -266,6 +267,22 @@ private: S32 mRef; }; +/** + * intrusive pointer support for LLThreadSafeRefCount + * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type + */ +namespace boost +{ + inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p) + { + p->ref(); + } + + inline void intrusive_ptr_release(LLThreadSafeRefCount* p) + { + p->unref(); + } +}; //============================================================================ // Simple responder for self destructing callbacks diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h index 40b3364b36..efcbe76795 100644 --- a/indra/llcommon/stdenums.h +++ b/indra/llcommon/stdenums.h @@ -51,7 +51,8 @@ enum EDragAndDropType DAD_LINK = 14, DAD_MESH = 15, DAD_WIDGET = 16, - DAD_COUNT = 17, // number of types in this enum + DAD_PERSON = 17, + DAD_COUNT = 18, // number of types in this enum }; // Reasons for drags to be denied. diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index fbf23bc3f0..6e54f9d78a 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -275,6 +275,18 @@ void LLInventoryObject::correctInventoryName(std::string& name) LLStringUtil::truncate(name, DB_INV_ITEM_NAME_STR_LEN); } +time_t LLInventoryObject::getCreationDate() const +{ + return mCreationDate; +} + +void LLInventoryObject::setCreationDate(time_t creation_date_utc) +{ + mCreationDate = creation_date_utc; +} + + + ///---------------------------------------------------------------------------- /// Class LLInventoryItem @@ -297,9 +309,10 @@ LLInventoryItem::LLInventoryItem(const LLUUID& uuid, mDescription(desc), mSaleInfo(sale_info), mInventoryType(inv_type), - mFlags(flags), - mCreationDate(creation_date_utc) + mFlags(flags) { + mCreationDate = creation_date_utc; + LLStringUtil::replaceNonstandardASCII(mDescription, ' '); LLStringUtil::replaceChar(mDescription, '|', ' '); mPermissions.initMasks(inv_type); @@ -312,9 +325,9 @@ LLInventoryItem::LLInventoryItem() : mDescription(), mSaleInfo(), mInventoryType(LLInventoryType::IT_NONE), - mFlags(0), - mCreationDate(0) + mFlags(0) { + mCreationDate = 0; } LLInventoryItem::LLInventoryItem(const LLInventoryItem* other) : @@ -379,11 +392,6 @@ const std::string& LLInventoryItem::getDescription() const return mDescription; } -time_t LLInventoryItem::getCreationDate() const -{ - return mCreationDate; -} - U32 LLInventoryItem::getCRC32() const { // *FIX: Not a real crc - more of a checksum. @@ -440,11 +448,6 @@ void LLInventoryItem::setFlags(U32 flags) mFlags = flags; } -void LLInventoryItem::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; -} - // Currently only used in the Viewer to handle calling cards // where the creator is actually used to store the target. void LLInventoryItem::setCreator(const LLUUID& creator) @@ -506,6 +509,12 @@ U32 LLInventoryItem::getFlags() const return mFlags; } +time_t LLInventoryItem::getCreationDate() const +{ + return mCreationDate; +} + + // virtual void LLInventoryItem::packMessage(LLMessageSystem* msg) const { diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index a5cfe59bda..dc9a09e9d6 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -75,6 +75,7 @@ public: virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items BOOL getIsLinkType() const; + virtual time_t getCreationDate() const; //-------------------------------------------------------------------- // Mutators @@ -85,6 +86,7 @@ public: virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); void setType(LLAssetType::EType type); + virtual void setCreationDate(time_t creation_date_utc); // only stored for items private: // in place correction for inventory name string @@ -113,6 +115,7 @@ protected: LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL. LLAssetType::EType mType; std::string mName; + time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -178,7 +181,6 @@ public: void setPermissions(const LLPermissions& perm); void setInventoryType(LLInventoryType::EType inv_type); void setFlags(U32 flags); - void setCreationDate(time_t creation_date_utc); void setCreator(const LLUUID& creator); // only used for calling cards // Check for changes in permissions masks and sale info @@ -224,7 +226,6 @@ protected: LLSaleInfo mSaleInfo; LLInventoryType::EType mInventoryType; U32 mFlags; - time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index 8282d79b67..8807b36117 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -85,6 +85,7 @@ LLInventoryDictionary::LLInventoryDictionary() addEntry(LLInventoryType::IT_GESTURE, new InventoryEntry("gesture", "gesture", 1, LLAssetType::AT_GESTURE)); addEntry(LLInventoryType::IT_MESH, new InventoryEntry("mesh", "mesh", 1, LLAssetType::AT_MESH)); addEntry(LLInventoryType::IT_WIDGET, new InventoryEntry("widget", "widget", 1, LLAssetType::AT_WIDGET)); + addEntry(LLInventoryType::IT_PERSON, new InventoryEntry("person", "person", 1, LLAssetType::AT_PERSON)); } @@ -140,7 +141,7 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = LLInventoryType::IT_NONE, // 42 AT_NONE LLInventoryType::IT_NONE, // 43 AT_NONE LLInventoryType::IT_NONE, // 44 AT_NONE - LLInventoryType::IT_NONE, // 45 AT_NONE + LLInventoryType::IT_PERSON, // 45 AT_PERSON LLInventoryType::IT_NONE, // 46 AT_NONE LLInventoryType::IT_NONE, // 47 AT_NONE LLInventoryType::IT_NONE, // 48 AT_NONE diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h index 4d1e0db040..645ebab234 100644 --- a/indra/llinventory/llinventorytype.h +++ b/indra/llinventory/llinventorytype.h @@ -63,7 +63,8 @@ public: IT_GESTURE = 20, IT_MESH = 22, IT_WIDGET = 23, - IT_COUNT = 24, + IT_PERSON = 24, + IT_COUNT = 25, IT_NONE = -1 }; diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index cca4ca3981..bc225593d8 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -33,6 +33,7 @@ set(llui_SOURCE_FILES llbadgeholder.cpp llbadgeowner.cpp llbutton.cpp + llchatentry.cpp llcheckboxctrl.cpp llclipboard.cpp llcombobox.cpp @@ -52,6 +53,9 @@ set(llui_SOURCE_FILES llfloaterreglistener.cpp llflyoutbutton.cpp llfocusmgr.cpp + llfolderview.cpp + llfolderviewitem.cpp + llfolderviewmodel.cpp llfunctorregistry.cpp lliconctrl.cpp llkeywords.cpp @@ -135,6 +139,7 @@ set(llui_HEADER_FILES llbadgeowner.h llbutton.h llcallbackmap.h + llchatentry.h llcheckboxctrl.h llclipboard.h llcombobox.h @@ -154,6 +159,9 @@ set(llui_HEADER_FILES llfloaterreglistener.h llflyoutbutton.h llfocusmgr.h + llfolderview.h + llfolderviewitem.h + llfolderviewmodel.h llfunctorregistry.h llhandle.h llhelp.h diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index c025cd7939..43462bd244 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -184,7 +184,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string if (mHeaderTextbox) { std::string text = mHeaderTextbox->getText(); - mStyleParams.font(mHeaderTextbox->getDefaultFont()); + mStyleParams.font(mHeaderTextbox->getFont()); mStyleParams.font.style(style); mHeaderTextbox->setText(text, mStyleParams); } diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp new file mode 100644 index 0000000000..2a6ccc3dc9 --- /dev/null +++ b/indra/llui/llchatentry.cpp @@ -0,0 +1,199 @@ +/** + * @file llchatentry.cpp + * @brief LLChatEntry implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llchatentry.h" + +static LLDefaultChildRegistry::Register<LLChatEntry> r("chat_editor"); + +LLChatEntry::Params::Params() +: has_history("has_history", true), + is_expandable("is_expandable", false), + expand_lines_count("expand_lines_count", 1) +{} + +LLChatEntry::LLChatEntry(const Params& p) +: LLTextEditor(p), + mTextExpandedSignal(NULL), + mHasHistory(p.has_history), + mIsExpandable(p.is_expandable), + mExpandLinesCount(p.expand_lines_count), + mPrevLinesCount(0) +{ + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + mAutoIndent = false; +} + +LLChatEntry::~LLChatEntry() +{ + delete mTextExpandedSignal; +} + +void LLChatEntry::draw() +{ + if(mIsExpandable) + { + expandText(); + } + + LLTextEditor::draw(); +} + +void LLChatEntry::onCommit() +{ + updateHistory(); + LLTextEditor::onCommit(); +} + +boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb) +{ + if (!mTextExpandedSignal) + { + mTextExpandedSignal = new commit_signal_t(); + } + return mTextExpandedSignal->connect(cb); +} + +void LLChatEntry::expandText() +{ + int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second); + bool can_expand = getLineCount() <= mExpandLinesCount; + + // true if pasted text has more lines than expand height limit and expand limit is not reached yet + bool text_pasted = (getLineCount() > mExpandLinesCount) && (visible_lines_count < mExpandLinesCount); + + if (mIsExpandable && (can_expand || text_pasted) && getLineCount() != mPrevLinesCount) + { + int lines_height = 0; + if (text_pasted) + { + // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty, + // so lines_height is the sum of the last 'mExpandLinesCount' lines height + lines_height = (mLineInfoList.end() - mExpandLinesCount)->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + else + { + lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + + int height = mVPad * 2 + lines_height; + + LLRect doc_rect = getRect(); + doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height); + setShape(doc_rect); + + mPrevLinesCount = getLineCount(); + + if (mTextExpandedSignal) + { + (*mTextExpandedSignal)(this, LLSD() ); + } + } +} + +// line history support +void LLChatEntry::updateHistory() +{ + // On history enabled, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if (mHasHistory && getLength()) + { + // Add text to history, ignoring duplicates + if (mLineHistory.empty() || getText() != mLineHistory.back()) + { + mLineHistory.push_back(getText()); + } + + mCurrentHistoryLine = mLineHistory.end(); + } +} + +BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) +{ + BOOL handled = FALSE; + + LLTextEditor::handleSpecialKey(key, mask); + + switch(key) + { + case KEY_RETURN: + if (MASK_NONE == mask) + { + needsReflow(); + } + break; + + case KEY_UP: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin()) + { + setText(*(--mCurrentHistoryLine)); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + case KEY_DOWN: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine < (mLineHistory.end() - 1) ) + { + setText(*(++mCurrentHistoryLine)); + endOfDoc(); + } + else if (!mLineHistory.empty() && mCurrentHistoryLine == (mLineHistory.end() - 1) ) + { + mCurrentHistoryLine++; + std::string empty(""); + setText(empty); + needsReflow(); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + default: + break; + } + + return handled; +} diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h new file mode 100644 index 0000000000..10a4594e83 --- /dev/null +++ b/indra/llui/llchatentry.h @@ -0,0 +1,98 @@ +/** + * @file llchatentry.h + * @author Paul Guslisty + * @brief Text editor widget which is used for user input + * + * Features: + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * Optional auto-resize behavior on input chat field + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCHATENTRY_H_ +#define LLCHATENTRY_H_ + +#include "lltexteditor.h" + +class LLChatEntry : public LLTextEditor +{ +public: + + struct Params : public LLInitParam::Block<Params, LLTextEditor::Params> + { + Optional<bool> has_history, + is_expandable; + + Optional<int> expand_lines_count; + + Params(); + }; + + virtual ~LLChatEntry(); + +protected: + + friend class LLUICtrlFactory; + LLChatEntry(const Params& p); + +public: + + virtual void draw(); + virtual void onCommit(); + + boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); + +private: + + /** + * Implements auto-resize behavior. + * When user's typing reaches the right edge of the chat field + * the chat field expands vertically by one line. The bottom of + * the chat field remains bottom-justified. The chat field does + * not expand beyond mExpandLinesCount. + */ + void expandText(); + + /** + * Implements line history so previous entries can be recalled by CTRL UP/DOWN + */ + void updateHistory(); + + BOOL handleSpecialKey(const KEY key, const MASK mask); + + + // Fired when text height expanded to mExpandLinesCount + commit_signal_t* mTextExpandedSignal; + + // line history support: + typedef std::vector<std::string> line_history_t; + line_history_t::iterator mCurrentHistoryLine; // currently browsed history line + line_history_t mLineHistory; // line history storage + bool mHasHistory; // flag for enabled/disabled line history + bool mIsExpandable; + + int mExpandLinesCount; + int mPrevLinesCount; +}; + +#endif /* LLCHATENTRY_H_ */ diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 8ca1e685a9..a1c902d562 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1406,7 +1406,6 @@ void LLFloater::setHost(LLMultiFloater* host) mButtonScale = 1.f; //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; } - updateTitleButtons(); if (host) { mHostHandle = host->getHandle(); @@ -1416,6 +1415,8 @@ void LLFloater::setHost(LLMultiFloater* host) { mHostHandle.markDead(); } + + updateTitleButtons(); } void LLFloater::moveResizeHandlesToFront() @@ -1682,6 +1683,7 @@ void LLFloater::onClickTearOff(LLFloater* self) self->setTornOff(false); } self->updateTitleButtons(); + self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); } // static diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 64d6dcea04..17402b8d63 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -324,6 +324,8 @@ public: virtual void setDocked(bool docked, bool pop_on_undock = true); virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } + bool getTornOff() {return mTornOff;} + void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;} // Return a closeable floater, if any, given the current focus. static LLFloater* getClosableFloaterFromFocus(); diff --git a/indra/newview/llfolderview.cpp b/indra/llui/llfolderview.cpp index 9ce64a630f..e09026fc33 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -24,39 +24,20 @@ * $/LicenseInfo$ */ -#include "llviewerprecompiledheaders.h" +#include "linden_common.h" #include "llfolderview.h" - -#include "llcallbacklist.h" -#include "llinventorybridge.h" +#include "llfolderviewmodel.h" #include "llclipboard.h" // *TODO: remove this once hack below gone. -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llinventorypanel.h" -#include "llfoldertype.h" -#include "llfloaterinventory.h"// hacked in for the bonus context menu items. #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" #include "llpanel.h" -#include "llpreview.h" #include "llscrollcontainer.h" // hack to allow scrolling -#include "lltooldraganddrop.h" +#include "lltextbox.h" #include "lltrans.h" #include "llui.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "llviewerjointattachment.h" -#include "llviewermenu.h" #include "lluictrlfactory.h" -#include "llviewercontrol.h" -#include "llviewerfoldertype.h" -#include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llfloaterproperties.h" -#include "llnotificationsutil.h" // Linden library includes #include "lldbstrings.h" @@ -64,7 +45,6 @@ #include "llfontgl.h" #include "llgl.h" #include "llrender.h" -#include "llinventory.h" // Third-party library includes #include <algorithm> @@ -94,42 +74,6 @@ enum { F32 LLFolderView::sAutoOpenTime = 1.f; -void delete_selected_item(void* user_data); -void copy_selected_item(void* user_data); -void open_selected_items(void* user_data); -void properties_selected_items(void* user_data); -void paste_items(void* user_data); - - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to sort their items -// (and only their items, not folders) by a certain function. -class LLSetItemSortFunction : public LLFolderViewFunctor -{ -public: - LLSetItemSortFunction(U32 ordering) - : mSortOrder(ordering) {} - virtual ~LLSetItemSortFunction() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - U32 mSortOrder; -}; - - -// Set the sort order. -void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) -{ - folder->setItemSortOrder(mSortOrder); -} - -// Do nothing. -void LLSetItemSortFunction::doItem(LLFolderViewItem* item) -{ - return; -} - //--------------------------------------------------------------------------- // Tells all folders in a folderview to close themselves @@ -154,7 +98,6 @@ public: }; -// Set the sort order. void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) { folder->setOpenArrangeRecursively(mOpen); @@ -177,7 +120,7 @@ const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView); if (folder_view) { - S32 height = folder_view->mRunningHeight; + S32 height = folder_view->getRect().getHeight(); rect = mScrolledView->getRect(); rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); @@ -195,27 +138,24 @@ LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer /// Class LLFolderView ///---------------------------------------------------------------------------- LLFolderView::Params::Params() -: task_id("task_id"), - title("title"), +: title("title"), use_label_suffix("use_label_suffix"), allow_multiselect("allow_multiselect", true), show_empty_message("show_empty_message", true), - show_load_status("show_load_status", true), use_ellipses("use_ellipses", false) { + folder_indentation = -4; } // Default constructor LLFolderView::LLFolderView(const Params& p) : LLFolderViewFolder(p), - mRunningHeight(0), mScrollContainer( NULL ), mPopupMenuHandle(), mAllowMultiSelect(p.allow_multiselect), mShowEmptyMessage(p.show_empty_message), mShowFolderHierarchy(FALSE), - mSourceID(p.task_id), mRenameItem( NULL ), mNeedsScroll( FALSE ), mUseLabelSuffix(p.use_label_suffix), @@ -223,9 +163,6 @@ LLFolderView::LLFolderView(const Params& p) mNeedsAutoSelect( FALSE ), mAutoSelectOverride(FALSE), mNeedsAutoRename(FALSE), - mDebugFilters(FALSE), - mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter( new LLInventoryFilter(p.title) ), mShowSelectionContext(FALSE), mShowSingleSelection(FALSE), mArrangeGeneration(0), @@ -236,33 +173,28 @@ LLFolderView::LLFolderView(const Params& p) mParentPanel(p.parent_panel), mUseEllipses(p.use_ellipses), mDraggingOverItem(NULL), - mStatusTextBox(NULL) + mStatusTextBox(NULL), + mShowItemLinkOverlays(p.show_item_link_overlays), + mViewModel(p.view_model) { + mViewModel->setFolderView(this); mRoot = this; - mShowLoadStatus = p.show_load_status(); - LLRect rect = p.rect; LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); setRect( rect ); reshape(rect.getWidth(), rect.getHeight()); - mIsOpen = TRUE; // this view is always open. mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); mAutoOpenCandidate = NULL; mAutoOpenTimer.stop(); mKeyboardSelection = FALSE; - const LLFolderViewItem::Params& item_params = - LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - S32 indentation = item_params.folder_indentation(); - mIndentation = -indentation; // children start at indentation 0 - gIdleCallbacks.addFunction(idle, this); + mIndentation = p.folder_indentation; //clear label // go ahead and render root folder as usual // just make sure the label ("Inventory Folder") never shows up mLabel = LLStringUtil::null; - //mRenamer->setWriteableBgColor(LLColor4::white); // Escape is handled by reverting the rename, not commiting it (default behavior) LLLineEditor::Params params; params.name("ren"); @@ -299,7 +231,7 @@ LLFolderView::LLFolderView(const Params& p) // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); if (!menu) { menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); @@ -307,7 +239,7 @@ LLFolderView::LLFolderView(const Params& p) menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); mPopupMenuHandle = menu->getHandle(); - mListener->openItem(); + mViewModelItem->openItem(); } // Destroys the object @@ -326,7 +258,6 @@ LLFolderView::~LLFolderView( void ) mStatusTextBox = NULL; mAutoOpenItems.removeAllNodes(); - gIdleCallbacks.deleteFunction(idle, this); if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); @@ -335,10 +266,7 @@ LLFolderView::~LLFolderView( void ) mItems.clear(); mFolders.clear(); - mItemMap.clear(); - - delete mFilter; - mFilter = NULL; + mViewModel = NULL; } BOOL LLFolderView::canFocusChildren() const @@ -346,45 +274,21 @@ BOOL LLFolderView::canFocusChildren() const return FALSE; } -static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); - -void LLFolderView::setSortOrder(U32 order) +BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) { - if (order != mSortOrder) - { - LLFastTimer t(FTM_SORT); - - mSortOrder = order; - - sortBy(order); - arrangeAll(); - } -} + LLFolderViewFolder::addFolder(folder); + // TODO RN: enforce sort order of My Inventory followed by Library + //mFolders.remove(folder); + //if (((LLFolderViewModelItemInventory*)folder->getViewModelItem())->getUUID() == gInventory.getLibraryRootFolderID()) + //{ + // mFolders.push_back(folder); + //} + //else + //{ + // mFolders.insert(mFolders.begin(), folder); + //} -U32 LLFolderView::getSortOrder() const -{ - return mSortOrder; -} - -BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) -{ - // enforce sort order of My Inventory followed by Library - if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID()) - { - mFolders.push_back(folder); - } - else - { - mFolders.insert(mFolders.begin(), folder); - } - folder->setShowLoadStatus(mShowLoadStatus); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - folder->requestArrange(); return TRUE; } @@ -405,145 +309,39 @@ void LLFolderView::openTopLevelFolders() } } -void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - // call base class to do proper recursion - LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse); - // make sure root folder is always open - mIsOpen = TRUE; -} - -static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); - // This view grows and shrinks to enclose all of its children items and folders. -S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) +// *width should be 0 +// conform show folder state works +S32 LLFolderView::arrange( S32* unused_width, S32* unused_height ) { - if (getListener()->getUUID().notNull()) - { - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - } - - LLFastTimer t2(FTM_ARRANGE); - - filter_generation = mFilter->getMinRequiredGeneration(); mMinWidth = 0; + S32 target_height; - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - // arrange always finishes, so optimistically set the arrange generation to the most current - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - - LLInventoryFilter::EFolderShow show_folder_state = - getRoot()->getFilter()->getShowFolderState(); + LLFolderViewFolder::arrange(&mMinWidth, &target_height); - S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? LLFontGL::getFontMonospace()->getLineHeight() : 0; - S32 target_height = running_height; - S32 parent_item_height = getRect().getHeight(); + LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + reshape( llmax(scroll_rect.getWidth(), mMinWidth), llround(mCurHeight) ); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folderp = (*fit); - if (getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible((show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation)))); - } - - if (folderp->getVisible()) - { - S32 child_height = 0; - S32 child_width = 0; - S32 child_top = parent_item_height - running_height; - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* itemp = (*iit); - itemp->setVisible(itemp->getFiltered(filter_generation)); - - if (itemp->getVisible()) - { - S32 child_width = 0; - S32 child_height = 0; - S32 child_top = parent_item_height - running_height; - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - itemp->reshape(itemp->getRect().getWidth(), child_height); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); - } - } - - if(!mHasVisibleChildren)// is there any filtered items ? - { - //Nope. We need to display status textbox, let's reserve some place for it - running_height = mStatusTextBox->getTextPixelHeight(); - target_height = running_height; - } - - mRunningHeight = running_height; - LLRect scroll_rect = mScrollContainer->getContentWindowRect(); - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - - LLRect new_scroll_rect = mScrollContainer->getContentWindowRect(); + LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) { - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); + reshape( llmax(scroll_rect.getWidth(), mMinWidth), llround(mCurHeight) ); } // move item renamer text field to item's new position updateRenamerPosition(); - mTargetHeight = (F32)target_height; return llround(mTargetHeight); } -const std::string LLFolderView::getFilterSubString(BOOL trim) -{ - return mFilter->getFilterSubString(trim); -} - -static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory"); +static LLFastTimer::DeclareTimer FTM_FILTER("Filter Folder View"); -void LLFolderView::filter( LLInventoryFilter& filter ) +void LLFolderView::filter( LLFolderViewFilter& filter ) { LLFastTimer t2(FTM_FILTER); - filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); + filter.setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000)); - if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) - { - mPassedFilter = FALSE; - mMinWidth = 0; - LLFolderViewFolder::filter(filter); - } - else - { - mPassedFilter = TRUE; - } + getViewModelItem()->filter(filter); } void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) @@ -554,8 +352,8 @@ void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) LLView::reshape(width, height, called_from_parent); scroll_rect = mScrollContainer->getContentWindowRect(); } - width = llmax(mMinWidth, scroll_rect.getWidth()); - height = llmax(mRunningHeight, scroll_rect.getHeight()); + width = llmax(mMinWidth, scroll_rect.getWidth()); + height = llmax(llround(mCurHeight), scroll_rect.getHeight()); // Restrict width within scroll container's width if (mUseEllipses && mScrollContainer) @@ -653,30 +451,6 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, return rv; } -void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) -{ - LLFolderViewItem* itemp = getItemByID(obj_id); - if(itemp && itemp->getListener()) - { - itemp->arrangeAndSet(TRUE, take_keyboard_focus); - mSelectThisID.setNull(); - return; - } - else - { - // save the desired item to be selected later (if/when ready) - mSelectThisID = obj_id; - } -} - -void LLFolderView::updateSelection() -{ - if (mSelectThisID.notNull()) - { - setSelectionByID(mSelectThisID, false); - } -} - BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) { BOOL rv = FALSE; @@ -727,9 +501,6 @@ void LLFolderView::sanitizeSelection() // and we want to preserve context LLFolderViewItem* original_selected_item = getCurSelectedItem(); - // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); - std::vector<LLFolderViewItem*> items_to_remove; selected_items_t::iterator item_iter; for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) @@ -737,23 +508,14 @@ void LLFolderView::sanitizeSelection() LLFolderViewItem* item = *item_iter; // ensure that each ancestor is open and potentially passes filtering - BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item + BOOL visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item // modify with parent open and filters states LLFolderViewFolder* parent_folder = item->getParentFolder(); - if ( parent_folder ) + // Move up through parent folders and see what's visible + while(parent_folder) { - if ( show_all_folders ) - { // "Show all folders" is on, so this folder is visible - visible = TRUE; - } - else - { // Move up through parent folders and see what's visible - while(parent_folder) - { - visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); - parent_folder = parent_folder->getParentFolder(); - } - } + visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible(); + parent_folder = parent_folder->getParentFolder(); } // deselect item if any ancestor is closed or didn't pass filter requirements. @@ -804,7 +566,7 @@ void LLFolderView::sanitizeSelection() parent_folder; parent_folder = parent_folder->getParentFolder()) { - if (parent_folder->potentiallyVisible()) + if (parent_folder->getViewModelItem()->potentiallyVisible()) { // give initial selection to first ancestor folder that potentially passes the filter if (!new_selection) @@ -843,42 +605,30 @@ void LLFolderView::clearSelection() } mSelectedItems.clear(); - mSelectThisID.setNull(); } -std::set<LLUUID> LLFolderView::getSelectionList() const +std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const { - std::set<LLUUID> selection; - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - selection.insert((*item_it)->getListener()->getUUID()); - } + std::set<LLFolderViewItem*> selection; + std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin())); return selection; } -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) +bool LLFolderView::startDrag() { - std::vector<EDragAndDropType> types; - uuid_vec_t cargo_ids; + std::vector<LLFolderViewModelItem*> selected_items; selected_items_t::iterator item_it; - BOOL can_drag = TRUE; + if (!mSelectedItems.empty()) { for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - EDragAndDropType type = DAD_NONE; - LLUUID id = LLUUID::null; - can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); - - types.push_back(type); - cargo_ids.push_back(id); + selected_items.push_back((*item_it)->getViewModelItem()); } - LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); + return getFolderViewModel()->startDrag(selected_items); } - return can_drag; + return false; } void LLFolderView::commitRename( const LLSD& data ) @@ -888,16 +638,6 @@ void LLFolderView::commitRename( const LLSD& data ) void LLFolderView::draw() { - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white); - if (mDebugFilters) - { - std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); - LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - //LLFontGL* font = getLabelFontForStyle(mLabelStyle); // if cursor has moved off of me during drag and drop @@ -907,52 +647,18 @@ void LLFolderView::draw() closeAutoOpenedFolders(); } - // while dragging, update selection rendering to reflect single/multi drag status - if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) - { - EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); - if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) - { - setShowSingleSelection(TRUE); - } - else - { - setShowSingleSelection(FALSE); - } - } - else - { - setShowSingleSelection(FALSE); - } - - - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) + if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout") || !mSearchString.size()) { mSearchString.clear(); } - if (hasVisibleChildren() - || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) + if (hasVisibleChildren()) { - mStatusText.clear(); mStatusTextBox->setVisible( FALSE ); } else if (mShowEmptyMessage) { - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) - { - mStatusText = LLTrans::getString("Searching"); - } - else - { - if (getFilter()) - { - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig()); - mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args); - } - } - mStatusTextBox->setValue(mStatusText); + mStatusTextBox->setValue(getFolderViewModel()->getStatusText()); mStatusTextBox->setVisible( TRUE ); // firstly reshape message textbox with current size. This is necessary to @@ -969,7 +675,11 @@ void LLFolderView::draw() // This will indirectly call ::arrange and reshape of the status textbox. // We should call this method to also notify parent about required rect. // See EXT-7564, EXT-7047. - arrangeFromRoot(); + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); + LLUI::popMatrix(); LLUI::pushMatrix(); LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); @@ -996,7 +706,7 @@ void LLFolderView::finishRenamingItem( void ) closeRenamer(); - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. + // List is re-sorted alphabetically, so scroll to make sure the selected item is visible. scrollToShowSelection(); } @@ -1005,18 +715,10 @@ void LLFolderView::closeRenamer( void ) if (mRenamer && mRenamer->getVisible()) { // Triggers onRenamerLost() that actually closes the renamer. - gViewerWindow->removePopup(mRenamer); + LLUI::removePopup(mRenamer); } } -void LLFolderView::removeSelectedItems( void ) -{ - if (mSelectedItems.empty()) return; - LLSD args; - args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" : "DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2)); -} - bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFolderViewItem*>& selectedItems) { LLFolderViewItem* item_parent = dynamic_cast<LLFolderViewItem*>(item->getParent()); @@ -1044,29 +746,8 @@ bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFol return false; } -// static -void LLFolderView::removeCutItems() -{ - // There's no item in "cut" mode on the clipboard -> exit - if (!LLClipboard::instance().isCutMode()) - return; - - // Get the list of clipboard item uuids and iterate through them - LLDynamicArray<LLUUID> objects; - LLClipboard::instance().pasteFromClipboard(objects); - for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); - iter != objects.end(); - ++iter) - { - gInventory.removeObject(*iter); - } -} - -void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response) +void LLFolderView::removeSelectedItems() { - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - if(getVisible() && getEnabled()) { // just in case we're removing the renaming item. @@ -1108,16 +789,16 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL } if(parent) { - if (parent->removeItem(item_to_delete)) + if (item_to_delete->remove()) { // change selection on successful delete if (new_selection) { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); + getRoot()->setSelection(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); } else { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); + getRoot()->setSelection(NULL, mParentPanel->hasFocus()); } } } @@ -1125,8 +806,8 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL } else if (count > 1) { - LLDynamicArray<LLFolderViewEventListener*> listeners; - LLFolderViewEventListener* listener; + LLDynamicArray<LLFolderViewModelItem*> listeners; + LLFolderViewModelItem* listener; LLFolderViewItem* last_item = items[count - 1]; LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); while(new_selection && new_selection->isSelected()) @@ -1143,22 +824,22 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL } if (new_selection) { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); + getRoot()->setSelection(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); } else { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); + getRoot()->setSelection(NULL, mParentPanel->hasFocus()); } for(S32 i = 0; i < count; ++i) { - listener = items[i]->getListener(); - if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL)) + listener = items[i]->getViewModelItem(); + if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewModelItem*>::FAIL)) { listeners.put(listener); } } - listener = listeners.get(0); + listener = static_cast<LLFolderViewModelItem*>(listeners.get(0)); if(listener) { listener->removeBatch(listeners); @@ -1169,81 +850,75 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL } } +// TODO RN: abstract // open the selected item. void LLFolderView::openSelectedItems( void ) { - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - mSelectedItems.front()->openItem(); - } - else - { - LLMultiPreview* multi_previewp = new LLMultiPreview(); - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - // IT_{OBJECT,ATTACHMENT} creates LLProperties - // floaters; others create LLPreviews. Put - // each one in the right type of container. - LLFolderViewEventListener* listener = (*item_it)->getListener(); - bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); - if (is_prop) - LLFloater::setFloaterHost(multi_propertiesp); - else - LLFloater::setFloaterHost(multi_previewp); - (*item_it)->openItem(); - } - - LLFloater::setFloaterHost(NULL); - // *NOTE: LLMulti* will safely auto-delete when open'd - // without any children. - multi_previewp->openFloater(LLSD()); - multi_propertiesp->openFloater(LLSD()); - } - } + //TODO RN: get working again + //if(getVisible() && getEnabled()) + //{ + // if (mSelectedItems.size() == 1) + // { + // mSelectedItems.front()->openItem(); + // } + // else + // { + // LLMultiPreview* multi_previewp = new LLMultiPreview(); + // LLMultiProperties* multi_propertiesp = new LLMultiProperties(); + + // selected_items_t::iterator item_it; + // for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + // { + // // IT_{OBJECT,ATTACHMENT} creates LLProperties + // // floaters; others create LLPreviews. Put + // // each one in the right type of container. + // LLFolderViewModelItemInventory* listener = static_cast<LLFolderViewModelItemInventory*>((*item_it)->getViewModelItem()); + // bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); + // if (is_prop) + // LLFloater::setFloaterHost(multi_propertiesp); + // else + // LLFloater::setFloaterHost(multi_previewp); + // listener->openItem(); + // } + + // LLFloater::setFloaterHost(NULL); + // // *NOTE: LLMulti* will safely auto-delete when open'd + // // without any children. + // multi_previewp->openFloater(LLSD()); + // multi_propertiesp->openFloater(LLSD()); + // } + //} } void LLFolderView::propertiesSelectedItems( void ) { - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - LLFolderViewItem* folder_item = mSelectedItems.front(); - if(!folder_item) return; - folder_item->getListener()->showProperties(); - } - else - { - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); + //TODO RN: get working again + //if(getVisible() && getEnabled()) + //{ + // if (mSelectedItems.size() == 1) + // { + // LLFolderViewItem* folder_item = mSelectedItems.front(); + // if(!folder_item) return; + // folder_item->getViewModelItem()->showProperties(); + // } + // else + // { + // LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - LLFloater::setFloaterHost(multi_propertiesp); + // LLFloater::setFloaterHost(multi_propertiesp); - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - (*item_it)->getListener()->showProperties(); - } + // selected_items_t::iterator item_it; + // for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + // { + // (*item_it)->getViewModelItem()->showProperties(); + // } - LLFloater::setFloaterHost(NULL); - multi_propertiesp->openFloater(LLSD()); - } - } + // LLFloater::setFloaterHost(NULL); + // multi_propertiesp->openFloater(LLSD()); + // } + //} } -void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type) -{ - LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get(); - - if (!folder_bridge) return; - LLViewerInventoryCategory *cat = folder_bridge->getCategory(); - if (!cat) return; - cat->changeType(new_folder_type); -} void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) { @@ -1268,7 +943,7 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); item->setOpen(TRUE); - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); scrollToShowItem(item, constraint_rect); } @@ -1329,7 +1004,7 @@ BOOL LLFolderView::canCopy() const for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) { const LLFolderViewItem* item = *selected_it; - if (!item->getListener()->isItemCopyable()) + if (!item->getViewModelItem()->isItemCopyable()) { return FALSE; } @@ -1345,11 +1020,11 @@ void LLFolderView::copy() S32 count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { - LLFolderViewEventListener* listener = NULL; + LLFolderViewModelItem* listener = NULL; selected_items_t::iterator item_it; for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - listener = (*item_it)->getListener(); + listener = (*item_it)->getViewModelItem(); if(listener) { listener->copyToClipboard(); @@ -1369,7 +1044,7 @@ BOOL LLFolderView::canCut() const for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) { const LLFolderViewItem* item = *selected_it; - const LLFolderViewEventListener* listener = item->getListener(); + const LLFolderViewModelItem* listener = item->getViewModelItem(); if (!listener || !listener->isItemRemovable()) { @@ -1386,17 +1061,17 @@ void LLFolderView::cut() S32 count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { - LLFolderViewEventListener* listener = NULL; + LLFolderViewModelItem* listener = NULL; selected_items_t::iterator item_it; for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - listener = (*item_it)->getListener(); + listener = (*item_it)->getViewModelItem(); if(listener) { listener->cutToClipboard(); + listener->removeItem(); } } - LLFolderView::removeCutItems(); } mSearchString.clear(); } @@ -1415,11 +1090,11 @@ BOOL LLFolderView::canPaste() const { // *TODO: only check folders and parent folders of items const LLFolderViewItem* item = (*item_it); - const LLFolderViewEventListener* listener = item->getListener(); + const LLFolderViewModelItem* listener = item->getViewModelItem(); if(!listener || !listener->isClipboardPasteable()) { const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getListener(); + listener = folderp->getViewModelItem(); if (!listener || !listener->isClipboardPasteable()) { return FALSE; @@ -1437,24 +1112,24 @@ void LLFolderView::paste() if(getVisible() && getEnabled()) { // find set of unique folders to paste into - std::set<LLFolderViewItem*> folder_set; + std::set<LLFolderViewFolder*> folder_set; selected_items_t::iterator selected_it; for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) { LLFolderViewItem* item = *selected_it; - LLFolderViewEventListener* listener = item->getListener(); - if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) + LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(item); + if (folder == NULL) { - item = item->getParentFolder(); + folder = item->getParentFolder(); } - folder_set.insert(item); + folder_set.insert(folder); } - std::set<LLFolderViewItem*>::iterator set_iter; + std::set<LLFolderViewFolder*>::iterator set_iter; for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) { - LLFolderViewEventListener* listener = (*set_iter)->getListener(); + LLFolderViewModelItem* listener = (*set_iter)->getViewModelItem(); if(listener && listener->isClipboardPasteable()) { listener->pasteFromClipboard(); @@ -1476,8 +1151,8 @@ void LLFolderView::startRenamingSelectedItem( void ) { item = mSelectedItems.front(); } - if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && - item->getListener()->isItemRenameable()) + if(getVisible() && getEnabled() && (count == 1) && item && item->getViewModelItem() && + item->getViewModelItem()->isItemRenameable()) { mRenameItem = item; @@ -1490,7 +1165,7 @@ void LLFolderView::startRenamingSelectedItem( void ) // set focus will fail unless item is visible mRenamer->setFocus( TRUE ); mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); - gViewerWindow->addPopup(mRenamer); + LLUI::addPopup(mRenamer); } } @@ -1548,25 +1223,37 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) case KEY_PAGE_UP: mSearchString.clear(); - mScrollContainer->pageUp(30); + if (mScrollContainer) + { + mScrollContainer->pageUp(30); + } handled = TRUE; break; case KEY_PAGE_DOWN: mSearchString.clear(); - mScrollContainer->pageDown(30); + if (mScrollContainer) + { + mScrollContainer->pageDown(30); + } handled = TRUE; break; case KEY_HOME: mSearchString.clear(); - mScrollContainer->goToTop(); + if (mScrollContainer) + { + mScrollContainer->goToTop(); + } handled = TRUE; break; case KEY_END: mSearchString.clear(); - mScrollContainer->goToBottom(); + if (mScrollContainer) + { + mScrollContainer->goToBottom(); + } break; case KEY_DOWN: @@ -1590,12 +1277,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (next->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + getRoot()->changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == next->getParentFolder()) { // grow selection - changeSelectionFromRoot(next, TRUE); + getRoot()->changeSelection(next, TRUE); } } } @@ -1654,12 +1341,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (prev->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + getRoot()->changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == prev->getParentFolder()) { // grow selection - changeSelectionFromRoot(prev, TRUE); + getRoot()->changeSelection(prev, TRUE); } } } @@ -1762,7 +1449,7 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) } //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) + if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout")) { mSearchString.clear(); } @@ -1786,7 +1473,7 @@ BOOL LLFolderView::canDoDelete() const for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - if (!(*item_it)->getListener()->isItemRemovable()) + if (!(*item_it)->getViewModelItem()->isItemRemovable()) { return FALSE; } @@ -1853,7 +1540,7 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc } } - const std::string current_item_label(search_item->getSearchableLabel()); + const std::string current_item_label(search_item->getViewModelItem()->getSearchableName()); S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); if (!current_item_label.compare(0, search_string_length, upper_case_string)) { @@ -1897,7 +1584,7 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) S32 count = mSelectedItems.size(); LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); if ( handled - && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible + && ( count > 0 && (hasVisibleChildren()) ) // show menu only if selected items are visible && menu ) { if (mCallbackRegistrar) @@ -1972,23 +1659,7 @@ BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // by the folder which is the hierarchy root. if (!handled) { - if (getListener()->getUUID().notNull()) - { - handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - else - { - if (!mFolders.empty()) - { - // dispatch to last folder as a hack to support "Contents" folder in object inventory - handled = mFolders.back()->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); - } - } - } - - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; + handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } return handled; @@ -2058,8 +1729,8 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr LLRect LLFolderView::getVisibleRect() { - S32 visible_height = mScrollContainer->getRect().getHeight(); - S32 visible_width = mScrollContainer->getRect().getWidth(); + S32 visible_height = (mScrollContainer ? mScrollContainer->getRect().getHeight() : 0); + S32 visible_width = (mScrollContainer ? mScrollContainer->getRect().getWidth() : 0); LLRect visible_rect; visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); return visible_rect; @@ -2088,161 +1759,24 @@ void LLFolderView::setShowSingleSelection(BOOL show) } } -void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) -{ - mItemMap[id] = itemp; -} - -void LLFolderView::removeItemID(const LLUUID& id) -{ - mItemMap.erase(id); -} - -LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); -LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) -{ - LLFastTimer _(FTM_GET_ITEM_BY_ID); - if (id == getListener()->getUUID()) - { - return this; - } - - std::map<LLUUID, LLFolderViewItem*>::iterator map_it; - map_it = mItemMap.find(id); - if (map_it != mItemMap.end()) - { - return map_it->second; - } - - return NULL; -} - -LLFolderViewFolder* LLFolderView::getFolderByID(const LLUUID& id) -{ - if (id == getListener()->getUUID()) - { - return this; - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder *folder = (*iter); - if (folder->getListener()->getUUID() == id) - { - return folder; - } - } - return NULL; -} - -bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) -{ - std::string action = userdata.asString(); - - if ("rename" == action) - { - startRenamingSelectedItem(); - return true; - } - if ("delete" == action) - { - removeSelectedItems(); - return true; - } - if (("copy" == action) || ("cut" == action)) - { - // Clear the clipboard before we start adding things on it - LLClipboard::instance().reset(); - } - - static const std::string change_folder_string = "change_folder_type_"; - if (action.length() > change_folder_string.length() && - (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) - { - LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); - changeType(model, new_folder_type); - return true; - } - - - std::set<LLUUID> selected_items = getSelectionList(); - - LLMultiPreview* multi_previewp = NULL; - LLMultiProperties* multi_propertiesp = NULL; - - if (("task_open" == action || "open" == action) && selected_items.size() > 1) - { - multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - - } - else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) - { - multi_propertiesp = new LLMultiProperties(); - gFloaterView->addChild(multi_propertiesp); - - LLFloater::setFloaterHost(multi_propertiesp); - } - - std::set<LLUUID>::iterator set_iter; - - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = getItemByID(*set_iter); - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) continue; - bridge->performAction(model, action); - } - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } - else if (multi_propertiesp) - { - multi_propertiesp->openFloater(LLSD()); - } - - return true; -} - static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select"); static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory"); // Main idle routine -void LLFolderView::doIdle() +void LLFolderView::update() { // If this is associated with the user's inventory, don't do anything // until that inventory is loaded up. - const LLInventoryPanel *inventory_panel = dynamic_cast<LLInventoryPanel*>(mParentPanel); - if (inventory_panel && !inventory_panel->getIsViewsInitialized()) - { - return; - } - LLFastTimer t2(FTM_INVENTORY); - BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); - if (debug_filters != getDebugFilters()) - { - mDebugFilters = debug_filters; - arrangeAll(); - } - - if (mFilter->isModified() && mFilter->isNotDefault()) + if (getFolderViewModel()->getFilter()->isModified() && getFolderViewModel()->getFilter()->isNotDefault()) { mNeedsAutoSelect = TRUE; } - mFilter->clearModified(); + getFolderViewModel()->getFilter()->clearModified(); // filter to determine visibility before arranging - filterFromRoot(); + filter(*(getFolderViewModel()->getFilter())); // automatically show matching items, and select first one if we had a selection if (mNeedsAutoSelect) @@ -2250,7 +1784,7 @@ void LLFolderView::doIdle() LLFastTimer t3(FTM_AUTO_SELECT); // select new item only if a filtered item not currently selected LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->potentiallyFiltered())) + if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->getViewModelItem()->potentiallyVisible())) { // these are named variables to get around gcc not binding non-const references to rvalues // and functor application is inherently non-const to allow for stateful functors @@ -2260,7 +1794,7 @@ void LLFolderView::doIdle() // Open filtered folders for folder views with mAutoSelectOverride=TRUE. // Used by LLPlacesFolderView. - if (mAutoSelectOverride && !mFilter->getFilterSubString().empty()) + if (getFolderViewModel()->getFilter()->showAllResults()) { // these are named variables to get around gcc not binding non-const references to rvalues // and functor application is inherently non-const to allow for stateful functors @@ -2271,11 +1805,11 @@ void LLFolderView::doIdle() scrollToShowSelection(); } - BOOL filter_finished = mCompletedFilterGeneration >= mFilter->getCurrentGeneration() - && !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); + BOOL filter_finished = getViewModelItem()->passedFilter() + && mViewModel->contentsReady(); if (filter_finished - || gFocusMgr.childHasKeyboardFocus(inventory_panel) - || gFocusMgr.childHasMouseCapture(inventory_panel)) + || gFocusMgr.childHasKeyboardFocus(mParentPanel) + || gFocusMgr.childHasMouseCapture(mParentPanel)) { // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process mNeedsAutoSelect = FALSE; @@ -2292,7 +1826,7 @@ void LLFolderView::doIdle() // lets pin it! mPinningSelectedItem = TRUE; - LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); + LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect()); LLFolderViewItem* selected_item = mSelectedItems.back(); LLRect item_rect; @@ -2307,7 +1841,7 @@ void LLFolderView::doIdle() else { // otherwise we just want it onscreen somewhere - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); } } @@ -2330,11 +1864,10 @@ void LLFolderView::doIdle() else { // during normal use (page up/page down, etc), just try to fit item on screen - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); } - BOOL is_visible = isInVisibleChain(); if ( is_visible ) @@ -2342,7 +1875,10 @@ void LLFolderView::doIdle() sanitizeSelection(); if( needsArrange() ) { - arrangeFromRoot(); + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); } } @@ -2366,17 +1902,6 @@ void LLFolderView::doIdle() mSignalSelectCallback = FALSE; } - -//static -void LLFolderView::idle(void* user_data) -{ - LLFolderView* self = (LLFolderView*)user_data; - if ( self ) - { // Do the real idle - self->doIdle(); - } -} - void LLFolderView::dumpSelectionInformation() { llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; @@ -2400,7 +1925,7 @@ void LLFolderView::updateRenamerPosition() screenPointToLocal( x, y, &x, &y ); mRenamer->setOrigin( x, y ); - LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidthScaled(), 0); + LLRect scroller_rect(0, 0, (S32)LLUI::getWindowSize().mV[VX], 0); if (mScrollContainer) { scroller_rect = mScrollContainer->getContentWindowRect(); @@ -2547,77 +2072,17 @@ void LLFolderView::onRenamerLost() if( mRenameItem ) { - setSelectionFromRoot( mRenameItem, TRUE ); + setSelection( mRenameItem, TRUE ); mRenameItem = NULL; } } -LLInventoryFilter* LLFolderView::getFilter() -{ - return mFilter; -} - -void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) -{ - mFilter->setFilterPermissions(filter_perm_mask); -} - -U32 LLFolderView::getFilterObjectTypes() const -{ - return mFilter->getFilterObjectTypes(); -} - -PermissionMask LLFolderView::getFilterPermissions() const -{ - return mFilter->getFilterPermissions(); -} - -BOOL LLFolderView::isFilterModified() -{ - return mFilter->isNotDefault(); -} - -void delete_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->removeSelectedItems(); - } -} - -void copy_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->copy(); - } -} - -void paste_items(void* user_data) +S32 LLFolderView::getItemHeight() { - if(user_data) + if(!hasVisibleChildren()) { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->paste(); - } -} - -void open_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->openSelectedItems(); - } -} - -void properties_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->propertiesSelectedItems(); + //We need to display status textbox, let's reserve some place for it + return llmax(0, mStatusTextBox->getTextPixelHeight()); } + return 0; } diff --git a/indra/newview/llfolderview.h b/indra/llui/llfolderview.h index da8bb15f8e..ba37a11bbe 100644 --- a/indra/newview/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -39,19 +39,16 @@ #include "lluictrl.h" #include "v4color.h" -#include "lldarray.h" #include "stdenums.h" #include "lldepthstack.h" #include "lleditmenuhandler.h" #include "llfontgl.h" #include "llscrollcontainer.h" -#include "lltooldraganddrop.h" -#include "llviewertexture.h" -class LLFolderViewEventListener; +class LLFolderViewModelInterface; class LLFolderViewFolder; class LLFolderViewItem; -class LLInventoryModel; +class LLFolderViewFilter; class LLPanel; class LLLineEditor; class LLMenuGL; @@ -90,13 +87,13 @@ public: struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> { Mandatory<LLPanel*> parent_panel; - Optional<LLUUID> task_id; Optional<std::string> title; Optional<bool> use_label_suffix, allow_multiselect, show_empty_message, - show_load_status, - use_ellipses; + use_ellipses, + show_item_link_overlays; + Mandatory<LLFolderViewModelInterface*> view_model; Params(); }; @@ -108,91 +105,71 @@ public: virtual BOOL canFocusChildren() const; + virtual const LLFolderView* getRoot() const { return this; } virtual LLFolderView* getRoot() { return this; } - // FolderViews default to sort by name. This will change that, - // and resort the items if necessary. - void setSortOrder(U32 order); - void setFilterPermMask(PermissionMask filter_perm_mask); - + LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; } + const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; } + 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); } - // filter is never null - LLInventoryFilter* getFilter(); - const std::string getFilterSubString(BOOL trim = FALSE); - U32 getFilterObjectTypes() const; - PermissionMask getFilterPermissions() const; - // *NOTE: use getFilter()->getShowFolderState(); - //LLInventoryFilter::EFolderShow getShowFolderState(); - U32 getSortOrder() const; - BOOL isFilterModified(); - bool getAllowMultiSelect() { return mAllowMultiSelect; } // Close all folders in the view void closeAllFolders(); void openTopLevelFolders(); - virtual void toggleOpen() {}; - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse); virtual BOOL addFolder( LLFolderViewFolder* folder); // Find width and height of this object and its children. Also // makes sure that this view and its children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); + virtual S32 arrange( S32* width, S32* height ); + virtual S32 getItemHeight(); void arrangeAll() { mArrangeGeneration++; } S32 getArrangeGeneration() { return mArrangeGeneration; } - // Apply filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); + // applies filters to control visibility of items + virtual void filter( LLFolderViewFilter& filter); // Get the last selected item virtual LLFolderViewItem* getCurSelectedItem( void ); // Record the selected item and pass it down the hierarchy. virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus); - - // Used by menu callbacks - void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); - - // Called once a frame to update the selection if mSelectThisID has been set - void updateSelection(); + BOOL take_keyboard_focus = TRUE); - // This method is used to toggle the selection of an item. - // Walks children and keeps track of selected objects. + // This method is used to toggle the selection of an item. Walks + // children, and keeps track of selected objects. virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - virtual std::set<LLUUID> getSelectionList() const; + virtual std::set<LLFolderViewItem*> getSelectionList() const; + S32 getNumSelectedItems() { return mSelectedItems.size(); } - // Make sure if ancestor is selected, descendents are not + // Make sure if ancestor is selected, descendants are not void sanitizeSelection(); - void clearSelection(); + virtual void clearSelection(); void addToSelectionList(LLFolderViewItem* item); void removeFromSelectionList(LLFolderViewItem* item); - BOOL startDrag(LLToolDragAndDrop::ESource source); + bool startDrag(); void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } // Deletion functionality void removeSelectedItems(); - static void removeCutItems(); // Open the selected item void openSelectedItems( void ); void propertiesSelectedItems( void ); - // Change the folder type - void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type); - void autoOpenItem(LLFolderViewFolder* item); void closeAutoOpenedFolders(); BOOL autoOpenTest(LLFolderViewFolder* item); + BOOL isOpen() const { return TRUE; } // root folder always open // Copy & paste virtual void copy(); @@ -210,14 +187,6 @@ public: // Public rename functionality - can only start the process void startRenamingSelectedItem( void ); - // These functions were used when there was only one folderview, - // and relied on that concept. This functionality is now handled - // by the listeners and the lldraganddroptool. - //LLFolderViewItem* getMovingItem() { return mMovingItem; } - //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; } - //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - // LLView functionality ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); @@ -249,15 +218,7 @@ public: F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } bool getUseEllipses() { return mUseEllipses; } - void addItemID(const LLUUID& id, LLFolderViewItem* itemp); - void removeItemID(const LLUUID& id); - LLFolderViewItem* getItemByID(const LLUUID& id); - LLFolderViewFolder* getFolderByID(const LLUUID& id); - - bool doToSelected(LLInventoryModel* model, const LLSD& userdata); - - void doIdle(); // Real idle routine - static void idle(void* user_data); // static glue to doIdle() + void update(); // needs to be called periodically (e.g. once per frame) BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; } BOOL needsAutoRename() { return mNeedsAutoRename; } @@ -265,9 +226,9 @@ public: void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; } void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; } - void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } + bool showItemLinkOverlays() { return mShowItemLinkOverlays; } - BOOL getDebugFilters() { return mDebugFilters; } + void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } LLPanel* getParentPanel() { return mParentPanel; } // DEBUG only @@ -296,7 +257,6 @@ protected: BOOL addNoOptions(LLMenuGL* menu) const; - void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response); protected: LLHandle<LLView> mPopupMenuHandle; @@ -307,7 +267,6 @@ protected: BOOL mAllowMultiSelect; BOOL mShowEmptyMessage; BOOL mShowFolderHierarchy; - LLUUID mSourceID; // Renaming variables and methods LLFolderViewItem* mRenameItem; // The item currently being renamed @@ -320,15 +279,13 @@ protected: BOOL mAutoSelectOverride; BOOL mNeedsAutoRename; bool mUseLabelSuffix; + bool mShowItemLinkOverlays; - BOOL mDebugFilters; - U32 mSortOrder; LLDepthStack<LLFolderViewFolder> mAutoOpenItems; LLFolderViewFolder* mAutoOpenCandidate; LLFrameTimer mAutoOpenTimer; LLFrameTimer mSearchTimer; std::string mSearchString; - LLInventoryFilter* mFilter; BOOL mShowSelectionContext; BOOL mShowSingleSelection; LLFrameTimer mMultiSelectionFadeTimer; @@ -338,13 +295,11 @@ protected: signal_t mReshapeSignal; S32 mSignalSelectCallback; S32 mMinWidth; - S32 mRunningHeight; - std::map<LLUUID, LLFolderViewItem*> mItemMap; BOOL mDragAndDropThisFrame; - LLUUID mSelectThisID; // if non null, select this item - LLPanel* mParentPanel; + + LLFolderViewModelInterface* mViewModel; /** * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. @@ -365,8 +320,66 @@ public: }; -bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b); -bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFolderViewFunctor +// +// Simple abstract base class for applying a functor to folders and +// items in a folder view hierarchy. This is suboptimal for algorithms +// that only work folders or only work on items, but I'll worry about +// that later when it's determined to be too slow. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFolderViewFunctor +{ +public: + virtual ~LLFolderViewFunctor() {} + virtual void doFolder(LLFolderViewFolder* folder) = 0; + virtual void doItem(LLFolderViewItem* item) = 0; +}; + +class LLSelectFirstFilteredItem : public LLFolderViewFunctor +{ +public: + LLSelectFirstFilteredItem() : mItemSelected(FALSE) {} + virtual ~LLSelectFirstFilteredItem() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); + BOOL wasItemSelected() { return mItemSelected; } +protected: + BOOL mItemSelected; +}; + +class LLOpenFilteredFolders : public LLFolderViewFunctor +{ +public: + LLOpenFilteredFolders() {} + virtual ~LLOpenFilteredFolders() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; + +class LLSaveFolderState : public LLFolderViewFunctor +{ +public: + LLSaveFolderState() : mApply(FALSE) {} + virtual ~LLSaveFolderState() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item) {} + void setApply(BOOL apply); + void clearOpenFolders() { mOpenFolders.clear(); } +protected: + std::set<LLUUID> mOpenFolders; + BOOL mApply; +}; + +class LLOpenFoldersWithSelection : public LLFolderViewFunctor +{ +public: + LLOpenFoldersWithSelection() {} + virtual ~LLOpenFoldersWithSelection() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; // Flags for buildContextMenu() const U32 SUPPRESS_OPEN_ITEM = 0x1; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 515e544452..cd8d8bafbc 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -23,26 +23,17 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -#include "llviewerprecompiledheaders.h" - +#include "linden_common.h" #include "llfolderviewitem.h" -// viewer includes -#include "llfolderview.h" // Items depend extensively on LLFolderViews -#include "llfoldervieweventlistener.h" -#include "llviewerfoldertype.h" -#include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator() -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" +#include "llfolderview.h" +#include "llfolderviewmodel.h" #include "llpanel.h" -#include "llviewercontrol.h" // gSavedSettings -#include "llviewerwindow.h" // Argh, only for setCursor() - -// linden library includes +#include "llcriticaldamp.h" #include "llclipboard.h" #include "llfocusmgr.h" // gFocusMgr #include "lltrans.h" +#include "llwindow.h" ///---------------------------------------------------------------------------- /// Class LLFolderViewItem @@ -91,10 +82,7 @@ void LLFolderViewItem::cleanupClass() // NOTE: Optimize this, we call it a *lot* when opening a large inventory LLFolderViewItem::Params::Params() -: icon(), - icon_open(), - icon_overlay(), - root(), +: root(), listener(), folder_arrow_image("folder_arrow_image"), folder_indentation("folder_indentation"), @@ -117,22 +105,19 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mHasVisibleChildren(FALSE), mIndentation(0), mItemHeight(p.item_height), - mPassedFilter(FALSE), - mLastFilterGeneration(-1), - mStringMatchOffset(std::string::npos), + //TODO RN: create interface for string highlighting + //mStringMatchOffset(std::string::npos), mControlLabelRotation(0.f), mDragAndDropTarget(FALSE), - mIsLoading(FALSE), mLabel(p.name), mRoot(p.root), - mCreationDate(p.creation_date), - mIcon(p.icon), - mIconOpen(p.icon_open), - mIconOverlay(p.icon_overlay), - mListener(p.listener), - mShowLoadStatus(false), + mViewModelItem(p.listener), mIsMouseOverTitle(false) { + if (mViewModelItem) + { + mViewModelItem->setFolderViewItem(this); + } } BOOL LLFolderViewItem::postBuild() @@ -144,8 +129,8 @@ BOOL LLFolderViewItem::postBuild() // Destroys the object LLFolderViewItem::~LLFolderViewItem( void ) { - delete mListener; - mListener = NULL; + delete mViewModelItem; + mViewModelItem = NULL; } LLFolderView* LLFolderViewItem::getRoot() @@ -153,6 +138,10 @@ LLFolderView* LLFolderViewItem::getRoot() return mRoot; } +const LLFolderView* LLFolderViewItem::getRoot() const +{ + return mRoot; +} // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) { @@ -214,146 +203,47 @@ LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) return itemp; } -// is this item something we think we should be showing? -// for example, if we haven't gotten around to filtering it yet, then the answer is yes -// until we find out otherwise -BOOL LLFolderViewItem::potentiallyVisible() -{ - // we haven't been checked against min required filter - // or we have and we passed - return potentiallyFiltered(); -} - -BOOL LLFolderViewItem::potentiallyFiltered() -{ - return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered(); -} - -BOOL LLFolderViewItem::getFiltered() -{ - return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -BOOL LLFolderViewItem::getFiltered(S32 filter_generation) -{ - return mPassedFilter && mLastFilterGeneration >= filter_generation; -} - -void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation) +BOOL LLFolderViewItem::passedFilter(S32 filter_generation) { - mPassedFilter = filtered; - mLastFilterGeneration = filter_generation; + return getViewModelItem()->passedFilter(filter_generation); } -void LLFolderViewItem::setIcon(LLUIImagePtr icon) +void LLFolderViewItem::refresh() { - mIcon = icon; -} + LLFolderViewModelItem& vmi = *getViewModelItem(); -// refresh information from the listener -void LLFolderViewItem::refreshFromListener() -{ - if(mListener) - { - mLabel = mListener->getDisplayName(); - LLFolderType::EType preferred_type = mListener->getPreferredType(); + mLabel = vmi.getDisplayName(); - // *TODO: to be removed when database supports multi language. This is a - // temporary attempt to display the inventory folder in the user locale. - // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID - // it uses the same way to find localized string - - // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) - // Translation of Accessories folder in Library inventory folder - bool accessories = false; - if(mLabel == std::string("Accessories")) - { - //To ensure that Accessories folder is in Library we have to check its parent folder. - //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model - LLInventoryCategory* cat = gInventory.getCategory(mListener->getUUID()); - if(cat) - { - const LLUUID& parent_folder_id = cat->getParentUUID(); - accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); - } - } - - //"Accessories" inventory category has folder type FT_NONE. So, this folder - //can not be detected as protected with LLFolderType::lookupIsProtectedType - if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) - { - LLTrans::findString(mLabel, "InvFolder " + mLabel); - }; + setToolTip(mLabel); + mIcon = vmi.getIcon(); + mIconOpen = vmi.getIconOpen(); + mIconOverlay = vmi.getIconOverlay(); - setToolTip(mLabel); - setIcon(mListener->getIcon()); - time_t creation_date = mListener->getCreationDate(); - if ((creation_date > 0) && (mCreationDate != creation_date)) - { - setCreationDate(creation_date); - dirtyFilter(); - } if (mRoot->useLabelSuffix()) { - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); - } - } + mLabelStyle = vmi.getLabelStyle(); + mLabelSuffix = vmi.getLabelSuffix(); } -void LLFolderViewItem::refresh() -{ - refreshFromListener(); + //TODO RN: make sure this logic still fires + //std::string searchable_label(mLabel); + //searchable_label.append(mLabelSuffix); + //LLStringUtil::toUpper(searchable_label); - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::toUpper(searchable_label); - - if (mSearchableLabel.compare(searchable_label)) - { - mSearchableLabel.assign(searchable_label); - dirtyFilter(); - // some part of label has changed, so overall width has potentially changed, and sort order too - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } - } + //if (mSearchableLabel.compare(searchable_label)) + //{ + // mSearchableLabel.assign(searchable_label); + // vmi.dirtyFilter(); + // // some part of label has changed, so overall width has potentially changed, and sort order too + // if (mParentFolder) + // { + // mParentFolder->requestSort(); + // mParentFolder->requestArrange(); + // } + //} mLabelWidthDirty = true; -} - -void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); -} - -// This function is called when items are added or view filters change. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::filterFromRoot( void ) -{ - LLFolderViewItem* root = getRoot(); - - root->filter(*((LLFolderView*)root)->getFilter()); -} - -// This function is called when the folder view is dirty. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::arrangeFromRoot() -{ - LLFolderViewItem* root = getRoot(); - - S32 height = 0; - S32 width = 0; - S32 total_height = root->arrange( &width, &height, 0 ); - - LLSD params; - params["action"] = "size_changes"; - params["height"] = total_height; - getParent()->notifyParent(params); + vmi.dirtyFilter(); } // Utility function for LLFolderView @@ -367,7 +257,7 @@ void LLFolderViewItem::arrangeAndSet(BOOL set_selection, } if(set_selection) { - setSelectionFromRoot(this, TRUE, take_keyboard_focus); + getRoot()->setSelection(this, TRUE, take_keyboard_focus); if(root) { root->scrollToShowSelection(); @@ -375,57 +265,29 @@ void LLFolderViewItem::arrangeAndSet(BOOL set_selection, } } -// This function clears the currently selected item, and records the -// specified selected item appropriately for display and use in the -// UI. If open is TRUE, then folders are opened up along the way to -// the selection. -void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, - BOOL openitem, - BOOL take_keyboard_focus) -{ - getRoot()->setSelection(selection, openitem, take_keyboard_focus); -} -// helper function to change the selection from the root. -void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected) +std::set<LLFolderViewItem*> LLFolderViewItem::getSelectionList() const { - getRoot()->changeSelection(selection, selected); -} - -std::set<LLUUID> LLFolderViewItem::getSelectionList() const -{ - std::set<LLUUID> selection; + std::set<LLFolderViewItem*> selection; return selection; } -EInventorySortGroup LLFolderViewItem::getSortGroup() const -{ - return SG_ITEM; -} - // addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) { - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); return folder->addItem(this); } // Finds width and height of this object and its children. Also // makes sure that this view and its children are the right size. -S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) +S32 LLFolderViewItem::arrange( S32* width, S32* height ) { const Params& p = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); S32 indentation = p.folder_indentation(); // Only indent deeper items in hierarchy - mIndentation = (getParentFolder() - && getParentFolder()->getParentFolder() ) - ? mParentFolder->getIndentation() + indentation + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + indentation : 0; if (mLabelWidthDirty) { @@ -451,40 +313,6 @@ S32 LLFolderViewItem::getItemHeight() return mItemHeight; } -void LLFolderViewItem::filter( LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFilter; - const BOOL passed_filter = filter.check(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - mParentFolder->requestArrange(); - } - - setFiltered(passed_filter, filter.getCurrentGeneration()); - mStringMatchOffset = filter.getStringMatchOffset(); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewItem::dirtyFilter() -{ - mLastFilterGeneration = -1; - // bubble up dirty flag all the way to root - if (getParentFolder()) - { - getParentFolder()->setCompletedFilterGeneration(-1, TRUE); - } -} - // *TODO: This can be optimized a lot by simply recording that it is // selected in the appropriate places, and assuming that set selection // means 'deselect' for a leaf item. Do this optimization after @@ -529,45 +357,31 @@ void LLFolderViewItem::selectItem(void) { if (mIsSelected == FALSE) { - if (mListener) - { - mListener->selectItem(); - } + getViewModelItem()->selectItem(); mIsSelected = TRUE; } } BOOL LLFolderViewItem::isMovable() { - if( mListener ) - { - return mListener->isItemMovable(); - } - else - { - return TRUE; - } + return getViewModelItem()->isItemMovable(); } BOOL LLFolderViewItem::isRemovable() { - if( mListener ) - { - return mListener->isItemRemovable(); - } - else - { - return TRUE; + return getViewModelItem()->isItemRemovable(); } -} void LLFolderViewItem::destroyView() { + getRoot()->removeFromSelectionList(this); + if (mParentFolder) { // removeView deletes me - mParentFolder->removeView(this); + mParentFolder->extractItem(this); } + delete this; } // Call through to the viewed object and return true if it can be @@ -579,45 +393,25 @@ BOOL LLFolderViewItem::remove() { return FALSE; } - if(mListener) - { - return mListener->removeItem(); + return getViewModelItem()->removeItem(); } - return TRUE; -} // Build an appropriate context menu for the item. void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) { - if(mListener) - { - mListener->buildContextMenu(menu, flags); - } + getViewModelItem()->buildContextMenu(menu, flags); } void LLFolderViewItem::openItem( void ) { - if( mListener ) - { - mListener->openItem(); - } -} - -void LLFolderViewItem::preview( void ) -{ - if (mListener) - { - mListener->previewItem(); - } + getViewModelItem()->openItem(); } void LLFolderViewItem::rename(const std::string& new_name) { if( !new_name.empty() ) { - if( mListener ) - { - mListener->renameItem(new_name); + getViewModelItem()->renameItem(new_name); if(mParentFolder) { @@ -625,26 +419,10 @@ void LLFolderViewItem::rename(const std::string& new_name) } } } -} - -const std::string& LLFolderViewItem::getSearchableLabel() const -{ - return mSearchableLabel; -} - -LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void) -{ - if (!getListener()) return NULL; - return gInventory.getItem(getListener()->getUUID()); -} const std::string& LLFolderViewItem::getName( void ) const { - if(mListener) - { - return mListener->getName(); - } - return mLabel; + return getViewModelItem()->getName(); } // LLView functionality @@ -652,7 +430,7 @@ BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) { if(!mIsSelected) { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } make_ui_sound("UISndClick"); return TRUE; @@ -673,7 +451,7 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) { if(mask & MASK_CONTROL) { - changeSelectionFromRoot(this, !mIsSelected); + getRoot()->changeSelection(this, !mIsSelected); } else if (mask & MASK_SHIFT) { @@ -681,7 +459,7 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) } else { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } make_ui_sound("UISndClick"); } @@ -690,51 +468,25 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) mSelectPending = TRUE; } - if( isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); - } + mDragStartX = x; + mDragStartY = y; return TRUE; } BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) { + static LLCachedControl<S32> drag_and_drop_threshold(*LLUI::sSettingGroups["config"],"DragAndDropDistanceThreshold"); + mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); if( hasMouseCapture() && isMovable() ) { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - BOOL can_drag = TRUE; - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { LLFolderView* root = getRoot(); - if(root->getCurSelectedItem()) - { - LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; - - // *TODO: push this into listener and remove - // dependency on llagent - if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getRootFolderID())) - { - src = LLToolDragAndDrop::SOURCE_AGENT; - } - else if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getLibraryRootFolderID())) + if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold() + && root->getCurSelectedItem() + && root->startDrag()) { - src = LLToolDragAndDrop::SOURCE_LIBRARY; - } - - can_drag = root->startDrag(src); - if (can_drag) - { - // if (mListener) mListener->startDrag(); // RN: when starting drag and drop, clear out last auto-open root->autoOpenTest(NULL); root->setShowSelectionContext(TRUE); @@ -744,25 +496,19 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) // item. gFocusMgr.setKeyboardFocus(NULL); - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } + getWindow()->setCursor(UI_CURSOR_ARROW); + return TRUE; } - } - - if (can_drag) - { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } else { - gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); + getWindow()->setCursor(UI_CURSOR_NOLOCKED); + return TRUE; } - return TRUE; } else { getRoot()->setShowSelectionContext(FALSE); - gViewerWindow->setCursor(UI_CURSOR_ARROW); + getWindow()->setCursor(UI_CURSOR_ARROW); // let parent handle this then... return FALSE; } @@ -771,7 +517,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) { - preview(); + getViewModelItem()->openItem(); return TRUE; } @@ -788,7 +534,7 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) //...then select if(mask & MASK_CONTROL) { - changeSelectionFromRoot(this, !mIsSelected); + getRoot()->changeSelection(this, !mIsSelected); } else if (mask & MASK_SHIFT) { @@ -796,7 +542,7 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) } else { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } } @@ -804,7 +550,10 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) if( hasMouseCapture() ) { + if (getRoot()) + { getRoot()->setShowSelectionContext(FALSE); + } gFocusMgr.setMouseCapture( NULL ); } return TRUE; @@ -821,11 +570,8 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* accept, std::string& tooltip_msg) { - BOOL accepted = FALSE; BOOL handled = FALSE; - if(mListener) - { - accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); + BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); handled = accepted; if (accepted) { @@ -836,7 +582,6 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, { *accept = ACCEPT_NO; } - } if(mParentFolder && !handled) { // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event. @@ -871,16 +616,12 @@ void LLFolderViewItem::draw() const S32 FOCUS_LEFT = 1; const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID()); - const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID()); + getViewModelItem()->update(); //--------------------------------------------------------------------------------// // Draw open folder arrow // - const bool up_to_date = mListener && mListener->isUpToDate(); - const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) // we fetched our children and some of them have passed the filter... - || (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter) - if (possibly_has_children) + if (hasVisibleChildren() || getViewModelItem()->hasChildren()) { LLUIImage* arrow_image = default_params.folder_arrow_image; gl_draw_scaled_rotated_image( @@ -892,8 +633,8 @@ void LLFolderViewItem::draw() //--------------------------------------------------------------------------------// // Draw highlight for selected items // - const BOOL show_context = getRoot()->getShowSelectionContext(); - const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled + const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); + const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled const S32 focus_top = getRect().getHeight(); const S32 focus_bottom = getRect().getHeight() - mItemHeight; const bool folder_open = (getRect().getHeight() > mItemHeight + 4); @@ -904,8 +645,8 @@ void LLFolderViewItem::draw() if (!mIsCurSelection) { // do time-based fade of extra objects - F32 fade_time = getRoot()->getSelectionFadeElapsedTime(); - if (getRoot()->getShowSingleSelection()) + F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); + if (getRoot() && getRoot()->getShowSingleSelection()) { // fading out bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); @@ -977,8 +718,6 @@ void LLFolderViewItem::draw() mDragAndDropTarget = FALSE; } - const LLViewerInventoryItem *item = getInventoryItem(); - const BOOL highlight_link = mIconOverlay && item && item->getIsLinkType(); //--------------------------------------------------------------------------------// // Draw open icon // @@ -992,7 +731,7 @@ void LLFolderViewItem::draw() mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); } - if (highlight_link) + if (mIconOverlay && getRoot()->showItemLinkOverlays()) { mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); } @@ -1006,31 +745,15 @@ void LLFolderViewItem::draw() } LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; - if (highlight_link) color = sLinkColor; - if (in_library) color = sLibraryColor; + //TODO RN: implement this in terms of getColor() + //if (highlight_link) color = sLinkColor; + //if (gInventory.isObjectDescendentOf(getViewModelItem()->getUUID(), gInventory.getLibraryRootFolderID())) color = sLibraryColor; F32 right_x = 0; F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); //--------------------------------------------------------------------------------// - // Highlight filtered text - // - if (getRoot()->getDebugFilters()) - { - if (!getFiltered() && !possibly_has_children) - { - color.mV[VALPHA] *= 0.5f; - } - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? - LLColor4(0.5f, 0.8f, 0.5f, 1.f) : - LLColor4(0.8f, 0.5f, 0.5f, 1.f); - LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; - } - //--------------------------------------------------------------------------------// // Draw the actual label text // font->renderUTF8(mLabel, 0, text_left, y, color, @@ -1038,30 +761,6 @@ void LLFolderViewItem::draw() S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); //--------------------------------------------------------------------------------// - // Draw "Loading..." text - // - bool root_is_loading = false; - if (in_inventory) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); - } - if (in_library) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); - } - if ((mIsLoading - && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) - || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() - && root_is_loading - && mShowLoadStatus)) - { - std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) "; - font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE); - } - - //--------------------------------------------------------------------------------// // Draw label suffix // if (!mLabelSuffix.empty()) @@ -1074,50 +773,55 @@ void LLFolderViewItem::draw() //--------------------------------------------------------------------------------// // Highlight string match // - if (mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = getRoot()->getFilterSubString().size(); - if (filter_string_length > 0) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; - - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - } + //TODO RN: expose interface for highlighting + //if (mStringMatchOffset != std::string::npos) + //{ + // // don't draw backgrounds for zero-length strings + // S32 filter_string_length = getRoot()->getFilterSubString().size(); + // if (filter_string_length > 0) + // { + // std::string combined_string = mLabel + mLabelSuffix; + // S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; + // S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; + // S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); + // S32 top = getRect().getHeight() - TOP_PAD; + // + // LLUIImage* box_image = default_params.selection_image; + // LLRect box_rect(left, top, right, bottom); + // box_image->draw(box_rect, sFilterBGColor); + // F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); + // F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; + // font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, + // sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + // filter_string_length, S32_MAX, &right_x, FALSE ); + // } + //} } +const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const + { + return getRoot()->getFolderViewModel(); +} + +LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) + { + return getRoot()->getFolderViewModel(); + } + ///---------------------------------------------------------------------------- /// Class LLFolderViewFolder ///---------------------------------------------------------------------------- LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time + LLFolderViewItem( p ), mIsOpen(FALSE), mExpanderHighlighted(FALSE), mCurHeight(0.f), mTargetHeight(0.f), mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false), - mPassedFolderFilter(FALSE) + mLastCalculatedWidth(0) { } @@ -1129,44 +833,26 @@ LLFolderViewFolder::~LLFolderViewFolder( void ) gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() } -void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) -{ - mPassedFolderFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) -{ - return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - // addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) { - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); return folder->addFolder(this); } +static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); + // Finds width and height of this object and its children. Also // makes sure that this view and its children are the right size. -S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) +S32 LLFolderViewFolder::arrange( S32* width, S32* height ) { // sort before laying out contents - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } + getRoot()->getFolderViewModel()->sort(this); + + LLFastTimer t2(FTM_ARRANGE); // evaluate mHasVisibleChildren mHasVisibleChildren = false; - if (hasFilteredDescendants(filter_generation)) + if (getViewModelItem()->descendantsPassedFilter()) { // We have to verify that there's at least one child that's not filtered out bool found = false; @@ -1174,7 +860,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) { LLFolderViewItem* itemp = (*iit); - found = (itemp->getFiltered(filter_generation)); + found = itemp->passedFilter(); if (found) break; } @@ -1184,10 +870,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) { LLFolderViewFolder* folderp = (*fit); - found = ( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); + found = folderp->passedFilter(); if (found) break; } @@ -1197,13 +880,12 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) } // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height, filter_generation ); + LLFolderViewItem::arrange( width, height ); // clamp existing animated height so as to never get smaller than a single item mCurHeight = llmax((F32)*height, mCurHeight); // initialize running height value as height of single item in case we have no children - *height = getItemHeight(); F32 running_height = (F32)*height; F32 target_height = (F32)*height; @@ -1213,7 +895,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) // set last arrange generation first, in case children are animating // and need to be arranged again mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (mIsOpen) + if (isOpen()) { // Add sizes of children S32 parent_item_height = getRect().getHeight(); @@ -1221,17 +903,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) { LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter - } + folderp->setVisible(folderp->passedFilter()); // passed filter or has descendants that passed filter if (folderp->getVisible()) { @@ -1239,7 +911,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) S32 child_height = 0; S32 child_top = parent_item_height - llround(running_height); - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); + target_height += folderp->arrange( &child_width, &child_height ); running_height += (F32)child_height; *width = llmax(*width, child_width); @@ -1250,14 +922,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) iit != mItems.end(); ++iit) { LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } + itemp->setVisible(itemp->passedFilter()); if (itemp->getVisible()) { @@ -1265,7 +930,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) S32 child_height = 0; S32 child_top = parent_item_height - llround(running_height); - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); + target_height += itemp->arrange( &child_width, &child_height ); // don't change width, as this item is as wide as its parent folder by construction itemp->reshape( itemp->getRect().getWidth(), child_height); @@ -1289,7 +954,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) // animate current height towards target height if (llabs(mCurHeight - mTargetHeight) > 1.f) { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); + mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(isOpen() ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); requestArrange(); @@ -1340,247 +1005,24 @@ BOOL LLFolderViewFolder::needsArrange() void LLFolderViewFolder::requestSort() { - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - //mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up - && mParentFolder - && generation < mParentFolder->getCompletedFilterGeneration()) - { - mParentFolder->setCompletedFilterGeneration(generation, TRUE); - } + getViewModelItem()->requestSort(); } -void LLFolderViewFolder::filter( LLInventoryFilter& filter) -{ - S32 filter_generation = filter.getCurrentGeneration(); - // if failed to pass filter newer than must_pass_generation - // you will automatically fail this time, so we only - // check against items that have passed the filter - S32 must_pass_generation = filter.getMustPassGeneration(); - - bool autoopen_folders = (filter.hasFilterString()); - - // if we have already been filtered against this generation, skip out - if (getCompletedFilterGeneration() >= filter_generation) - { - return; - } +//TODO RN: get height resetting working +//void LLFolderViewFolder::setPassedFilter(BOOL passed, BOOL passed_folder, S32 filter_generation) +//{ +// // if this folder is now filtered, but wasn't before +// // (it just passed) +// if (passed && !passedFilter(filter_generation)) +// { +// // reset current height, because last time we drew it +// // it might have had more visible items than now +// mCurHeight = 0.f; +// } +// +// LLFolderViewItem::setPassedFilter(passed, passed_folder, filter_generation); +//} - // filter folder itself - if (getLastFilterGeneration() < filter_generation) - { - if (getLastFilterGeneration() >= must_pass_generation // folder has been compared to a valid precursor filter - && !mPassedFilter) // and did not pass the filter - { - // go ahead and flag this folder as done - mLastFilterGeneration = filter_generation; - mStringMatchOffset = std::string::npos; - } - else // filter self only on first pass through - { - // filter against folder rules - filterFolder(filter); - // and then item rules - LLFolderViewItem::filter( filter ); - } - } - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - mStatusText += llformat("(%d)", mCompletedFilterGeneration); - mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); - } - - // all descendants have been filtered later than must pass generation - // but none passed - if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation)) - { - // don't traverse children if we've already filtered them since must_pass_generation - // and came back with nothing - return; - } - - // we entered here with at least one filter iteration left - // check to see if we have any more before continuing on to children - if (filter.getFilterCount() < 0) - { - return; - } - - // when applying a filter, matching folders get their contents downloaded first - if (filter.isNotDefault() - && getFiltered(filter.getMinRequiredGeneration()) - && (mListener - && !gInventory.isCategoryComplete(mListener->getUUID()))) - { - LLInventoryModelBackgroundFetch::instance().start(mListener->getUUID()); - } - - // now query children - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder* folder = (*iter); - // have we run out of iterations this frame? - if (filter.getFilterCount() < 0) - { - break; - } - - // mMostFilteredDescendantGeneration might have been reset - // in which case we need to update it even for folders that - // don't need to be filtered anymore - if (folder->getCompletedFilterGeneration() >= filter_generation) - { - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - // just skip it, it has already been filtered - continue; - } - - // update this folders filter status (and children) - folder->filter( filter ); - - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation)) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - if (getRoot()->needsAutoSelect() && autoopen_folders) - { - folder->setOpenArrangeRecursively(TRUE); - } - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end(); - ++iter) - { - LLFolderViewItem* item = (*iter); - if (filter.getFilterCount() < 0) - { - break; - } - if (item->getLastFilterGeneration() >= filter_generation) - { - if (item->getFiltered()) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - continue; - } - - if (item->getLastFilterGeneration() >= must_pass_generation && - !item->getFiltered(must_pass_generation)) - { - // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done - item->setFiltered(FALSE, filter_generation); - continue; - } - - item->filter( filter ); - - if (item->getFiltered(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - } - - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // instead of exhausting the filter count for this frame - if (filter.getFilterCount() > 0) - { - // flag this folder as having completed filter pass for all descendants - setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/); - } -} - -void LLFolderViewFolder::filterFolder(LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFolderFilter; - const BOOL passed_filter = filter.checkFolder(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - { - mParentFolder->requestArrange(); - } - } - - setFilteredFolder(passed_filter, filter.getCurrentGeneration()); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) -{ - // if this folder is now filtered, but wasn't before - // (it just passed) - if (filtered && !mPassedFilter) - { - // reset current height, because last time we drew it - // it might have had more visible items than now - mCurHeight = 0.f; - } - - LLFolderViewItem::setFiltered(filtered, filter_generation); -} - -void LLFolderViewFolder::dirtyFilter() -{ - // we're a folder, so invalidate our completed generation - setCompletedFilterGeneration(-1, FALSE); - LLFolderViewItem::dirtyFilter(); -} - -BOOL LLFolderViewFolder::getFiltered() -{ - return getFilteredFolder(getRoot()->getFilter()->getMinRequiredGeneration()) - && LLFolderViewItem::getFiltered(); -} - -BOOL LLFolderViewFolder::getFiltered(S32 filter_generation) -{ - return getFilteredFolder(filter_generation) && LLFolderViewItem::getFiltered(filter_generation); -} - -BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation) -{ - return mMostFilteredDescendantGeneration >= filter_generation; -} - - -BOOL LLFolderViewFolder::hasFilteredDescendants() -{ - return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); -} // Passes selection information on to children and record selection // information if necessary. @@ -1934,14 +1376,6 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) void LLFolderViewFolder::destroyView() { - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - getRoot()->removeItemID(item->getListener()->getUUID()); - } - std::for_each(mItems.begin(), mItems.end(), DeletePointer()); mItems.clear(); @@ -1951,41 +1385,7 @@ void LLFolderViewFolder::destroyView() folderp->destroyView(); // removes entry from mFolders } - //deleteAllChildren(); - - if (mParentFolder) - { - mParentFolder->removeView(this); - } -} - -// remove the specified item (and any children) if possible. Return -// TRUE if the item was deleted. -BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) -{ - if(item->remove()) - { - return TRUE; - } - return FALSE; -} - -// simply remove the view (and any children) Don't bother telling the -// listeners. -void LLFolderViewFolder::removeView(LLFolderViewItem* item) -{ - if (!item || item->getParentFolder() != this) - { - return; - } - // deselect without traversing hierarchy - if (item->isSelected()) - { - item->deselectItem(); - } - getRoot()->removeFromSelectionList(item); - extractItem(item); - delete item; + LLFolderViewItem::destroyView(); } // extractItem() removes the specified item from the folder, but @@ -2011,106 +1411,16 @@ void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) mItems.erase(it); } //item has been removed, need to update filter - dirtyFilter(); + getViewModelItem()->removeChild(item->getViewModelItem()); + getViewModelItem()->dirtyFilter(); //because an item is going away regardless of filter status, force rearrange requestArrange(); - getRoot()->removeItemID(item->getListener()->getUUID()); removeChild(item); } -bool LLFolderViewFolder::isTrash() const -{ - if (mAmTrash == LLFolderViewFolder::UNKNOWN) - { - mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; - } - return mAmTrash == LLFolderViewFolder::TRASH; -} - -void LLFolderViewFolder::sortBy(U32 order) -{ - if (!mSortFunction.updateSort(order)) - { - // No changes. - return; - } - - // Propagate this change to sub folders - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - // Don't sort the topmost folders (My Inventory and Library) - if (mListener->getUUID().notNull()) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } - - if (order & LLInventoryFilter::SO_DATE) - { - time_t latest = 0; - - if (!mItems.empty()) - { - LLFolderViewItem* item = *(mItems.begin()); - latest = item->getCreationDate(); - } - - if (!mFolders.empty()) - { - LLFolderViewFolder* folder = *(mFolders.begin()); - if (folder->getCreationDate() > latest) - { - latest = folder->getCreationDate(); - } - } - mSubtreeCreationDate = latest; - } -} - -void LLFolderViewFolder::setItemSortOrder(U32 ordering) -{ - if (mSortFunction.updateSort(ordering)) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setItemSortOrder(ordering); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } -} - -EInventorySortGroup LLFolderViewFolder::getSortGroup() const -{ - if (isTrash()) - { - return SG_TRASH_FOLDER; - } - - if( mListener ) - { - if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType())) - { - return SG_SYSTEM_FOLDER; - } - } - - return SG_NORMAL_FOLDER; -} - BOOL LLFolderViewFolder::isMovable() { - if( mListener ) - { - if( !(mListener->isItemMovable()) ) + if( !(getViewModelItem()->isItemMovable()) ) { return FALSE; } @@ -2134,16 +1444,13 @@ BOOL LLFolderViewFolder::isMovable() return FALSE; } } - } return TRUE; } BOOL LLFolderViewFolder::isRemovable() { - if( mListener ) - { - if( !(mListener->isItemRemovable()) ) + if( !(getViewModelItem()->isItemRemovable()) ) { return FALSE; } @@ -2167,13 +1474,18 @@ BOOL LLFolderViewFolder::isRemovable() return FALSE; } } - } return TRUE; } // this is an internal method used for adding items to folders. BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) { + if (item->getParentFolder()) + { + item->getParentFolder()->extractItem(item); + } + item->setParentFolder(this); + mItems.push_back(item); item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); @@ -2181,30 +1493,43 @@ BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) addChild(item); - item->dirtyFilter(); + item->getViewModelItem()->dirtyFilter(); + // XXX stinson TODO : handle the creation date +#if 0 // Update the folder creation date if the child is newer than our current date setCreationDate(llmax<time_t>(mCreationDate, item->getCreationDate())); +#endif // Handle sorting requestArrange(); requestSort(); + getViewModelItem()->addChild(item->getViewModelItem()); + // XXX stinson TODO : handle the creation date +#if 0 // Traverse parent folders and update creation date and resort, if necessary LLFolderViewFolder* parentp = getParentFolder(); while (parentp) { // Update the folder creation date if the child is newer than our current date parentp->setCreationDate(llmax<time_t>(parentp->mCreationDate, item->getCreationDate())); + } +#endif - if (parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - } + //TODO RN - make sort bubble up as long as parent Folder doesn't have anything matching sort criteria + //// Traverse parent folders and update creation date and resort, if necessary + //LLFolderViewFolder* parentp = this; + //while (parentp) + //{ + // if (parentp->mSortFunction.isByDate()) + // { + // // parent folder doesn't have a time stamp yet, so get it from us + // parentp->requestSort(); + // } - parentp = parentp->getParentFolder(); - } + // parentp = parentp->getParentFolder(); + //} return TRUE; } @@ -2212,48 +1537,42 @@ BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) // this is an internal method used for adding items to folders. BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) { + if (folder->mParentFolder) + { + folder->mParentFolder->extractItem(folder); + } + folder->mParentFolder = this; mFolders.push_back(folder); folder->setOrigin(0, 0); folder->reshape(getRect().getWidth(), 0); folder->setVisible(FALSE); addChild( folder ); - folder->dirtyFilter(); + folder->getViewModelItem()->dirtyFilter(); // rearrange all descendants too, as our indentation level might have changed - folder->requestArrange(TRUE); + folder->requestArrange(); requestSort(); - LLFolderViewFolder* parentp = getParentFolder(); - while (parentp && !parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - parentp = parentp->getParentFolder(); - } + + getViewModelItem()->addChild(folder->getViewModelItem()); + return TRUE; } -void LLFolderViewFolder::requestArrange(BOOL include_descendants) +void LLFolderViewFolder::requestArrange() { + //if ( mLastArrangeGeneration != -1) + { mLastArrangeGeneration = -1; // flag all items up to root if (mParentFolder) { mParentFolder->requestArrange(); } - - if (include_descendants) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - (*iter)->requestArrange(TRUE); } } -} void LLFolderViewFolder::toggleOpen() { - setOpen(!mIsOpen); + setOpen(!isOpen()); } // Force a folder open or closed @@ -2264,18 +1583,15 @@ void LLFolderViewFolder::setOpen(BOOL openitem) void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) { - BOOL was_open = mIsOpen; + BOOL was_open = isOpen(); mIsOpen = openitem; - if (mListener) - { if(!was_open && openitem) { - mListener->openItem(); + getViewModelItem()->openItem(); } else if(was_open && !openitem) { - mListener->closeItem(); - } + getViewModelItem()->closeItem(); } if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) @@ -2294,7 +1610,7 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); } - if (was_open != mIsOpen) + if (was_open != isOpen()) { requestArrange(); } @@ -2307,7 +1623,7 @@ BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); + BOOL accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); if (accepted) { mDragAndDropTarget = TRUE; @@ -2363,23 +1679,6 @@ void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) } } -void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyListenerFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->applyListenerFunctorRecursively(functor); - } -} - // LLView functionality BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -2390,7 +1689,7 @@ BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, { BOOL handled = FALSE; - if (mIsOpen) + if (isOpen()) { handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); } @@ -2412,7 +1711,7 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); if (accepted) { @@ -2436,11 +1735,8 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) { BOOL handled = FALSE; - // fetch contents of this folder, as context menu can depend on contents - // still, user would have to open context menu again to see the changes - gInventory.fetchDescendentsOf(mListener->getUUID()); - if( mIsOpen ) + if( isOpen() ) { handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; } @@ -2470,7 +1766,7 @@ BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) { BOOL handled = FALSE; - if( mIsOpen ) + if( isOpen() ) { handled = childrenHandleMouseDown(x,y,mask) != NULL; } @@ -2493,18 +1789,8 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) { - /* Disable outfit double click to wear - const LLUUID &cat_uuid = getListener()->getUUID(); - const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); - if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) - { - getListener()->performAction(NULL, NULL,"replaceoutfit"); - return TRUE; - } - */ - BOOL handled = FALSE; - if( mIsOpen ) + if( isOpen() ) { handled = childrenHandleDoubleClick( x, y, mask ) != NULL; } @@ -2518,7 +1804,7 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) } else { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); toggleOpen(); } handled = TRUE; @@ -2532,7 +1818,7 @@ void LLFolderViewFolder::draw() { mControlLabelRotation = mAutoOpenCountdown * -90.f; } - else if (mIsOpen) + else if (isOpen()) { mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); } @@ -2541,31 +1827,10 @@ void LLFolderViewFolder::draw() mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); } - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date - && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - - - BOOL loading = (mIsOpen - && possibly_has_children - && !up_to_date ); - - if ( loading && !mIsLoading ) - { - // Measure how long we've been in the loading state - mTimeSinceRequestStart.reset(); - } - - mIsLoading = loading; - LLFolderViewItem::draw(); // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight )) + if( getRoot() == this || (isOpen() || mCurHeight != mTargetHeight )) { LLView::draw(); } @@ -2573,22 +1838,6 @@ void LLFolderViewFolder::draw() mExpanderHighlighted = FALSE; } -time_t LLFolderViewFolder::getCreationDate() const -{ - return llmax<time_t>(mCreationDate, mSubtreeCreationDate); -} - - -BOOL LLFolderViewFolder::potentiallyVisible() -{ - // folder should be visible by it's own filter status - return LLFolderViewItem::potentiallyVisible() - // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration()) - // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); -} - // this does prefix traversal, as folders are listed above their contents LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) { @@ -2800,98 +2049,3 @@ LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* it return result; } - -bool LLInventorySort::updateSort(U32 order) -{ - if (order != mSortOrder) - { - mSortOrder = order; - mByDate = (order & LLInventoryFilter::SO_DATE); - mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); - mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); - return true; - } - return false; -} - -bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) -{ - // ignore sort order for landmarks in the Favorites folder. - // they should be always sorted as in Favorites bar. See EXT-719 - if (a->getSortGroup() == SG_ITEM - && b->getSortGroup() == SG_ITEM - && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK - && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - - static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - - LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); - LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); - - if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) - { - // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem - // or to LLInvFVBridge - LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a->getListener()))->getItem(); - LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b->getListener()))->getItem(); - if (!aitem || !bitem) - return false; - S32 a_sort = aitem->getSortField(); - S32 b_sort = bitem->getSortField(); - return a_sort < b_sort; - } - } - - // We sort by name if we aren't sorting by date - // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - - if (a->getSortGroup() != b->getSortGroup()) - { - if (mSystemToTop) - { - // Group order is System Folders, Trash, Normal Folders, Items - return (a->getSortGroup() < b->getSortGroup()); - } - else if (mByDate) - { - // Trash needs to go to the bottom if we are sorting by date - if ( (a->getSortGroup() == SG_TRASH_FOLDER) - || (b->getSortGroup() == SG_TRASH_FOLDER)) - { - return (b->getSortGroup() == SG_TRASH_FOLDER); - } - } - } - - if (by_name) - { - S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel()); - if (0 == compare) - { - return (a->getCreationDate() > b->getCreationDate()); - } - else - { - return (compare < 0); - } - } - else - { - // BUG: This is very very slow. The getCreationDate() is log n in number - // of inventory items. - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) - { - return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0); - } - else - { - return (first_create > second_create); - } - } -} diff --git a/indra/newview/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 3c7592046a..50d3e0580e 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -27,54 +27,14 @@ #define LLFOLDERVIEWITEM_H #include "llview.h" -#include "lldarray.h" // *TODO: Eliminate, forward declare #include "lluiimage.h" -class LLFontGL; class LLFolderView; -class LLFolderViewEventListener; +class LLFolderViewModelItem; class LLFolderViewFolder; class LLFolderViewFunctor; -class LLFolderViewItem; -class LLFolderViewListenerFunctor; -class LLInventoryFilter; -class LLMenuGL; -class LLUIImage; -class LLViewerInventoryItem; - -// These are grouping of inventory types. -// Order matters when sorting system folders to the top. -enum EInventorySortGroup -{ - SG_SYSTEM_FOLDER, - SG_TRASH_FOLDER, - SG_NORMAL_FOLDER, - SG_ITEM -}; - -// *TODO: do we really need one sort object per folder? -// can we just have one of these per LLFolderView ? -class LLInventorySort -{ -public: - LLInventorySort() - : mSortOrder(0), - mByDate(false), - mSystemToTop(false), - mFoldersByName(false) { } - - // Returns true if order has changed - bool updateSort(U32 order); - U32 getSort() { return mSortOrder; } - bool isByDate() { return mByDate; } - - bool operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b); -private: - U32 mSortOrder; - bool mByDate; - bool mSystemToTop; - bool mFoldersByName; -}; +class LLFolderViewFilter; +class LLFolderViewModelInterface; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFolderViewItem @@ -91,19 +51,16 @@ public: struct Params : public LLInitParam::Block<Params, LLView::Params> { - Optional<LLUIImage*> icon; - Optional<LLUIImage*> icon_open; // used for folders - Optional<LLUIImage*> icon_overlay; // for links - Optional<LLFolderView*> root; - Mandatory<LLFolderViewEventListener*> listener; + Optional<LLUIImage*> folder_arrow_image, + selection_image; + Mandatory<LLFolderView*> root; + Mandatory<LLFolderViewModelItem*> listener; - Optional<LLUIImage*> folder_arrow_image; - Optional<S32> folder_indentation; // pixels - Optional<LLUIImage*> selection_image; - Optional<S32> item_height; // pixels - Optional<S32> item_top_pad; // pixels + Optional<S32> folder_indentation, // pixels + item_height, + item_top_pad; - Optional<S32> creation_date; //UTC seconds + Optional<time_t> creation_date; Params(); }; @@ -121,52 +78,40 @@ public: static const F32 FOLDER_CLOSE_TIME_CONSTANT; static const F32 FOLDER_OPEN_TIME_CONSTANT; - // Mostly for debugging printout purposes. - const std::string& getSearchableLabel() { return mSearchableLabel; } - - BOOL isLoading() const { return mIsLoading; } - private: BOOL mIsSelected; protected: friend class LLUICtrlFactory; - friend class LLFolderViewEventListener; + friend class LLFolderViewModelItem; LLFolderViewItem(const Params& p); std::string mLabel; - std::string mSearchableLabel; S32 mLabelWidth; bool mLabelWidthDirty; - time_t mCreationDate; LLFolderViewFolder* mParentFolder; - LLFolderViewEventListener* mListener; + LLFolderViewModelItem* mViewModelItem; BOOL mIsCurSelection; BOOL mSelectPending; LLFontGL::StyleFlags mLabelStyle; std::string mLabelSuffix; LLUIImagePtr mIcon; - std::string mStatusText; LLUIImagePtr mIconOpen; LLUIImagePtr mIconOverlay; BOOL mHasVisibleChildren; S32 mIndentation; S32 mItemHeight; - BOOL mPassedFilter; - S32 mLastFilterGeneration; - std::string::size_type mStringMatchOffset; + S32 mDragStartX, + mDragStartY; + + //TODO RN: create interface for string highlighting + //std::string::size_type mStringMatchOffset; F32 mControlLabelRotation; LLFolderView* mRoot; BOOL mDragAndDropTarget; - BOOL mIsLoading; - LLTimer mTimeSinceRequestStart; - bool mShowLoadStatus; bool mIsMouseOverTitle; - // helper function to change the selection from the root. - void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); - // this is an internal method used for adding items to folders. A // no-op at this level, but reimplemented in derived classes. virtual BOOL addItem(LLFolderViewItem*) { return FALSE; } @@ -174,47 +119,23 @@ protected: static LLFontGL* getLabelFontForStyle(U8 style); - virtual void setCreationDate(time_t creation_date_utc) { mCreationDate = creation_date_utc; } - public: BOOL postBuild(); - // This function clears the currently selected item, and records - // the specified selected item appropriately for display and use - // in the UI. If open is TRUE, then folders are opened up along - // the way to the selection. - void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus = TRUE); - - // This function is called when the folder view is dirty. It's - // implemented here but called by derived classes when folding the - // views. - void arrangeFromRoot(); - void filterFromRoot( void ); - + virtual void openItem( void ); + void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus); virtual ~LLFolderViewItem( void ); // addToFolder() returns TRUE if it succeeds. FALSE otherwise - enum { ARRANGE = TRUE, DO_NOT_ARRANGE = FALSE }; - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); - - virtual EInventorySortGroup getSortGroup() const; + virtual BOOL addToFolder(LLFolderViewFolder* folder); // Finds width and height of this object and it's children. Also // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); + virtual S32 arrange( S32* width, S32* height ); virtual S32 getItemHeight(); - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - - // updates filter serial number and optionally propagated value up to root - S32 getLastFilterGeneration() { return mLastFilterGeneration; } - - virtual void dirtyFilter(); - // If 'selection' is 'this' then note that otherwise ignore. // Returns TRUE if this item ends up being selected. virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); @@ -231,7 +152,7 @@ public: virtual void selectItem(); // gets multiple-element selection - virtual std::set<LLUUID> getSelectionList() const; + virtual std::set<LLFolderViewItem*> getSelectionList() const; // Returns true is this object and all of its children can be removed (deleted by user) virtual BOOL isRemovable(); @@ -252,74 +173,56 @@ public: BOOL hasVisibleChildren() { return mHasVisibleChildren; } - void setShowLoadStatus(bool status) { mShowLoadStatus = status; } - // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. //virtual BOOL removeRecursively(BOOL single_item); BOOL remove(); // Build an appropriate context menu for the item. Flags unused. - void buildContextMenu(LLMenuGL& menu, U32 flags); + void buildContextMenu(class LLMenuGL& menu, U32 flags); // This method returns the actual name of the thing being // viewed. This method will ask the viewed object itself. const std::string& getName( void ) const; - const std::string& getSearchableLabel( void ) const; - // This method returns the label displayed on the view. This // method was primarily added to allow sorting on the folder // contents possible before the entire view has been constructed. const std::string& getLabel() const { return mLabel; } - // Used for sorting, like getLabel() above. - virtual time_t getCreationDate() const { return mCreationDate; } LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } + void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; } + LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE ); LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE ); - const LLFolderViewEventListener* getListener( void ) const { return mListener; } - LLFolderViewEventListener* getListener( void ) { return mListener; } - - // Gets the inventory item if it exists (null otherwise) - LLViewerInventoryItem * getInventoryItem(void); + const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; } + LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; } + + const LLFolderViewModelInterface* getFolderViewModel( void ) const; + LLFolderViewModelInterface* getFolderViewModel( void ); // just rename the object. void rename(const std::string& new_name); - // open - virtual void openItem( void ); - virtual void preview(void); // Show children (unfortunate that this is called "open") virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() const { return FALSE; } virtual LLFolderView* getRoot(); + virtual const LLFolderView* getRoot() const; BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); S32 getIndentation() { return mIndentation; } - virtual BOOL potentiallyVisible(); // do we know for a fact that this item won't be displayed? - virtual BOOL potentiallyFiltered(); // do we know for a fact that this item has been filtered out? - - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - - // change the icon - void setIcon(LLUIImagePtr icon); + virtual BOOL passedFilter(S32 filter_generation = -1); // refresh information from the object being viewed. - void refreshFromListener(); virtual void refresh(); - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); - // LLView functionality virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); @@ -343,11 +246,6 @@ private: static std::map<U8, LLFontGL*> sFonts; // map of styles to fonts }; - -// function used for sorting. -typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b); - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFolderViewFolder // @@ -363,10 +261,6 @@ protected: friend class LLUICtrlFactory; public: - typedef enum e_trash - { - UNKNOWN, TRASH, NOT_TRASH - } ETrash; typedef std::list<LLFolderViewItem*> items_t; typedef std::list<LLFolderViewFolder*> folders_t; @@ -374,21 +268,16 @@ public: protected: items_t mItems; folders_t mFolders; - LLInventorySort mSortFunction; BOOL mIsOpen; BOOL mExpanderHighlighted; F32 mCurHeight; F32 mTargetHeight; F32 mAutoOpenCountdown; - time_t mSubtreeCreationDate; - mutable ETrash mAmTrash; S32 mLastArrangeGeneration; S32 mLastCalculatedWidth; - S32 mCompletedFilterGeneration; S32 mMostFilteredDescendantGeneration; bool mNeedsSort; - bool mPassedFolderFilter; public: typedef enum e_recurse_type @@ -402,48 +291,25 @@ public: virtual ~LLFolderViewFolder( void ); - virtual BOOL potentiallyVisible(); - LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); // addToFolder() returns TRUE if it succeeds. FALSE otherwise - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); + virtual BOOL addToFolder(LLFolderViewFolder* folder); // Finds width and height of this object and it's children. Also // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); + virtual S32 arrange( S32* width, S32* height ); BOOL needsArrange(); - void requestSort(); - // Returns the sort group (system, trash, folder) for this folder. - virtual EInventorySortGroup getSortGroup() const; - - virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up); - virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; } - - BOOL hasFilteredDescendants(S32 filter_generation); - BOOL hasFilteredDescendants(); - - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - - virtual void dirtyFilter(); - - // folder-specific filtering (filter status propagates top down instead of bottom up) - void filterFolder(LLInventoryFilter& filter); - void setFilteredFolder(bool filtered, S32 filter_generation); - bool getFilteredFolder(S32 filter_generation); + bool descendantsPassedFilter(S32 filter_generation = -1); // Passes selection information on to children and record // selection information if necessary. // Returns TRUE if this object (or a child) ends up being selected. // If 'openitem' is TRUE then folders are opened up along the way to the selection. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); + virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus = TRUE); // This method is used to change the selection of an item. // Recursively traverse all children; if 'selection' is 'this' then change @@ -469,25 +335,13 @@ public: //virtual BOOL removeRecursively(BOOL single_item); //virtual BOOL remove(); - // remove the specified item (and any children) if - // possible. Return TRUE if the item was deleted. - BOOL removeItem(LLFolderViewItem* item); - - // simply remove the view (and any children) Don't bother telling - // the listeners. - void removeView(LLFolderViewItem* item); - // extractItem() removes the specified item from the folder, but // doesn't delete it. - void extractItem( LLFolderViewItem* item ); + virtual void extractItem( LLFolderViewItem* item ); // This function is called by a child that needs to be resorted. void resort(LLFolderViewItem* item); - void setItemSortOrder(U32 ordering); - void sortBy(U32); - //BOOL (*func)(LLFolderViewItem* a, LLFolderViewItem* b)); - void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; } // folders can be opened. This will usually be called by internal @@ -498,8 +352,9 @@ public: virtual void setOpen(BOOL openitem = TRUE); // Called when a child is refreshed. - // don't rearrange child folder contents unless explicitly requested - virtual void requestArrange(BOOL include_descendants = FALSE); + virtual void requestArrange(); + + virtual void requestSort(); // internal method which doesn't update the entire view. This // method was written because the list iterators destroy the state @@ -519,7 +374,6 @@ public: std::string& tooltip_msg); void applyFunctorRecursively(LLFolderViewFunctor& functor); - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); // Just apply this functor to the folder's immediate children. void applyFunctorToChildren(LLFolderViewFunctor& functor); @@ -545,32 +399,22 @@ public: std::string& tooltip_msg); virtual void draw(); - time_t getCreationDate() const; - bool isTrash() const; - - folders_t::const_iterator getFoldersBegin() const { return mFolders.begin(); } - folders_t::const_iterator getFoldersEnd() const { return mFolders.end(); } + folders_t::iterator getFoldersBegin() { return mFolders.begin(); } + folders_t::iterator getFoldersEnd() { return mFolders.end(); } folders_t::size_type getFoldersCount() const { return mFolders.size(); } items_t::const_iterator getItemsBegin() const { return mItems.begin(); } items_t::const_iterator getItemsEnd() const { return mItems.end(); } items_t::size_type getItemsCount() const { return mItems.size(); } + LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse); void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items); -}; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewListenerFunctor -// -// This simple abstract base class can be used to applied to all -// listeners in a hierarchy. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewListenerFunctor -{ public: - virtual ~LLFolderViewListenerFunctor() {} - virtual void operator()(LLFolderViewEventListener* listener) = 0; + //WARNING: do not call directly...use the appropriate LLFolderViewModel-derived class instead + template<typename SORT_FUNC> void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); } + template<typename SORT_FUNC> void sortItems(const SORT_FUNC& func) { mItems.sort(func); } }; + #endif // LLFOLDERVIEWITEM_H diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp new file mode 100644 index 0000000000..dc6e4d754b --- /dev/null +++ b/indra/llui/llfolderviewmodel.cpp @@ -0,0 +1,53 @@ +/** + * @file llfolderviewmodel.cpp + * @brief Implementation of the view model collection of classes. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llfolderviewmodel.h" +#include "lltrans.h" + +bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item) +{ + return item->getSortVersion() < mTargetSortVersion; +} + +std::string LLFolderViewModelCommon::getStatusText() +{ + if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter()->getCurrentGeneration()) + { + return LLTrans::getString("Searching"); + } + else + { + return getFilter()->getEmptyLookupMessage(); + } +} + +void LLFolderViewModelCommon::filter() +{ + getFilter()->setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000)); + mFolderView->getViewModelItem()->filter(*getFilter()); +} diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h new file mode 100644 index 0000000000..acdec53602 --- /dev/null +++ b/indra/llui/llfolderviewmodel.h @@ -0,0 +1,354 @@ +/** + * @file llfolderviewmodel.h + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLFOLDERVIEWMODEL_H +#define LLFOLDERVIEWMODEL_H + +#include "llfontgl.h" // just for StyleFlags enum +#include "llfolderview.h" + +// These are grouping of inventory types. +// Order matters when sorting system folders to the top. +enum EInventorySortGroup +{ + SG_SYSTEM_FOLDER, + SG_TRASH_FOLDER, + SG_NORMAL_FOLDER, + SG_ITEM +}; + +class LLFontGL; +class LLInventoryModel; +class LLMenuGL; +class LLUIImage; +class LLUUID; +class LLFolderViewItem; +class LLFolderViewFolder; + +class LLFolderViewFilter +{ +public: + enum EFilterModified + { + FILTER_NONE, // nothing to do, already filtered + FILTER_RESTART, // restart filtering from scratch + FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter + FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one + }; + +public: + + LLFolderViewFilter() {} + virtual ~LLFolderViewFilter() {} + + // +-------------------------------------------------------------------+ + // + Execution And Results + // +-------------------------------------------------------------------+ + virtual bool check(const LLFolderViewModelItem* item) = 0; + virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0; + + virtual void setEmptyLookupMessage(const std::string& message) = 0; + virtual std::string getEmptyLookupMessage() const = 0; + + virtual bool showAllResults() const = 0; + + // +-------------------------------------------------------------------+ + // + Status + // +-------------------------------------------------------------------+ + virtual bool isActive() const = 0; + virtual bool isModified() const = 0; + virtual void clearModified() = 0; + virtual const std::string& getName() const = 0; + virtual const std::string& getFilterText() = 0; + //RN: this is public to allow system to externally force a global refilter + virtual void setModified(EFilterModified behavior = FILTER_RESTART) = 0; + + // +-------------------------------------------------------------------+ + // + Count + // +-------------------------------------------------------------------+ + virtual void setFilterCount(S32 count) = 0; + virtual S32 getFilterCount() const = 0; + virtual void decrementFilterCount() = 0; + + // +-------------------------------------------------------------------+ + // + Default + // +-------------------------------------------------------------------+ + virtual bool isDefault() const = 0; + virtual bool isNotDefault() const = 0; + virtual void markDefault() = 0; + virtual void resetDefault() = 0; + + // +-------------------------------------------------------------------+ + // + Generation + // +-------------------------------------------------------------------+ + virtual S32 getCurrentGeneration() const = 0; + virtual S32 getFirstSuccessGeneration() const = 0; + virtual S32 getFirstRequiredGeneration() const = 0; +}; + +// This is am abstract base class that users of the folderview classes +// would use to bridge the folder view with the underlying data +class LLFolderViewModelItem +{ +public: + virtual ~LLFolderViewModelItem( void ) {}; + + virtual void update() {} //called when drawing + virtual const std::string& getName() const = 0; + virtual const std::string& getDisplayName() const = 0; + virtual const std::string& getSearchableName() const = 0; + + virtual LLPointer<LLUIImage> getIcon() const = 0; + virtual LLPointer<LLUIImage> getIconOpen() const { return getIcon(); } + virtual LLPointer<LLUIImage> getIconOverlay() const { return NULL; } + + virtual LLFontGL::StyleFlags getLabelStyle() const = 0; + virtual std::string getLabelSuffix() const = 0; + + virtual void openItem( void ) = 0; + virtual void closeItem( void ) = 0; + virtual void selectItem(void) = 0; + + virtual BOOL isItemRenameable() const = 0; + virtual BOOL renameItem(const std::string& new_name) = 0; + + virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder + virtual void move( LLFolderViewModelItem* parent_listener ) = 0; + + virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed + virtual BOOL removeItem() = 0; + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0; + + virtual BOOL isItemCopyable() const = 0; + virtual BOOL copyToClipboard() const = 0; + virtual BOOL cutToClipboard() const = 0; + + virtual BOOL isClipboardPasteable() const = 0; + virtual void pasteFromClipboard() = 0; + virtual void pasteLinkFromClipboard() = 0; + + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; + + virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet? + + virtual void filter( LLFolderViewFilter& filter) = 0; + virtual bool passedFilter(S32 filter_generation = -1) = 0; + virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0; + virtual void setPassedFilter(bool passed, bool passed_folder, S32 filter_generation) = 0; + virtual void dirtyFilter() = 0; + + virtual S32 getLastFilterGeneration() const = 0; + + virtual bool hasChildren() const = 0; + virtual void addChild(LLFolderViewModelItem* child) = 0; + virtual void removeChild(LLFolderViewModelItem* child) = 0; + + // This method will be called to determine if a drop can be + // performed, and will set drop to TRUE if a drop is + // requested. Returns TRUE if a drop is possible/happened, + // otherwise FALSE. + virtual BOOL dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) = 0; + + virtual void requestSort() = 0; + virtual S32 getSortVersion() = 0; + virtual void setSortVersion(S32 version) = 0; + virtual void setParent(LLFolderViewModelItem* parent) = 0; + +protected: + + friend class LLFolderViewItem; + virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0; + +}; + +class LLFolderViewModelItemCommon : public LLFolderViewModelItem +{ +public: + LLFolderViewModelItemCommon() + : mSortVersion(-1), + mPassedFilter(true), + mPassedFolderFilter(true), + mPrevPassedAllFilters(false), + mFolderViewItem(NULL), + mLastFilterGeneration(-1), + mMostFilteredDescendantGeneration(-1), + mParent(NULL) + { + std::for_each(mChildren.begin(), mChildren.end(), DeletePointer()); + } + + void requestSort() { mSortVersion = -1; } + S32 getSortVersion() { return mSortVersion; } + void setSortVersion(S32 version) { mSortVersion = version;} + + S32 getLastFilterGeneration() const { return mLastFilterGeneration; } + void dirtyFilter() + { + mLastFilterGeneration = -1; + + // bubble up dirty flag all the way to root + if (mParent) + { + mParent->dirtyFilter(); + } + } + virtual void addChild(LLFolderViewModelItem* child) + { + mChildren.push_back(child); + child->setParent(this); + } + virtual void removeChild(LLFolderViewModelItem* child) + { + mChildren.remove(child); + child->setParent(NULL); + } + +protected: + virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } + + S32 mSortVersion; + bool mPassedFilter; + bool mPassedFolderFilter; + bool mPrevPassedAllFilters; + + S32 mLastFilterGeneration; + S32 mMostFilteredDescendantGeneration; + + + typedef std::list<LLFolderViewModelItem*> child_list_t; + child_list_t mChildren; + LLFolderViewModelItem* mParent; + + void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} + LLFolderViewItem* mFolderViewItem; +}; + +class LLFolderViewModelInterface +{ +public: + virtual ~LLFolderViewModelInterface() {} + virtual void requestSortAll() = 0; + + virtual void sort(class LLFolderViewFolder*) = 0; + virtual void filter() = 0; + + virtual bool contentsReady() = 0; + virtual void setFolderView(LLFolderView* folder_view) = 0; + virtual LLFolderViewFilter* getFilter() = 0; + virtual const LLFolderViewFilter* getFilter() const = 0; + virtual std::string getStatusText() = 0; + + virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0; +}; + +class LLFolderViewModelCommon : public LLFolderViewModelInterface +{ +public: + LLFolderViewModelCommon() + : mTargetSortVersion(0), + mFolderView(NULL) + {} + + virtual void requestSortAll() + { + // sort everything + mTargetSortVersion++; + } + virtual std::string getStatusText(); + virtual void filter(); + + void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} + +protected: + bool needsSort(class LLFolderViewModelItem* item); + + S32 mTargetSortVersion; + LLFolderView* mFolderView; + +}; + +template <typename SORT_TYPE, typename ITEM_TYPE, typename FOLDER_TYPE, typename FILTER_TYPE> +class LLFolderViewModel : public LLFolderViewModelCommon +{ +public: + LLFolderViewModel(){} + virtual ~LLFolderViewModel() {} + + typedef SORT_TYPE SortType; + typedef ITEM_TYPE ItemType; + typedef FOLDER_TYPE FolderType; + typedef FILTER_TYPE FilterType; + + virtual SortType& getSorter() { return mSorter; } + virtual const SortType& getSorter() const { return mSorter; } + virtual void setSorter(const SortType& sorter) { mSorter = sorter; requestSortAll(); } + + virtual FilterType* getFilter() { return &mFilter; } + virtual const FilterType* getFilter() const { return &mFilter; } + virtual void setFilter(const FilterType& filter) { mFilter = filter; } + + // TODO RN: remove this and put all filtering logic in view model + // add getStatusText and isFiltering() + virtual bool contentsReady() { return true; } + + + struct ViewModelCompare + { + ViewModelCompare(const SortType& sorter) + : mSorter(sorter) + {} + + bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const + { + return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem())); + } + + bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const + { + return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem())); + } + + const SortType& mSorter; + }; + + void sort(LLFolderViewFolder* folder) + { + if (needsSort(folder->getViewModelItem())) + { + folder->sortFolders(ViewModelCompare(getSorter())); + folder->sortItems(ViewModelCompare(getSorter())); + folder->getViewModelItem()->setSortVersion(mTargetSortVersion); + folder->requestArrange(); + } + } + +protected: + SortType mSorter; + FilterType mFilter; +}; + +#endif // LLFOLDERVIEWMODEL_H diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h index 37c657dd92..680a1a7f1d 100644 --- a/indra/llui/llhandle.h +++ b/indra/llui/llhandle.h @@ -158,13 +158,6 @@ public: return mHandle; } -protected: - typedef LLHandle<T> handle_type_t; - LLHandleProvider() - { - // provided here to enforce T deriving from LLHandleProvider<T> - } - template <typename U> LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const { @@ -173,6 +166,12 @@ protected: return downcast_handle; } +protected: + typedef LLHandle<T> handle_type_t; + LLHandleProvider() + { + // provided here to enforce T deriving from LLHandleProvider<T> + } private: mutable LLRootHandle<T> mHandle; diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 648cd5fdce..883331c792 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -179,6 +179,8 @@ public: F32 getVisibleAmount() const; S32 getVisibleDim() const; + bool isCollapsed() const { return mCollapsed;} + void setOrientation(LLLayoutStack::ELayoutOrientation orientation); void storeOriginalDim(); diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp index 6ac38f5ad4..1ede5b706f 100644 --- a/indra/llui/llloadingindicator.cpp +++ b/indra/llui/llloadingindicator.cpp @@ -52,7 +52,7 @@ LLLoadingIndicator::LLLoadingIndicator(const Params& p) void LLLoadingIndicator::initFromParams(const Params& p) { - BOOST_FOREACH(LLUIImage* image, p.images.image) + BOOST_FOREACH(LLUIImage* image, p.images().image) { mImages.push_back(image); } diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h index c1f979c111..ffcb329f42 100644 --- a/indra/llui/llloadingindicator.h +++ b/indra/llui/llloadingindicator.h @@ -51,7 +51,7 @@ class LLLoadingIndicator LOG_CLASS(LLLoadingIndicator); public: - struct Images : public LLInitParam::BatchBlock<Images> + struct Images : public LLInitParam::Block<Images> { Multiple<LLUIImage*> image; @@ -62,8 +62,8 @@ public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - Optional<F32> images_per_sec; - Optional<Images> images; + Optional<F32> images_per_sec; + Optional<Atomic<Images> > images; Params() : images_per_sec("images_per_sec", 1.0f), diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 50d59f79f4..746ade4648 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -44,33 +44,27 @@ void LLMenuButton::MenuPositions::declareValues() LLMenuButton::Params::Params() : menu_filename("menu_filename"), - position("position", MP_BOTTOM_LEFT) + position("menu_position", MP_BOTTOM_LEFT) { + addSynonym(position, "position"); } LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) : LLButton(p), mIsMenuShown(false), - mMenuPosition(p.position) + mMenuPosition(p.position), + mOwnMenu(false) { std::string menu_filename = p.menu_filename; - if (!menu_filename.empty()) - { - LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - llwarns << "Error loading menu_button menu" << llendl; - return; - } - - menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); - - mMenuHandle = menu->getHandle(); + setMenu(menu_filename, mMenuPosition); + updateMenuOrigin(); +} - updateMenuOrigin(); - } +LLMenuButton::~LLMenuButton() +{ + cleanup(); } boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) @@ -80,9 +74,7 @@ boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_sign void LLMenuButton::hideMenu() { - if(mMenuHandle.isDead()) return; - - LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (menu) { menu->setVisible(FALSE); @@ -94,19 +86,39 @@ LLToggleableMenu* LLMenuButton::getMenu() return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); } -void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/) +void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/) +{ + if (menu_filename.empty()) + { + return; + } + + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + llwarns << "Error loading menu_button menu" << llendl; + return; + } + + setMenu(menu, position, true); +} + +void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/, bool take_ownership /*false*/) { if (!menu) return; + cleanup(); // destroy the previous memnu if we own it + mMenuHandle = menu->getHandle(); mMenuPosition = position; + mOwnMenu = take_ownership; menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); } BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) { - if (mMenuHandle.isDead()) return FALSE; + if (!getMenu()) return FALSE; if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { @@ -118,7 +130,7 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) return TRUE; } - LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) { menu->setVisible(FALSE); @@ -139,9 +151,12 @@ BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) void LLMenuButton::toggleMenu() { - if(mMenuHandle.isDead()) return; + if (mValidateSignal && !(*mValidateSignal)(this, LLSD())) + { + return; + } - LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (!menu) return; // Store the button rectangle to toggle menu visibility if a mouse event @@ -170,7 +185,8 @@ void LLMenuButton::toggleMenu() void LLMenuButton::updateMenuOrigin() { - if (mMenuHandle.isDead()) return; + LLToggleableMenu* menu = getMenu(); + if (!menu) return; LLRect rect = getRect(); @@ -179,12 +195,12 @@ void LLMenuButton::updateMenuOrigin() case MP_TOP_LEFT: { mX = rect.mLeft; - mY = rect.mTop + mMenuHandle.get()->getRect().getHeight(); + mY = rect.mTop + menu->getRect().getHeight(); break; } case MP_TOP_RIGHT: { - const LLRect& menu_rect = mMenuHandle.get()->getRect(); + const LLRect& menu_rect = menu->getRect(); mX = rect.mRight - menu_rect.getWidth(); mY = rect.mTop + menu_rect.getHeight(); break; @@ -211,3 +227,11 @@ void LLMenuButton::onMenuVisibilityChange(const LLSD& param) mIsMenuShown = false; } } + +void LLMenuButton::cleanup() +{ + if (mMenuHandle.get() && mOwnMenu) + { + mMenuHandle.get()->die(); + } +} diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index e2396e7fb2..67ec1983b3 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -34,6 +34,8 @@ class LLToggleableMenu; class LLMenuButton : public LLButton { + LOG_CLASS(LLMenuButton); + public: typedef enum e_menu_position { @@ -53,7 +55,7 @@ public: { // filename for it's toggleable menu Optional<std::string> menu_filename; - Optional<EMenuPosition> position; + Optional<EMenuPosition, MenuPositions> position; Params(); }; @@ -68,13 +70,15 @@ public: void hideMenu(); LLToggleableMenu* getMenu(); - void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT); + void setMenu(const std::string& menu_filename, EMenuPosition position = MP_TOP_LEFT); + void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT, bool take_ownership = false); void setMenuPosition(EMenuPosition position) { mMenuPosition = position; } protected: friend class LLUICtrlFactory; LLMenuButton(const Params&); + ~LLMenuButton(); void toggleMenu(); void updateMenuOrigin(); @@ -82,11 +86,14 @@ protected: void onMenuVisibilityChange(const LLSD& param); private: + void cleanup(); + LLHandle<LLView> mMenuHandle; bool mIsMenuShown; EMenuPosition mMenuPosition; S32 mX; S32 mY; + bool mOwnMenu; // true if we manage the menu lifetime }; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index efb9848a90..5182a8cea1 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -4029,11 +4029,6 @@ BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) return result; } -void LLContextMenu::draw() -{ - LLMenuGL::draw(); -} - BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu) { diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 67b3e1fbe6..a9de3ef937 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -668,8 +668,6 @@ public: // can't set visibility directly, must call show or hide virtual void setVisible (BOOL visible); - virtual void draw (); - virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); virtual void hide (); diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index aa5f577897..e80799b16d 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -173,7 +173,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, else if (floaterp->getHost()) { // floaterp is hosted by somebody else and - // this is adding it, so remove it from it's old host + // this is adding it, so remove it from its old host floaterp->getHost()->removeFloater(floaterp); } else if (floaterp->getParent() == gFloaterView) @@ -468,23 +468,12 @@ BOOL LLMultiFloater::postBuild() void LLMultiFloater::updateResizeLimits() { - static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; // initialize minimum size constraint to the original xml values. S32 new_min_width = mOrigMinWidth; S32 new_min_height = mOrigMinHeight; - // possibly increase minimum size constraint due to children's minimums. - for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) - { - LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); - if (floaterp) - { - new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); - new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); - } - } + + computeResizeLimits(new_min_width, new_min_height); + setResizeLimits(new_min_width, new_min_height); S32 cur_height = getRect().getHeight(); @@ -510,3 +499,22 @@ void LLMultiFloater::updateResizeLimits() gFloaterView->adjustToFitScreen(this, TRUE); } } + +void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); + if (floaterp) + { + new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); + } + } +} diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h index 9fa917eca1..44514a6246 100644 --- a/indra/llui/llmultifloater.h +++ b/indra/llui/llmultifloater.h @@ -45,8 +45,8 @@ public: virtual BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void draw(); - /*virtual*/ void setVisible(BOOL visible); + virtual void draw(); + virtual void setVisible(BOOL visible); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); @@ -93,6 +93,9 @@ protected: LLTabContainer::TabPosition mTabPos; BOOL mAutoResize; S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late + +private: + virtual void computeResizeLimits(S32& new_min_width, S32& new_min_height); }; #endif // LL_MULTI_FLOATER_H diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 09480968a6..ed59c607a7 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -60,7 +60,8 @@ void NotificationPriorityValues::declareValues() } LLNotificationForm::FormElementBase::FormElementBase() -: name("name") +: name("name"), + enabled("enabled", true) {} LLNotificationForm::FormIgnore::FormIgnore() @@ -104,39 +105,7 @@ LLNotificationForm::Params::Params() form_elements("") {} -// Local channel for persistent notifications -// Stores only persistent notifications. -// Class users can use connectChanged() to process persistent notifications -// (see LLNotificationStorage for example). -class LLPersistentNotificationChannel : public LLNotificationChannel -{ - LOG_CLASS(LLPersistentNotificationChannel); -public: - LLPersistentNotificationChannel() : - LLNotificationChannel("Persistent", "Visible", ¬ificationFilter, LLNotificationComparators::orderByUUID()) - { - } - -private: - - // The channel gets all persistent notifications except those that have been canceled - static bool notificationFilter(LLNotificationPtr pNotification) - { - bool handle_notification = false; - - handle_notification = pNotification->isPersistent() - && !pNotification->isCancelled(); - - return handle_notification; - } - void onDelete(LLNotificationPtr pNotification) - { - // we want to keep deleted notifications in our log, otherwise some - // notifications will be lost on exit. - mItems.insert(pNotification); - } -}; bool filterIgnoredNotifications(LLNotificationPtr notification) { @@ -210,6 +179,14 @@ LLNotificationForm::LLNotificationForm() { } +LLNotificationForm::LLNotificationForm( const LLNotificationForm& other ) +{ + mFormData = other.mFormData; + mIgnore = other.mIgnore; + mIgnoreMsg = other.mIgnoreMsg; + mIgnoreSetting = other.mIgnoreSetting; + mInvertSetting = other.mInvertSetting; +} LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) : mIgnore(IGNORE_NO), @@ -246,14 +223,6 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica LLParamSDParser parser; parser.writeSD(mFormData, p.form_elements); - if (!mFormData.isArray()) - { - // change existing contents to a one element array - LLSD new_llsd_array = LLSD::emptyArray(); - new_llsd_array.append(mFormData); - mFormData = new_llsd_array; - } - for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray(); it != end_it; ++it) @@ -300,7 +269,7 @@ LLSD LLNotificationForm::getElement(const std::string& element_name) } -bool LLNotificationForm::hasElement(const std::string& element_name) +bool LLNotificationForm::hasElement(const std::string& element_name) const { for (LLSD::array_const_iterator it = mFormData.beginArray(); it != mFormData.endArray(); @@ -311,7 +280,36 @@ bool LLNotificationForm::hasElement(const std::string& element_name) return false; } -void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value) +bool LLNotificationForm::getElementEnabled(const std::string& element_name) const +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + return (*it)["enabled"].asBoolean(); + } + } + + return false; +} + +void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled) +{ + for (LLSD::array_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + (*it)["enabled"] = enabled; + } + } +} + + +void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled) { LLSD element; element["type"] = type; @@ -319,6 +317,7 @@ void LLNotificationForm::addElement(const std::string& type, const std::string& element["text"] = name; element["value"] = value; element["index"] = mFormData.size(); + element["enabled"] = enabled; mFormData.append(element); } @@ -408,9 +407,13 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par mURLOption(p.url.option), mURLTarget(p.url.target), mUnique(p.unique.isProvided()), + mCombineBehavior(p.unique.combine), mPriority(p.priority), mPersist(p.persist), - mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()) + mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()), + mLogToChat(p.log_to_chat), + mLogToIM(p.log_to_im), + mShowToast(p.show_toast) { if (p.sound.isProvided() && LLUI::sSettingGroups["config"]->controlExists(p.sound)) @@ -460,18 +463,18 @@ LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationV } } -LLNotification::LLNotification(const LLNotification::Params& p) : +LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) : mTimestamp(p.time_stamp), mSubstitutions(p.substitutions), mPayload(p.payload), - mExpiresAt(0), + mExpiresAt(p.expiry), mTemporaryResponder(false), mRespondedTo(false), mPriority(p.priority), mCancelled(false), mIgnored(false), mResponderObj(NULL), - mIsReusable(false) + mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()) { if (p.functor.name.isChosen()) { @@ -494,52 +497,32 @@ LLNotification::LLNotification(const LLNotification::Params& p) : mResponderObj = p.responder; } - mId.generate(); init(p.name, p.form_elements); } -LLNotification::LLNotification(const LLSD& sd) : - mTemporaryResponder(false), - mRespondedTo(false), - mCancelled(false), - mIgnored(false), - mResponderObj(NULL), - mIsReusable(false) -{ - mId.generate(); - mSubstitutions = sd["substitutions"]; - mPayload = sd["payload"]; - mTimestamp = sd["time"]; - mExpiresAt = sd["expiry"]; - mPriority = (ENotificationPriority)sd["priority"].asInteger(); - mResponseFunctorName = sd["responseFunctor"].asString(); - std::string templatename = sd["name"].asString(); - init(templatename, LLSD()); - // replace form with serialized version - mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"])); -} - - LLSD LLNotification::asLLSD() { - LLSD output; - output["id"] = mId; - output["name"] = mTemplatep->mName; - output["form"] = getForm()->asLLSD(); - output["substitutions"] = mSubstitutions; - output["payload"] = mPayload; - output["time"] = mTimestamp; - output["expiry"] = mExpiresAt; - output["priority"] = (S32)mPriority; - output["responseFunctor"] = mResponseFunctorName; - output["reusable"] = mIsReusable; + LLParamSDParser parser; - if(mResponder) + Params p; + p.id = mId; + p.name = mTemplatep->mName; + p.form_elements = getForm()->asLLSD(); + + p.substitutions = mSubstitutions; + p.payload = mPayload; + p.time_stamp = mTimestamp; + p.expiry = mExpiresAt; + p.priority = mPriority; + + if(!mResponseFunctorName.empty()) { - output["responder"] = mResponder->asLLSD(); + p.functor.name = mResponseFunctorName; } + LLSD output; + parser.writeSD(output, p); return output; } @@ -569,7 +552,6 @@ void LLNotification::updateFrom(LLNotificationPtr other) mRespondedTo = other->mRespondedTo; mResponse = other->mResponse; mTemporaryResponder = other->mTemporaryResponder; - mIsReusable = other->isReusable(); update(); } @@ -668,7 +650,7 @@ void LLNotification::respond(const LLSD& response) return; } - if (mTemporaryResponder && !isReusable()) + if (mTemporaryResponder) { LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); mResponseFunctorName = ""; @@ -897,6 +879,44 @@ std::string LLNotification::getURL() const return (mTemplatep ? url : ""); } +bool LLNotification::canLogToChat() const +{ + return mTemplatep->mLogToChat; +} + +bool LLNotification::canLogToIM() const +{ + return mTemplatep->mLogToIM; +} + +bool LLNotification::canShowToast() const +{ + return mTemplatep->mShowToast; +} + +bool LLNotification::hasFormElements() const +{ + return mTemplatep->mForm->getNumElements() != 0; +} + +LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const +{ + return mTemplatep->mCombineBehavior; +} + +void LLNotification::updateForm( const LLNotificationFormPtr& form ) +{ + mForm = form; +} + +void LLNotification::repost() +{ + mRespondedTo = false; + LLNotifications::instance().update(shared_from_this()); +} + + + // ========================================================= // LLNotificationChannel implementation // --- @@ -957,7 +977,7 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt std::string cmd = payload["sigtype"]; LLNotificationSet::iterator foundItem = mItems.find(pNotification); bool wasFound = (foundItem != mItems.end()); - bool passesFilter = mFilter(pNotification); + bool passesFilter = mFilter ? mFilter(pNotification) : true; // first, we offer the result of the filter test to the simple // signals for pass/fail. One of these is guaranteed to be called. @@ -987,8 +1007,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // not in our list, add it and say so mItems.insert(pNotification); - abortProcessing = mChanged(payload); onLoad(pNotification); + abortProcessing = mChanged(payload); } } else if (cmd == "change") @@ -1003,18 +1023,18 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // it already existed, so this is a change // since it changed in place, all we have to do is resend the signal - abortProcessing = mChanged(payload); onChange(pNotification); + abortProcessing = mChanged(payload); } else { // not in our list, add it and say so mItems.insert(pNotification); + onChange(pNotification); // our payload is const, so make a copy before changing it LLSD newpayload = payload; newpayload["sigtype"] = "add"; abortProcessing = mChanged(newpayload); - onChange(pNotification); } } else @@ -1023,11 +1043,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // it already existed, so this is a delete mItems.erase(pNotification); + onChange(pNotification); // our payload is const, so make a copy before changing it LLSD newpayload = payload; newpayload["sigtype"] = "delete"; abortProcessing = mChanged(newpayload); - onChange(pNotification); } // didn't pass, not on our list, do nothing } @@ -1041,8 +1061,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // not in our list, add it and say so mItems.insert(pNotification); - abortProcessing = mChanged(payload); onAdd(pNotification); + abortProcessing = mChanged(payload); } } else if (cmd == "delete") @@ -1050,65 +1070,35 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt // if we have it in our list, pass on the delete, then delete it, else do nothing if (wasFound) { + onDelete(pNotification); abortProcessing = mChanged(payload); - // do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window - if( ! pNotification->isReusable() ) - { - mItems.erase(pNotification); - onDelete(pNotification); - } + mItems.erase(pNotification); } } return abortProcessing; } -/* static */ -LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name, - const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) +LLNotificationChannel::LLNotificationChannel(const Params& p) +: LLNotificationChannelBase(p.filter()), + LLInstanceTracker<LLNotificationChannel, std::string>(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()), + mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()) +{ + BOOST_FOREACH(const std::string& source, p.sources) { - // note: this is not a leak; notifications are self-registering. - // This factory helps to prevent excess deletions by making sure all smart - // pointers to notification channels come from the same source - new LLNotificationChannel(name, parent, filter, comparator); - return LLNotifications::instance().getChannel(name); + connectToChannel(source); + } } LLNotificationChannel::LLNotificationChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) : -LLNotificationChannelBase(filter, comparator), -mName(name), -mParent(parent) -{ - // store myself in the channel map - LLNotifications::instance().addChannel(LLNotificationChannelPtr(this)); + LLNotificationFilter filter) +: LLNotificationChannelBase(filter), + LLInstanceTracker<LLNotificationChannel, std::string>(name), + mName(name) +{ // bind to notification broadcast - if (parent.empty()) - { - LLNotifications::instance().connectChanged( - boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } - else - { - LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent); - p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } -} - - -void LLNotificationChannel::setComparator(LLNotificationComparator comparator) -{ - mComparator = comparator; - LLNotificationSet s2(mComparator); - s2.insert(mItems.begin(), mItems.end()); - mItems.swap(s2); - - // notify clients that we've been resorted - mChanged(LLSD().with("sigtype", "sort")); + connectToChannel(parent); } bool LLNotificationChannel::isEmpty() const @@ -1126,6 +1116,11 @@ LLNotificationChannel::Iterator LLNotificationChannel::end() return mItems.end(); } +size_t LLNotificationChannel::size() +{ + return mItems.size(); +} + std::string LLNotificationChannel::summarize() { std::string s("Channel '"); @@ -1139,18 +1134,31 @@ std::string LLNotificationChannel::summarize() return s; } +void LLNotificationChannel::connectToChannel( const std::string& channel_name ) +{ + if (channel_name.empty()) + { + LLNotifications::instance().connectChanged( + boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); + } + else + { + LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name); + p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); + } +} // --- // END OF LLNotificationChannel implementation // ========================================================= -// ========================================================= +// ============================================== =========== // LLNotifications implementation // --- -LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, - LLNotificationComparators::orderByUUID()), - mIgnoreAllNotifications(false) +LLNotifications::LLNotifications() +: LLNotificationChannelBase(LLNotificationFilters::includeEverything), + mIgnoreAllNotifications(false) { LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); @@ -1191,7 +1199,15 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) if (pNotif != existing_notification && pNotif->isEquivalentTo(existing_notification)) { - return false; + if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD) + { + cancel(existing_notification); + return true; + } + else + { + return false; + } } } @@ -1231,43 +1247,43 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload) return false; } - // Update the existing unique notification with the data from this particular instance... - // This guarantees that duplicate notifications will be collapsed to the one - // most recently triggered - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) + switch(pNotif->getCombineBehavior()) { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) + case LLNotification::REPLACE_WITH_NEW: + // Update the existing unique notification with the data from this particular instance... + // This guarantees that duplicate notifications will be collapsed to the one + // most recently triggered + for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); + existing_it != mUniqueNotifications.end(); + ++existing_it) { - // copy notification instance data over to oldest instance - // of this unique notification and update it - existing_notification->updateFrom(pNotif); - // then delete the new one - cancel(pNotif); + LLNotificationPtr existing_notification = existing_it->second; + if (pNotif != existing_notification + && pNotif->isEquivalentTo(existing_notification)) + { + // copy notification instance data over to oldest instance + // of this unique notification and update it + existing_notification->updateFrom(pNotif); + // then delete the new one + cancel(pNotif); + } } + break; + case LLNotification::KEEP_OLD: + break; + case LLNotification::CANCEL_OLD: + // already handled by filter logic + break; + default: + break; } return false; } - -void LLNotifications::addChannel(LLNotificationChannelPtr pChan) -{ - mChannels[pChan->getName()] = pChan; -} - LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) { - ChannelMap::iterator p = mChannels.find(channelName); - if(p == mChannels.end()) - { - llerrs << "Did not find channel named " << channelName << llendl; - return LLNotificationChannelPtr(); - } - return p->second; + return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName)); } @@ -1283,24 +1299,21 @@ void LLNotifications::createDefaultChannels() { // now construct the various channels AFTER loading the notifications, // because the history channel is going to rewrite the stored notifications file - LLNotificationChannel::buildChannel("Enabled", "", - !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)); - LLNotificationChannel::buildChannel("Expiration", "Enabled", - boost::bind(&LLNotifications::expirationFilter, this, _1)); - LLNotificationChannel::buildChannel("Unexpired", "Enabled", - !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind - LLNotificationChannel::buildChannel("Unique", "Unexpired", - boost::bind(&LLNotifications::uniqueFilter, this, _1)); - LLNotificationChannel::buildChannel("Ignore", "Unique", - filterIgnoredNotifications); - LLNotificationChannel::buildChannel("VisibilityRules", "Ignore", - boost::bind(&LLNotifications::isVisibleByRules, this, _1)); - LLNotificationChannel::buildChannel("Visible", "VisibilityRules", - &LLNotificationFilters::includeEverything); - - // create special persistent notification channel - // this isn't a leak, don't worry about the empty "new" - new LLPersistentNotificationChannel(); + mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "", + !boost::bind(&LLNotifications::getIgnoreAllNotifications, this))); + mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled", + boost::bind(&LLNotifications::expirationFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled", + !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind + mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired", + boost::bind(&LLNotifications::uniqueFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique", + filterIgnoredNotifications)); + mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore", + boost::bind(&LLNotifications::isVisibleByRules, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules", + &LLNotificationFilters::includeEverything)); + mDefaultChannels.push_back(new LLPersistentNotificationChannel()); // connect action methods to these channels LLNotifications::instance().getChannel("Enabled")-> @@ -1532,34 +1545,32 @@ void LLNotifications::addFromCallback(const LLSD& name) add(name.asString(), LLSD(), LLSD()); } -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload) { LLNotification::Params::Functor functor_p; functor_p.name = name; return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); } -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - const std::string& functor_name) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name) { LLNotification::Params::Functor functor_p; functor_p.name = functor_name; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); } //virtual -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - LLNotificationFunctorRegistry::ResponseFunctor functor) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor) { LLNotification::Params::Functor functor_p; functor_p.function = functor; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); } // generalized add function that takes a parameter block object for more complex instantiations @@ -1590,12 +1601,11 @@ void LLNotifications::cancel(LLNotificationPtr pNotif) if (pNotif == NULL || pNotif->isCancelled()) return; LLNotificationSet::iterator it=mItems.find(pNotif); - if (it == mItems.end()) + if (it != mItems.end()) { - llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl; + pNotif->cancel(); + updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); } - pNotif->cancel(); - updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); } void LLNotifications::cancelByName(const std::string& name) @@ -1634,7 +1644,7 @@ void LLNotifications::update(const LLNotificationPtr pNotif) LLNotificationPtr LLNotifications::find(LLUUID uuid) { - LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid)); + LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid))); LLNotificationSet::iterator it=mItems.find(target); if (it == mItems.end()) { diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 4ae02b943f..19b30b8973 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -88,16 +88,14 @@ #include <boost/enable_shared_from_this.hpp> #include <boost/type_traits.hpp> -// we want to minimize external dependencies, but this one is important -#include "llsd.h" - -// and we need this to manage the notification callbacks #include "llevents.h" #include "llfunctorregistry.h" -#include "llpointer.h" #include "llinitparam.h" #include "llnotificationslistener.h" #include "llnotificationptr.h" +#include "llpointer.h" +#include "llrefcount.h" +#include "llsdparam.h" class LLAvatarName; typedef enum e_notification_priority @@ -164,6 +162,7 @@ public: struct FormElementBase : public LLInitParam::Block<FormElementBase> { Optional<std::string> name; + Optional<bool> enabled; FormElementBase(); }; @@ -233,16 +232,20 @@ public: } EIgnoreType; LLNotificationForm(); + LLNotificationForm(const LLNotificationForm&); LLNotificationForm(const LLSD& sd); LLNotificationForm(const std::string& name, const Params& p); + void fromLLSD(const LLSD& sd); LLSD asLLSD() const; S32 getNumElements() { return mFormData.size(); } LLSD getElement(S32 index) { return mFormData.get(index); } LLSD getElement(const std::string& element_name); - bool hasElement(const std::string& element_name); - void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD()); + bool hasElement(const std::string& element_name) const; + bool getElementEnabled(const std::string& element_name) const; + void setElementEnabled(const std::string& element_name, bool enabled); + void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true); void formatElements(const LLSD& substitutions); // appends form elements from another form serialized as LLSD void append(const LLSD& sub_form); @@ -296,19 +299,20 @@ LOG_CLASS(LLNotification); friend class LLNotifications; public: + // parameter object used to instantiate a new notification struct Params : public LLInitParam::Block<Params> { friend class LLNotification; Mandatory<std::string> name; - - // optional - Optional<LLSD> substitutions; - Optional<LLSD> payload; + Optional<LLUUID> id; + Optional<LLSD> substitutions, + form_elements, + payload; Optional<ENotificationPriority, NotificationPriorityValues> priority; - Optional<LLSD> form_elements; - Optional<LLDate> time_stamp; + Optional<LLDate> time_stamp, + expiry; Optional<LLNotificationContext*> context; Optional<void*> responder; @@ -319,7 +323,7 @@ public: Alternative<LLNotificationResponderPtr> responder; Functor() - : name("functor_name"), + : name("responseFunctor"), function("functor"), responder("responder") {} @@ -328,10 +332,13 @@ public: Params() : name("name"), + id("id"), priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time_stamp"), + time_stamp("time"), payload("payload"), - form_elements("form_elements") + form_elements("form"), + substitutions("substitutions"), + expiry("expiry") { time_stamp = LLDate::now(); responder = NULL; @@ -340,9 +347,11 @@ public: Params(const std::string& _name) : name("name"), priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time_stamp"), + time_stamp("time"), payload("payload"), - form_elements("form_elements") + form_elements("form"), + substitutions("substitutions"), + expiry("expiry") { functor.name = _name; name = _name; @@ -355,7 +364,7 @@ public: private: - LLUUID mId; + const LLUUID mId; LLSD mPayload; LLSD mSubstitutions; LLDate mTimestamp; @@ -367,7 +376,6 @@ private: ENotificationPriority mPriority; LLNotificationFormPtr mForm; void* mResponderObj; // TODO - refactor/remove this field - bool mIsReusable; LLNotificationResponderPtr mResponder; // a reference to the template @@ -392,18 +400,10 @@ private: void init(const std::string& template_name, const LLSD& form_elements); - LLNotification(const Params& p); - - // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT - // for anything real! - LLNotification(LLUUID uuid) : mId(uuid), mCancelled(false), mRespondedTo(false), mIgnored(false), mPriority(NOTIFICATION_PRIORITY_UNSPECIFIED), mTemporaryResponder(false) {} - void cancel(); public: - - // constructor from a saved notification - LLNotification(const LLSD& sd); + LLNotification(const LLSDParamAdapter<Params>& p); void setResponseFunctor(std::string const &responseFunctorName); @@ -448,6 +448,11 @@ public: // ["responseFunctor"] = name of registered functor that handles responses to notification; LLSD asLLSD(); + const LLNotificationFormPtr getForm(); + void updateForm(const LLNotificationFormPtr& form); + + void repost(); + void respond(const LLSD& sd); void respondWithDefault(); @@ -514,8 +519,20 @@ public: std::string getURL() const; S32 getURLOption() const; S32 getURLOpenExternally() const; + bool canLogToChat() const; + bool canLogToIM() const; + bool canShowToast() const; + bool hasFormElements() const; + + typedef enum e_combine_behavior + { + REPLACE_WITH_NEW, + KEEP_OLD, + CANCEL_OLD + + } ECombineBehavior; - const LLNotificationFormPtr getForm(); + ECombineBehavior getCombineBehavior() const; const LLDate getExpiration() const { @@ -532,10 +549,6 @@ public: return mId; } - bool isReusable() { return mIsReusable; } - - void setReusable(bool reusable) { mIsReusable = reusable; } - // comparing two notifications normally means comparing them by UUID (so we can look them // up quickly this way) bool operator<(const LLNotification& rhs) const @@ -647,44 +660,18 @@ namespace LLNotificationFilters namespace LLNotificationComparators { - typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection; - - // generic order functor that takes method or member variable reference - template<typename T> - struct orderBy + struct orderByUUID { - typedef boost::function<T (LLNotificationPtr)> field_t; - orderBy(field_t field, EDirection direction = ORDER_INCREASING) : mField(field), mDirection(direction) {} bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) { - if (mDirection == ORDER_DECREASING) - { - return mField(lhs) > mField(rhs); - } - else - { - return mField(lhs) < mField(rhs); - } + return lhs->id() < rhs->id(); } - - field_t mField; - EDirection mDirection; - }; - - struct orderByUUID : public orderBy<const LLUUID&> - { - orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {} - }; - - struct orderByDate : public orderBy<const LLDate&> - { - orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {} }; }; typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter; typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator; -typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet; +typedef std::set<LLNotificationPtr, LLNotificationComparators::orderByUUID> LLNotificationSet; typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap; // ======================================================== @@ -705,12 +692,14 @@ typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap; // all of the built-in tests should attach to the "Visible" channel // class LLNotificationChannelBase : - public LLEventTrackable + public LLEventTrackable, + public LLRefCount { LOG_CLASS(LLNotificationChannelBase); public: - LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) : - mFilter(filter), mItems(comp) + LLNotificationChannelBase(LLNotificationFilter filter) + : mFilter(filter), + mItems() {} virtual ~LLNotificationChannelBase() {} // you can also connect to a Channel, so you can be notified of @@ -785,59 +774,49 @@ protected: // destroy it, but if it becomes necessary to do so, the shared_ptr model // will ensure that we don't leak resources. class LLNotificationChannel; -typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr; +typedef boost::intrusive_ptr<LLNotificationChannel> LLNotificationChannelPtr; // manages a list of notifications // Note that if this is ever copied around, we might find ourselves with multiple copies // of a queue with notifications being added to different nonequivalent copies. So we -// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it. -// -// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to -// do something like: -// LLNotificationChannel::buildChannel("name", "parent"...); -// This returns an LLNotificationChannelPtr, which you can store, or -// you can then retrieve the channel by using the registry: -// LLNotifications::instance().getChannel("name")... +// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it. // class LLNotificationChannel : boost::noncopyable, - public LLNotificationChannelBase + public LLNotificationChannelBase, + public LLInstanceTracker<LLNotificationChannel, std::string> { LOG_CLASS(LLNotificationChannel); public: + // Notification Channels have a filter, which determines which notifications + // will be added to this channel. + // Channel filters cannot change. + struct Params : public LLInitParam::Block<Params> + { + Mandatory<std::string> name; + Optional<LLNotificationFilter> filter; + Multiple<std::string> sources; + }; + + LLNotificationChannel(const Params& p = Params()); + LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter); + virtual ~LLNotificationChannel() {} typedef LLNotificationSet::iterator Iterator; std::string getName() const { return mName; } - std::string getParentChannelName() { return mParent; } + + void connectToChannel(const std::string& channel_name); bool isEmpty() const; Iterator begin(); Iterator end(); + size_t size(); - // Channels have a comparator to control sort order; - // the default sorts by arrival date - void setComparator(LLNotificationComparator comparator); - std::string summarize(); - // factory method for constructing these channels; since they're self-registering, - // we want to make sure that you can't use new to make them - static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter=LLNotificationFilters::includeEverything, - LLNotificationComparator comparator=LLNotificationComparators::orderByUUID()); - -protected: - // Notification Channels have a filter, which determines which notifications - // will be added to this channel. - // Channel filters cannot change. - // Channels have a protected constructor so you can't make smart pointers that don't - // come from our internal reference; call NotificationChannel::build(args) - LLNotificationChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter, LLNotificationComparator comparator); - private: std::string mName; std::string mParent; @@ -924,10 +903,6 @@ public: void createDefaultChannels(); - typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap; - ChannelMap mChannels; - - void addChannel(LLNotificationChannelPtr pChan); LLNotificationChannelPtr getChannel(const std::string& channelName); std::string getGlobalString(const std::string& key) const; @@ -966,6 +941,7 @@ private: bool mIgnoreAllNotifications; boost::scoped_ptr<LLNotificationsListener> mListener; + std::vector<LLNotificationChannelPtr> mDefaultChannels; }; /** @@ -1028,5 +1004,56 @@ protected: std::string mName; }; +// Stores only persistent notifications. +// Class users can use connectChanged() to process persistent notifications +// (see LLNotificationStorage for example). +class LLPersistentNotificationChannel : public LLNotificationChannel +{ + LOG_CLASS(LLPersistentNotificationChannel); +public: + LLPersistentNotificationChannel() + : LLNotificationChannel("Persistent", "Visible", ¬ificationFilter) + { + } + + typedef std::vector<LLNotificationPtr> history_list_t; + history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); } + history_list_t::iterator endHistory() { return mHistory.end(); } + +private: + + struct sortByTime + { + S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b) + { + return a->getDate() < b->getDate(); + } + }; + + void sortHistory() + { + std::sort(mHistory.begin(), mHistory.end(), sortByTime()); + } + + + // The channel gets all persistent notifications except those that have been canceled + static bool notificationFilter(LLNotificationPtr pNotification) + { + bool handle_notification = false; + + handle_notification = pNotification->isPersistent() + && !pNotification->isCancelled(); + + return handle_notification; + } + + void onAdd(LLNotificationPtr p) + { + mHistory.push_back(p); + } + + std::vector<LLNotificationPtr> mHistory; +}; + #endif//LL_LLNOTIFICATIONS_H diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp index 3bbeb3a778..e4e127336b 100644 --- a/indra/llui/llnotificationslistener.cpp +++ b/indra/llui/llnotificationslistener.cpp @@ -121,13 +121,13 @@ void LLNotificationsListener::listChannels(const LLSD& params) const { LLReqID reqID(params); LLSD response(reqID.makeResponse()); - for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()), - cmend(mNotifications.mChannels.end()); + for (LLNotificationChannel::instance_iter cmi(LLNotificationChannel::beginInstances()), + cmend(LLNotificationChannel::endInstances()); cmi != cmend; ++cmi) { LLSD channelInfo; - channelInfo["parent"] = cmi->second->getParentChannelName(); - response[cmi->first] = channelInfo; + //channelInfo["parent"] = cmi->second->getParentChannelName(); + response[cmi->getName()] = channelInfo; } LLEventPumps::instance().obtain(params["reply"]).post(response); } diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h index 72973789db..c9e9d0f32a 100644 --- a/indra/llui/llnotificationtemplate.h +++ b/indra/llui/llnotificationtemplate.h @@ -61,6 +61,18 @@ typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr; // from the appropriate local language directory). struct LLNotificationTemplate { + struct CombineBehaviorNames + : public LLInitParam::TypeValuesHelper<LLNotification::ECombineBehavior, CombineBehaviorNames> + { + static void declareValues() + { + declare("replace_with_new", LLNotification::REPLACE_WITH_NEW); + declare("keep_old", LLNotification::KEEP_OLD); + declare("cancel_old", LLNotification::CANCEL_OLD); + } + }; + + struct GlobalString : public LLInitParam::Block<GlobalString> { Mandatory<std::string> name, @@ -94,9 +106,11 @@ struct LLNotificationTemplate Optional<LLInitParam::Flag> dummy_val; public: Multiple<UniquenessContext> contexts; + Optional<LLNotification::ECombineBehavior, CombineBehaviorNames> combine; UniquenessConstraint() : contexts("context"), + combine("combine", LLNotification::REPLACE_WITH_NEW), dummy_val("") {} }; @@ -181,7 +195,10 @@ struct LLNotificationTemplate struct Params : public LLInitParam::Block<Params> { Mandatory<std::string> name; - Optional<bool> persist; + Optional<bool> persist, + log_to_im, + show_toast, + log_to_chat; Optional<std::string> functor, icon, label, @@ -202,6 +219,9 @@ struct LLNotificationTemplate Params() : name("name"), persist("persist", false), + log_to_im("log_to_im", false), + show_toast("show_toast", true), + log_to_chat("log_to_chat", true), functor("functor"), icon("icon"), label("label"), @@ -260,6 +280,7 @@ struct LLNotificationTemplate // (used for things like progress indications, or repeating warnings // like "the grid is going down in N minutes") bool mUnique; + LLNotification::ECombineBehavior mCombineBehavior; // if we want to be unique only if a certain part of the payload or substitutions args // are constant specify the field names for the payload. The notification will only be // combined if all of the fields named in the context are identical in the @@ -306,6 +327,11 @@ struct LLNotificationTemplate LLUUID mSoundEffect; // List of tags that rules can match against. std::list<std::string> mTags; + + // inject these notifications into chat/IM streams + bool mLogToChat; + bool mLogToIM; + bool mShowToast; }; #endif //LL_LLNOTIFICATION_TEMPLATE_H diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 5fc2cc350d..d0920685bf 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1209,7 +1209,11 @@ void LLTabContainer::removeTabPanel(LLPanel* child) update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition()); } - removeChild( tuple->mButton ); + if (!getTabsHidden()) + { + // We need to remove tab buttons only if the tabs are not hidden. + removeChild( tuple->mButton ); + } delete tuple->mButton; removeChild( tuple->mTabPanel ); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 3815eec447..98624f42b9 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -145,6 +145,7 @@ LLTextBase::Params::Params() : cursor_color("cursor_color"), text_color("text_color"), text_readonly_color("text_readonly_color"), + text_tentative_color("text_tentative_color"), bg_visible("bg_visible", false), border_visible("border_visible", false), bg_readonly_color("bg_readonly_color"), @@ -179,7 +180,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) : LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), mURLClickSignal(NULL), mMaxTextByteLength( p.max_text_length ), - mDefaultFont(p.font), + mFont(p.font), mFontShadow(p.font_shadow), mPopupMenu(NULL), mReadOnly(p.read_only), @@ -190,6 +191,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mFgColor(p.text_color), mBorderVisible( p.border_visible ), mReadOnlyFgColor(p.text_readonly_color), + mTentativeFgColor(p.text_tentative_color()), mWriteableBgColor(p.bg_writeable_color), mReadOnlyBgColor(p.bg_readonly_color), mFocusBgColor(p.bg_focus_color), @@ -319,21 +321,21 @@ bool LLTextBase::truncate() return did_truncate; } -const LLStyle::Params& LLTextBase::getDefaultStyleParams() +const LLStyle::Params& LLTextBase::getStyleParams() { //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html //and eliminate color member values if (mStyleDirty) { - mDefaultStyle + mStyle .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor .readonly_color(LLUIColor(&mReadOnlyFgColor)) .selected_color(LLUIColor(&mTextSelectedColor)) - .font(mDefaultFont) + .font(mFont) .drop_shadow(mFontShadow); mStyleDirty = false; } - return mDefaultStyle; + return mStyle; } void LLTextBase::onValueChange(S32 start, S32 end) @@ -522,11 +524,17 @@ void LLTextBase::drawCursor() void LLTextBase::drawText() { - const S32 text_len = getLength(); - if( text_len <= 0 ) + S32 text_len = getLength(); + + if (text_len <= 0 && mLabel.empty()) { return; } + else if (text_len <= 0 && !mLabel.empty() && !hasFocus()) + { + text_len = mLabel.getWString().length(); + } + S32 selection_left = -1; S32 selection_right = -1; // Draw selection even if we don't have keyboard focus for search/replace @@ -770,7 +778,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s else { // create default editable segment to hold new text - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this); } @@ -890,7 +898,7 @@ void LLTextBase::createDefaultSegment() // ensures that there is always at least one segment if (mSegments.empty()) { - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this); mSegments.insert(default_segment); default_segment->linkToDocument(this); @@ -1338,6 +1346,23 @@ void LLTextBase::onSpellCheckSettingsChange() mSpellCheckStart = mSpellCheckEnd = -1; } +void LLTextBase::onFocusReceived() +{ + if (!getLength() && !mLabel.empty()) + { + // delete label which is LLLabelTextSegment + clearSegments(); + } +} + +void LLTextBase::onFocusLost() +{ + if (!getLength() && !mLabel.empty()) + { + resetLabel(); + } +} + // Sets the scrollbar from the cursor position void LLTextBase::updateScrollFromCursor() { @@ -1924,7 +1949,7 @@ static LLFastTimer::DeclareTimer FTM_PARSE_HTML("Parse HTML"); void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); - style_params.fillFrom(getDefaultStyleParams()); + style_params.fillFrom(getStyleParams()); S32 part = (S32)LLTextParser::WHOLE; if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). @@ -2009,6 +2034,39 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c appendTextImpl(new_text,input_params); } +void LLTextBase::setLabel(const LLStringExplicit& label) +{ + mLabel = label; + resetLabel(); +} + +BOOL LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return TRUE; +} + +void LLTextBase::resetLabel() +{ + if (!getLength() && !mLabel.empty() && !hasFocus()) + { + clearSegments(); + + LLStyle* style = new LLStyle(getStyleParams()); + style->setColor(mTentativeFgColor); + LLStyleConstSP sp(style); + + LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this); + insertSegment(label); + } +} + +void LLTextBase::setFont(const LLFontGL* font) +{ + mFont = font; + mStyleDirty = true; +} + void LLTextBase::needsReflow(S32 index) { lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl; @@ -2399,7 +2457,7 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const { // return default height rect in upper left local_rect = content_window_rect; - local_rect.mBottom = local_rect.mTop - mDefaultFont->getLineHeight(); + local_rect.mBottom = local_rect.mTop - mFont->getLineHeight(); return local_rect; } @@ -2904,7 +2962,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele { F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); F32 right_x = rect.mLeft; if (!mStyle->isVisible()) @@ -3067,7 +3125,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt if (num_chars > 0) { height = mFontHeight; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); // if last character is a newline, then return true, forcing line break width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); } @@ -3076,7 +3134,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, (F32)segment_local_x_coord, F32_MAX, @@ -3086,7 +3144,7 @@ S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); LLUIImagePtr image = mStyle->getImage(); if( image.notNull()) @@ -3122,7 +3180,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin S32 last_char_in_run = mStart + segment_offset + num_chars; // check length first to avoid indexing off end of string if (last_char_in_run < mEnd - && (last_char_in_run >= mEditor.getLength() )) + && (last_char_in_run >= getLength())) { num_chars++; } @@ -3140,6 +3198,39 @@ void LLNormalTextSegment::dump() const llendl; } +/*virtual*/ +const LLWString& LLNormalTextSegment::getWText() const +{ + return mEditor.getWText(); +} + +/*virtual*/ +const S32 LLNormalTextSegment::getLength() const +{ + return mEditor.getLength(); +} + +LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) +: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +/*virtual*/ +const LLWString& LLLabelTextSegment::getWText() const +{ + return mEditor.getWlabel(); +} +/*virtual*/ +const S32 LLLabelTextSegment::getLength() const +{ + return mEditor.getWlabel().length(); +} + // // LLOnHoverChangeableTextSegment // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 90b147cee1..79662ebd33 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -106,7 +106,7 @@ class LLNormalTextSegment : public LLTextSegment public: LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); - ~LLNormalTextSegment(); + virtual ~LLNormalTextSegment(); /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; @@ -131,6 +131,9 @@ public: protected: F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect); + virtual const LLWString& getWText() const; + virtual const S32 getLength() const; + protected: class LLTextBase& mEditor; LLStyleConstSP mStyle; @@ -140,6 +143,21 @@ protected: boost::signals2::connection mImageLoadedConnection; }; +// This text segment is the same as LLNormalTextSegment, the only difference +// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()), +// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel) +class LLLabelTextSegment : public LLNormalTextSegment +{ +public: + LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); + LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + +protected: + + /*virtual*/ const LLWString& getWText() const; + /*virtual*/ const S32 getLength() const; +}; + // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) class LLOnHoverChangeableTextSegment : public LLNormalTextSegment { @@ -251,6 +269,7 @@ public: Optional<LLUIColor> cursor_color, text_color, text_readonly_color, + text_tentative_color, bg_readonly_color, bg_writeable_color, bg_focus_color, @@ -314,6 +333,9 @@ public: /*virtual*/ BOOL canDeselect() const; /*virtual*/ void deselect(); + virtual void onFocusReceived(); + virtual void onFocusLost(); + // LLSpellCheckMenuHandler overrides /*virtual*/ bool getSpellCheck() const; @@ -351,6 +373,21 @@ public: const LLWString& getWText() const; void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); + + void setLabel(const LLStringExplicit& label); + virtual BOOL setLabelArg(const std::string& key, const LLStringExplicit& text ); + + const std::string& getLabel() { return mLabel.getString(); } + const LLWString& getWlabel() { return mLabel.getWString();} + + /** + * If label is set, draws text label (which is LLLabelTextSegment) + * that is visible when no user text provided and has no focus + */ + void resetLabel(); + + void setFont(const LLFontGL* font); + // force reflow of text void needsReflow(S32 index = 0); @@ -390,7 +427,7 @@ public: bool scrolledToStart(); bool scrolledToEnd(); - const LLFontGL* getDefaultFont() const { return mDefaultFont; } + const LLFontGL* getFont() const { return mFont; } virtual void appendLineBreakSegment(const LLStyle::Params& style_params); virtual void appendImageSegment(const LLStyle::Params& style_params); @@ -490,7 +527,7 @@ protected: void createDefaultSegment(); virtual void updateSegments(); void insertSegment(LLTextSegmentPtr segment_to_insert); - const LLStyle::Params& getDefaultStyleParams(); + const LLStyle::Params& getStyleParams(); // manage lines S32 getLineStart( S32 line ) const; @@ -535,15 +572,16 @@ protected: LLRect mTextBoundingRect; // default text style - LLStyle::Params mDefaultStyle; + LLStyle::Params mStyle; bool mStyleDirty; - const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor - const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor + const LLFontGL* mFont; + const LLFontGL::ShadowType mFontShadow; // colors LLUIColor mCursorColor; LLUIColor mFgColor; LLUIColor mReadOnlyFgColor; + LLUIColor mTentativeFgColor; LLUIColor mWriteableBgColor; LLUIColor mReadOnlyBgColor; LLUIColor mFocusBgColor; @@ -587,6 +625,7 @@ protected: bool mClip; // clip text to widget rect bool mClipPartial; // false if we show lines that are partially inside bounding rect bool mPlainText; // didn't use Image or Icon segments + bool mAutoIndent; S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes // support widgets @@ -602,6 +641,7 @@ protected: // Fired when a URL link is clicked commit_signal_t* mURLClickSignal; + LLUIString mLabel; // text label that is visible when no user text provided }; #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 144b6960a1..d42d6473ed 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -237,6 +237,7 @@ LLTextEditor::Params::Params() embedded_items("embedded_items", false), ignore_tab("ignore_tab", true), show_line_numbers("show_line_numbers", false), + auto_indent("auto_indent", true), default_color("default_color"), commit_on_focus_lost("commit_on_focus_lost", false), show_context_menu("show_context_menu") @@ -251,6 +252,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mLastCmd( NULL ), mDefaultColor( p.default_color() ), mShowLineNumbers ( p.show_line_numbers ), + mAutoIndent(p.auto_indent), mCommitOnFocusLost( p.commit_on_focus_lost), mAllowEmbeddedItems( p.embedded_items ), mMouseDownX(0), @@ -258,7 +260,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mTabsToNextField(p.ignore_tab), mPrevalidateFunc(p.prevalidate_callback()), mContextMenu(NULL), - mShowContextMenu(p.show_context_menu) + mShowContextMenu(p.show_context_menu), + mPassDelete(FALSE) { mSourceID.generate(); @@ -1608,7 +1611,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) { deleteSelection(FALSE); } - autoIndent(); // TODO: make this optional + if (mAutoIndent) + { + autoIndent(); + } } else { @@ -1748,7 +1754,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // virtual BOOL LLTextEditor::canDoDelete() const { - return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) ); + return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); } void LLTextEditor::doDelete() @@ -2017,7 +2023,7 @@ void LLTextEditor::drawPreeditMarker() return; } - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); S32 line_start = getLineStart(cur_line); S32 line_y = mVisibleTextRect.mTop - line_height; @@ -2056,16 +2062,16 @@ void LLTextEditor::drawPreeditMarker() S32 preedit_left = mVisibleTextRect.mLeft; if (left > line_start) { - preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start); + preedit_left += mFont->getWidth(text, line_start, left - line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (right < line_end) { - preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start); + preedit_right += mFont->getWidth(text, line_start, right - line_start); } else { - preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start); + preedit_right += mFont->getWidth(text, line_start, line_end - line_start); } if (mPreeditStandouts[i]) @@ -2740,11 +2746,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect const LLWString textString(getWText()); const llwchar * const text = textString.c_str(); - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); if (coord) { - const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); + const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start); const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; S32 query_screen_x, query_screen_y; localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); @@ -2756,17 +2762,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect S32 preedit_left = mVisibleTextRect.mLeft; if (preedit_left_position > current_line_start) { - preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); + preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (preedit_right_position < current_line_end) { - preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); } else { - preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start); } const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; @@ -2843,7 +2849,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) S32 LLTextEditor::getPreeditFontSize() const { - return llround((F32)mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); + return llround((F32)mFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); } BOOL LLTextEditor::isDirty() const diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 40821ae9fb..f8f636b876 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -64,7 +64,8 @@ public: ignore_tab, show_line_numbers, commit_on_focus_lost, - show_context_menu; + show_context_menu, + auto_indent; //colors Optional<LLUIColor> default_color; @@ -202,6 +203,8 @@ public: void setShowContextMenu(bool show) { mShowContextMenu = show; } bool getShowContextMenu() const { return mShowContextMenu; } + void setPassDelete(BOOL b) { mPassDelete = b; } + protected: void showContextMenu(S32 x, S32 y); void drawPreeditMarker(); @@ -214,8 +217,8 @@ protected: S32 indentLine( S32 pos, S32 spaces ); void unindentLineBeforeCloseBrace(); + virtual BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleNavigationKey(const KEY key, const MASK mask); - BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleSelectionKey(const KEY key, const MASK mask); BOOL handleControlKey(const KEY key, const MASK mask); @@ -279,6 +282,7 @@ protected: LLUIColor mDefaultColor; BOOL mShowLineNumbers; + bool mAutoIndent; /*virtual*/ void updateSegments(); void updateLinkSegments(); @@ -321,6 +325,7 @@ private: BOOL mAllowEmbeddedItems; bool mShowContextMenu; bool mParseOnTheFly; + bool mPassDelete; LLUUID mSourceID; diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index 2094bd776f..dd9ac5b8c1 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -58,6 +58,8 @@ public: // its visibility off. bool toggleVisibility(); + LLHandle<LLToggleableMenu> getHandle() { return getDerivedHandle<LLToggleableMenu>(); } + protected: bool mClosedByButtonClick; LLRect mButtonRect; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 87bf518aa1..41a948e545 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -978,31 +978,31 @@ void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha) { if (!LLGLSLShader::sNoFixedFunction) { - // Initialize the first time this is called. - const S32 PIXELS = 32; - static GLubyte checkerboard[PIXELS * PIXELS]; - static BOOL first = TRUE; - if( first ) + // Initialize the first time this is called. + const S32 PIXELS = 32; + static GLubyte checkerboard[PIXELS * PIXELS]; + static BOOL first = TRUE; + if( first ) + { + for( S32 i = 0; i < PIXELS; i++ ) { - for( S32 i = 0; i < PIXELS; i++ ) + for( S32 j = 0; j < PIXELS; j++ ) { - for( S32 j = 0; j < PIXELS; j++ ) - { - checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF; - } + checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF; } - first = FALSE; } + first = FALSE; + } - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - // ...white squares - gGL.color4f( 1.f, 1.f, 1.f, alpha ); - gl_rect_2d(rect); + // ...white squares + gGL.color4f( 1.f, 1.f, 1.f, alpha ); + gl_rect_2d(rect); - // ...gray squares - gGL.color4f( .7f, .7f, .7f, alpha ); - gGL.flush(); + // ...gray squares + gGL.color4f( .7f, .7f, .7f, alpha ); + gGL.flush(); glPolygonStipple( checkerboard ); @@ -1478,144 +1478,132 @@ void gl_segmented_rect_2d_fragment_tex(const S32 left, gGL.popUIMatrix(); } -void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, - const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, - const U32 edges) +void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, + const LLVector3& width_vec, const LLVector3& height_vec) { - LLVector3 left_border_width = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? border_width : LLVector3::zero; - LLVector3 right_border_width = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? border_width : LLVector3::zero; - - LLVector3 top_border_height = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? border_height : LLVector3::zero; - LLVector3 bottom_border_height = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? border_height : LLVector3::zero; - - gGL.begin(LLRender::QUADS); { // draw bottom left - gGL.texCoord2f(0.f, 0.f); + gGL.texCoord2f(clip_rect.mLeft, clip_rect.mBottom); gGL.vertex3f(0.f, 0.f, 0.f); - gGL.texCoord2f(border_scale.mV[VX], 0.f); - gGL.vertex3fv(left_border_width.mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(0.f, border_scale.mV[VY]); - gGL.vertex3fv(bottom_border_height.mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV); // draw bottom middle - gGL.texCoord2f(border_scale.mV[VX], 0.f); - gGL.vertex3fv(left_border_width.mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f); - gGL.vertex3fv((width_vec - right_border_width).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); // draw bottom right - gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f); - gGL.vertex3fv((width_vec - right_border_width).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV); - gGL.texCoord2f(1.f, 0.f); + gGL.texCoord2f(clip_rect.mRight, clip_rect.mBottom); gGL.vertex3fv(width_vec.mV); - gGL.texCoord2f(1.f, border_scale.mV[VY]); - gGL.vertex3fv((width_vec + bottom_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); // draw left - gGL.texCoord2f(0.f, border_scale.mV[VY]); - gGL.vertex3fv(bottom_border_height.mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); // draw middle - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); // draw right - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f, border_scale.mV[VY]); - gGL.vertex3fv((width_vec + bottom_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec + height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); // draw top left - gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f); - gGL.vertex3fv((left_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); - gGL.texCoord2f(0.f, 1.f); + gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop); gGL.vertex3fv((height_vec).mV); // draw top middle - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f); - gGL.vertex3fv((width_vec - right_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f); - gGL.vertex3fv((left_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); // draw top right - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec + height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f, 1.f); + gGL.texCoord2f(clip_rect.mRight, clip_rect.mTop); gGL.vertex3fv((width_vec + height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f); - gGL.vertex3fv((width_vec - right_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV); } gGL.end(); } -void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec) -{ - gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP); -} void LLUI::initClass(const settings_map_t& settings, LLImageProviderInterface* image_provider, @@ -2118,7 +2106,7 @@ const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) namespace LLInitParam { - ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color) + ParamValue<LLUIColor>::ParamValue(const LLUIColor& color) : super_t(color), red("red"), green("green"), @@ -2129,7 +2117,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock() + void ParamValue<LLUIColor>::updateValueFromBlock() { if (control.isProvided() && !control().empty()) { @@ -2141,7 +2129,7 @@ namespace LLInitParam } } - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue<LLUIColor>::updateBlockFromValue(bool make_block_authoritative) { LLColor4 color = getValue(); red.set(color.mV[VRED], make_block_authoritative); @@ -2157,7 +2145,7 @@ namespace LLInitParam && !(b->getFontDesc() < a->getFontDesc()); } - ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp) + ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp) : super_t(fontp), name("name"), size("size"), @@ -2171,7 +2159,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock() + void ParamValue<const LLFontGL*>::updateValueFromBlock() { const LLFontGL* res_fontp = LLFontGL::getFontByName(name); if (res_fontp) @@ -2194,7 +2182,7 @@ namespace LLInitParam } } - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue<const LLFontGL*>::updateBlockFromValue(bool make_block_authoritative) { if (getValue()) { @@ -2204,7 +2192,7 @@ namespace LLInitParam } } - ParamValue<LLRect, TypeValues<LLRect> >::ParamValue(const LLRect& rect) + ParamValue<LLRect>::ParamValue(const LLRect& rect) : super_t(rect), left("left"), top("top"), @@ -2216,7 +2204,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue<LLRect, TypeValues<LLRect> >::updateValueFromBlock() + void ParamValue<LLRect>::updateValueFromBlock() { LLRect rect; @@ -2280,7 +2268,7 @@ namespace LLInitParam updateValue(rect); } - void ParamValue<LLRect, TypeValues<LLRect> >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue<LLRect>::updateBlockFromValue(bool make_block_authoritative) { // because of the ambiguity in specifying a rect by position and/or dimensions // we use the lowest priority pairing so that any valid pairing in xui @@ -2297,7 +2285,7 @@ namespace LLInitParam height.set(value.getHeight(), make_block_authoritative); } - ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::ParamValue(const LLCoordGL& coord) + ParamValue<LLCoordGL>::ParamValue(const LLCoordGL& coord) : super_t(coord), x("x"), y("y") @@ -2305,12 +2293,12 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::updateValueFromBlock() + void ParamValue<LLCoordGL>::updateValueFromBlock() { updateValue(LLCoordGL(x, y)); } - void ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue<LLCoordGL>::updateBlockFromValue(bool make_block_authoritative) { x.set(getValue().mX, make_block_authoritative); y.set(getValue().mY, make_block_authoritative); diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 28e84fa444..1fbfbd7a07 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -127,8 +127,7 @@ typedef enum e_rounded_edge void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL); void gl_segmented_rect_2d_fragment_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL); -void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, U32 edges = ROUNDED_RECT_ALL); -void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec); +void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, const LLVector3& width_vec, const LLVector3& height_vec); inline void gl_rect_2d( const LLRect& rect, BOOL filled ) { @@ -512,7 +511,7 @@ public: namespace LLInitParam { template<> - class ParamValue<LLRect, TypeValues<LLRect> > + class ParamValue<LLRect> : public CustomParamValue<LLRect> { typedef CustomParamValue<LLRect> super_t; @@ -531,7 +530,7 @@ namespace LLInitParam }; template<> - class ParamValue<LLUIColor, TypeValues<LLUIColor> > + class ParamValue<LLUIColor> : public CustomParamValue<LLUIColor> { typedef CustomParamValue<LLUIColor> super_t; @@ -549,7 +548,7 @@ namespace LLInitParam }; template<> - class ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> > + class ParamValue<const LLFontGL*> : public CustomParamValue<const LLFontGL* > { typedef CustomParamValue<const LLFontGL*> super_t; @@ -589,7 +588,7 @@ namespace LLInitParam template<> - class ParamValue<LLCoordGL, TypeValues<LLCoordGL> > + class ParamValue<LLCoordGL> : public CustomParamValue<LLCoordGL> { typedef CustomParamValue<LLCoordGL> super_t; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 25e7a31e90..f64f33bc5e 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -278,13 +278,13 @@ const LLInitParam::BaseBlock& get_empty_param_block() // adds a widget and its param block to various registries //static -void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag) +void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name) { // associate parameter block type with template .xml file - std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type); - if (existing_tag != NULL) + std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type); + if (existing_name != NULL) { - if(*existing_tag != tag) + if(*existing_name != name) { std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl; // forcing crash here @@ -293,18 +293,19 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st } else { - // widget already registered + // widget already registered this name return; } } - LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag); + + LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name); //FIXME: comment this in when working on schema generation //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type); //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block<T>); } //static -const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type) +const std::string* LLUICtrlFactory::getWidgetName(const std::type_info* widget_type) { return LLWidgetNameRegistry::instance().getValue(widget_type); } diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index d612ad5005..b441cb0c9d 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -105,7 +105,7 @@ private: ParamDefaults() { // look up template file for this param block... - const std::string* param_block_tag = getWidgetTag(&typeid(PARAM_BLOCK)); + const std::string* param_block_tag = getWidgetName(&typeid(PARAM_BLOCK)); if (param_block_tag) { // ...and if it exists, back fill values using the most specific template first PARAM_BLOCK params; @@ -303,7 +303,7 @@ private: } - static const std::string* getWidgetTag(const std::type_info* widget_type); + static const std::string* getWidgetName(const std::type_info* widget_type); // this exists to get around dependency on llview static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group); diff --git a/indra/llui/lluiimage.cpp b/indra/llui/lluiimage.cpp index 1d9ce29ba9..9ed98f941f 100644 --- a/indra/llui/lluiimage.cpp +++ b/indra/llui/lluiimage.cpp @@ -112,6 +112,50 @@ void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& drawSolid(border_rect, color); } +void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, + const LLRect& rect, const LLColor4& color) +{ + F32 border_scale = 1.f; + F32 border_height = (1.f - mScaleRegion.getHeight()) * getHeight(); + F32 border_width = (1.f - mScaleRegion.getWidth()) * getWidth(); + if (rect.getHeight() < border_height || rect.getWidth() < border_width) + { + if(border_height - rect.getHeight() > border_width - rect.getWidth()) + { + border_scale = (F32)rect.getHeight() / border_height; + } + else + { + border_scale = (F32)rect.getWidth() / border_width; + } + } + + LLUI::pushMatrix(); + { + LLVector3 rect_origin = origin_agent + (rect.mLeft * x_axis) + (rect.mBottom * y_axis); + LLUI::translate(rect_origin.mV[VX], + rect_origin.mV[VY], + rect_origin.mV[VZ]); + gGL.getTexUnit(0)->bind(getImage()); + gGL.color4fv(color.mV); + + LLRectf center_uv_rect(mClipRegion.mLeft + mScaleRegion.mLeft * mClipRegion.getWidth(), + mClipRegion.mBottom + mScaleRegion.mTop * mClipRegion.getHeight(), + mClipRegion.mLeft + mScaleRegion.mRight * mClipRegion.getWidth(), + mClipRegion.mBottom + mScaleRegion.mBottom * mClipRegion.getHeight()); + gl_segmented_rect_3d_tex(mClipRegion, + center_uv_rect, + LLRectf(border_width * border_scale * 0.5f / (F32)rect.getWidth(), + (rect.getHeight() - (border_height * border_scale * 0.5f)) / (F32)rect.getHeight(), + (rect.getWidth() - (border_width * border_scale * 0.5f)) / (F32)rect.getWidth(), + (border_height * border_scale * 0.5f) / (F32)rect.getHeight()), + rect.getWidth() * x_axis, + rect.getHeight() * y_axis); + + } LLUI::popMatrix(); +} + + S32 LLUIImage::getWidth() const { // return clipped dimensions of actual image area @@ -155,7 +199,7 @@ void LLUIImage::onImageLoaded() namespace LLInitParam { - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock() + void ParamValue<LLUIImage*>::updateValueFromBlock() { // The keyword "none" is specifically requesting a null image // do not default to current value. Used to overwrite template images. @@ -172,7 +216,7 @@ namespace LLInitParam } } - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue<LLUIImage*>::updateBlockFromValue(bool make_block_authoritative) { if (getValue() == NULL) { diff --git a/indra/llui/lluiimage.h b/indra/llui/lluiimage.h index f07e8fa746..7817ba1c7b 100644 --- a/indra/llui/lluiimage.h +++ b/indra/llui/lluiimage.h @@ -64,7 +64,9 @@ public: void drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const; void drawBorder(const LLRect& rect, const LLColor4& color, S32 border_width) const { drawBorder(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color, border_width); } void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, getWidth(), getHeight(), color, border_width); } - + + void draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, const LLRect& rect, const LLColor4& color); + const std::string& getName() const { return mName; } virtual S32 getWidth() const; @@ -92,7 +94,7 @@ protected: namespace LLInitParam { template<> - class ParamValue<LLUIImage*, TypeValues<LLUIImage*> > + class ParamValue<LLUIImage*> : public CustomParamValue<LLUIImage*> { typedef boost::add_reference<boost::add_const<LLUIImage*>::type>::type T_const_ref; @@ -100,7 +102,7 @@ namespace LLInitParam public: Optional<std::string> name; - ParamValue(LLUIImage* const& image) + ParamValue(LLUIImage* const& image = NULL) : super_t(image) { updateBlockFromValue(false); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 54843227b7..5c2b3236f6 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -349,7 +349,7 @@ void LLView::removeChild(LLView* child) } else { - llwarns << child->getName() << "is not a child of " << getName() << llendl; + llwarns << "\"" << child->getName() << "\" is not a child of " << getName() << llendl; } updateBoundingRect(); } diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index afc76024d1..3ad5ad7d42 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -42,7 +42,7 @@ #include <boost/spirit/include/classic_core.hpp> #include "lluicolor.h" - +#include "v3math.h" using namespace BOOST_SPIRIT_CLASSIC_NS; const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; @@ -79,7 +79,6 @@ struct Occurs : public LLInitParam::Block<Occurs> {} }; - typedef enum { USE_REQUIRED, @@ -101,14 +100,23 @@ namespace LLInitParam struct Element; struct Group; -struct Choice; struct Sequence; -struct Any; + +struct All : public LLInitParam::Block<All, Occurs> +{ + Multiple< Lazy<Element, IS_A_BLOCK> > elements; + + All() + : elements("element") + { + maxOccurs = 1; + } +}; struct Attribute : public LLInitParam::Block<Attribute> { - Mandatory<std::string> name; - Mandatory<std::string> type; + Mandatory<std::string> name, + type; Mandatory<EUse> use; Attribute() @@ -127,24 +135,13 @@ struct Any : public LLInitParam::Block<Any, Occurs> {} }; -struct All : public LLInitParam::Block<All, Occurs> -{ - Multiple< Lazy<Element> > elements; - - All() - : elements("element") - { - maxOccurs = 1; - } -}; - struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs> { - Alternative< Lazy<Element> > element; - Alternative< Lazy<Group> > group; - Alternative< Lazy<Choice> > choice; - Alternative< Lazy<Sequence> > sequence; - Alternative< Lazy<Any> > any; + Alternative< Lazy<Element, IS_A_BLOCK> > element; + Alternative< Lazy<Group, IS_A_BLOCK> > group; + Alternative< Lazy<Choice, IS_A_BLOCK> > choice; + Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence; + Alternative< Lazy<Any> > any; Choice() : element("element"), @@ -158,11 +155,11 @@ struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs> struct Sequence : public LLInitParam::ChoiceBlock<Sequence, Occurs> { - Alternative< Lazy<Element> > element; - Alternative< Lazy<Group> > group; - Alternative< Lazy<Choice> > choice; - Alternative< Lazy<Sequence> > sequence; - Alternative< Lazy<Any> > any; + Alternative< Lazy<Element, IS_A_BLOCK> > element; + Alternative< Lazy<Group, IS_A_BLOCK> > group; + Alternative< Lazy<Choice> > choice; + Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence; + Alternative< Lazy<Any> > any; }; struct GroupContents : public LLInitParam::ChoiceBlock<GroupContents, Occurs> @@ -247,7 +244,7 @@ struct ComplexType : public LLInitParam::Block<ComplexType, ComplexTypeContents> Optional<bool> mixed; Multiple<Attribute> attribute; - Multiple< Lazy<Element> > elements; + Multiple< Lazy<Element, IS_A_BLOCK > > elements; ComplexType() : name("name"), @@ -313,7 +310,6 @@ public: setNameSpace(ns); }; } - }; // @@ -625,7 +621,7 @@ void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& p nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd"); // add to front of schema - mSchemaNode->addChild(nodep, mSchemaNode); + mSchemaNode->addChild(nodep); } for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); @@ -670,6 +666,7 @@ LLXUIParser::LLXUIParser() registerParserFuncs<S32>(readS32Value, writeS32Value); registerParserFuncs<F32>(readF32Value, writeF32Value); registerParserFuncs<F64>(readF64Value, writeF64Value); + registerParserFuncs<LLVector3>(readVector3Value, writeVector3Value); registerParserFuncs<LLColor4>(readColor4Value, writeColor4Value); registerParserFuncs<LLUIColor>(readUIColorValue, writeUIColorValue); registerParserFuncs<LLUUID>(readUUIDValue, writeUUIDValue); @@ -880,16 +877,24 @@ LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack) it = next_it) { ++next_it; + bool force_new_node = false; + if (it->first.empty()) { it->second = false; continue; } + if (next_it != stack.end() && next_it->first.empty() && next_it->second) + { + force_new_node = true; + } + + out_nodes_t::iterator found_it = mOutNodes.find(it->first); // node with this name not yet written - if (found_it == mOutNodes.end() || it->second) + if (found_it == mOutNodes.end() || it->second || force_new_node) { // make an attribute if we are the last element on the name stack bool is_attribute = next_it == stack.end(); @@ -1144,6 +1149,31 @@ bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_ return false; } +bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast<LLXUIParser&>(parser); + LLVector3* vecp = (LLVector3*)val_ptr; + if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3) + { + return true; + } + + return false; +} + +bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast<LLXUIParser&>(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + LLVector3 vector = *((LLVector3*)val_ptr); + node->setFloatValue(3, vector.mV); + return true; + } + return false; +} + bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr) { LLXUIParser& self = static_cast<LLXUIParser&>(parser); diff --git a/indra/llui/llxuiparser.h b/indra/llui/llxuiparser.h index d7cd256967..e48663e5cc 100644 --- a/indra/llui/llxuiparser.h +++ b/indra/llui/llxuiparser.h @@ -127,6 +127,7 @@ private: static bool readS32Value(Parser& parser, void* val_ptr); static bool readF32Value(Parser& parser, void* val_ptr); static bool readF64Value(Parser& parser, void* val_ptr); + static bool readVector3Value(Parser& parser, void* val_ptr); static bool readColor4Value(Parser& parser, void* val_ptr); static bool readUIColorValue(Parser& parser, void* val_ptr); static bool readUUIDValue(Parser& parser, void* val_ptr); @@ -144,6 +145,7 @@ private: static bool writeS32Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeF32Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeF64Value(Parser& parser, const void* val_ptr, name_stack_t&); + static bool writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t&); diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index cb3b7abb14..ee87d01239 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -105,14 +105,14 @@ LLStyle::Params::Params() namespace LLInitParam { - ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color) + ParamValue<LLUIColor>::ParamValue(const LLUIColor& color) : super_t(color) {} - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock() + void ParamValue<LLUIColor>::updateValueFromBlock() {} - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool) + void ParamValue<LLUIColor>::updateBlockFromValue(bool) {} bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) @@ -120,14 +120,14 @@ namespace LLInitParam return false; } - ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp) + ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp) : super_t(fontp) {} - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock() + void ParamValue<const LLFontGL*>::updateValueFromBlock() {} - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool) + void ParamValue<const LLFontGL*>::updateBlockFromValue(bool) {} void TypeValues<LLFontGL::HAlign>::declareValues() @@ -139,10 +139,10 @@ namespace LLInitParam void TypeValues<LLFontGL::ShadowType>::declareValues() {} - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock() + void ParamValue<LLUIImage*>::updateValueFromBlock() {} - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool) + void ParamValue<LLUIImage*>::updateBlockFromValue(bool) {} diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 963473c92a..109d3ca7bb 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -63,14 +63,14 @@ S32 LLUIImage::getHeight() const namespace LLInitParam { - ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color) + ParamValue<LLUIColor>::ParamValue(const LLUIColor& color) : super_t(color) {} - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock() + void ParamValue<LLUIColor>::updateValueFromBlock() {} - void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool) + void ParamValue<LLUIColor>::updateBlockFromValue(bool) {} bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) @@ -79,14 +79,14 @@ namespace LLInitParam } - ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp) + ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp) : super_t(fontp) {} - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock() + void ParamValue<const LLFontGL*>::updateValueFromBlock() {} - void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool) + void ParamValue<const LLFontGL*>::updateBlockFromValue(bool) {} void TypeValues<LLFontGL::HAlign>::declareValues() @@ -98,10 +98,10 @@ namespace LLInitParam void TypeValues<LLFontGL::ShadowType>::declareValues() {} - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock() + void ParamValue<LLUIImage*>::updateValueFromBlock() {} - void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool) + void ParamValue<LLUIImage*>::updateBlockFromValue(bool) {} bool ParamCompare<LLUIImage*, false>::equals( diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 2ffb0d8503..afb0d87da9 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -147,13 +147,15 @@ LLXMLNodePtr LLXMLNode::deepCopy() for (LLXMLChildList::iterator iter = mChildren->map.begin(); iter != mChildren->map.end(); ++iter) { - newnode->addChild(iter->second->deepCopy()); + LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); + newnode->addChild(temp_ptr_for_gcc); } } for (LLXMLAttribList::iterator iter = mAttributes.begin(); iter != mAttributes.end(); ++iter) { - newnode->addChild(iter->second->deepCopy()); + LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); + newnode->addChild(temp_ptr_for_gcc); } return newnode; @@ -259,7 +261,7 @@ BOOL LLXMLNode::removeChild(LLXMLNode *target_child) return FALSE; } -void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) +void LLXMLNode::addChild(LLXMLNodePtr& new_child) { if (new_child->mParent != NULL) { @@ -273,6 +275,11 @@ void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) new_child->mParent = this; if (new_child->mIsAttribute) { + LLXMLAttribList::iterator found_it = mAttributes.find(new_child->mName); + if (found_it != mAttributes.end()) + { + removeChild(found_it->second); + } mAttributes.insert(std::make_pair(new_child->mName, new_child)); } else @@ -285,49 +292,11 @@ void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) } mChildren->map.insert(std::make_pair(new_child->mName, new_child)); - // if after_child is specified, it damn well better be in the list of children - // for this node. I'm not going to assert that, because it would be expensive, - // but don't specify that parameter if you didn't get the value for it from the - // list of children of this node! - if (after_child.isNull()) - { - if (mChildren->tail != new_child) - { - mChildren->tail->mNext = new_child; - new_child->mPrev = mChildren->tail; - mChildren->tail = new_child; - } - } - // if after_child == parent, then put new_child at beginning - else if (after_child == this) - { - // add to front of list - new_child->mNext = mChildren->head; - if (mChildren->head) - { - mChildren->head->mPrev = new_child; - mChildren->head = new_child; - } - else // no children - { - mChildren->head = new_child; - mChildren->tail = new_child; - } - } - else + if (mChildren->tail != new_child) { - if (after_child->mNext.notNull()) - { - // if after_child was not the last item, fix up some pointers - after_child->mNext->mPrev = new_child; - new_child->mNext = after_child->mNext; - } - new_child->mPrev = after_child; - after_child->mNext = new_child; - if (mChildren->tail == after_child) - { - mChildren->tail = new_child; - } + mChildren->tail->mNext = new_child; + new_child->mPrev = mChildren->tail; + mChildren->tail = new_child; } } @@ -343,8 +312,9 @@ LLXMLNodePtr LLXMLNode::createChild(const char* name, BOOL is_attribute) // virtual LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, BOOL is_attribute) { - LLXMLNode* ret = new LLXMLNode(name, is_attribute); + LLXMLNodePtr ret(new LLXMLNode(name, is_attribute)); ret->mID.clear(); + addChild(ret); return ret; } @@ -358,11 +328,12 @@ BOOL LLXMLNode::deleteChild(LLXMLNode *child) return FALSE; } -void LLXMLNode::setParent(LLXMLNodePtr new_parent) +void LLXMLNode::setParent(LLXMLNodePtr& new_parent) { if (new_parent.notNull()) { - new_parent->addChild(this); + LLXMLNodePtr this_ptr(this); + new_parent->addChild(this_ptr); } else { @@ -681,27 +652,6 @@ bool LLXMLNode::updateNode( return TRUE; } - -// static -LLXMLNodePtr LLXMLNode::replaceNode(LLXMLNodePtr node, LLXMLNodePtr update_node) -{ - if (!node || !update_node) - { - llwarns << "Node invalid" << llendl; - return node; - } - - LLXMLNodePtr cloned_node = update_node->deepCopy(); - node->mParent->addChild(cloned_node, node); // add after node - LLXMLNodePtr parent = node->mParent; - parent->removeChild(node); - parent->updateDefault(); - - return cloned_node; -} - - - // static bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXMLNode* defaults_tree) { @@ -1198,7 +1148,8 @@ void LLXMLNode::scrubToTree(LLXMLNode *tree) std::vector<LLXMLNodePtr>::iterator itor3; for (itor3=to_delete_list.begin(); itor3!=to_delete_list.end(); ++itor3) { - (*itor3)->setParent(NULL); + LLXMLNodePtr ptr; + (*itor3)->setParent(ptr); } } } @@ -2733,7 +2684,8 @@ void LLXMLNode::setName(LLStringTableEntry* name) mName = name; if (old_parent) { - old_parent->addChild(this); + LLXMLNodePtr this_ptr(this); + old_parent->addChild(this_ptr); } } diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index e3da7169e7..ec486d7957 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -127,8 +127,8 @@ public: BOOL isNull(); BOOL deleteChild(LLXMLNode* child); - void addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child = LLXMLNodePtr(NULL)); - void setParent(LLXMLNodePtr new_parent); // reparent if necessary + void addChild(LLXMLNodePtr& new_child); + void setParent(LLXMLNodePtr& new_parent); // reparent if necessary // Serialization static bool parseFile( @@ -147,7 +147,6 @@ public: static bool updateNode( LLXMLNodePtr& node, LLXMLNodePtr& update_node); - static LLXMLNodePtr replaceNode(LLXMLNodePtr node, LLXMLNodePtr replacement_node); static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector<std::string>& paths); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 65170a2140..eb88a6b251 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -104,6 +104,8 @@ set(viewer_SOURCE_FILES llavatarlist.cpp llavatarlistitem.cpp llavatarpropertiesprocessor.cpp + llblockedlistitem.cpp + llblocklist.cpp llbox.cpp llbreadcrumbview.cpp llbrowsernotification.cpp @@ -248,13 +250,13 @@ set(viewer_SOURCE_FILES llfloateruipreview.cpp llfloaterurlentry.cpp llfloatervoiceeffect.cpp + llfloatervoicevolume.cpp llfloaterwebcontent.cpp llfloaterwebprofile.cpp llfloaterwhitelistentry.cpp llfloaterwindowsize.cpp llfloaterworldmap.cpp - llfolderview.cpp - llfolderviewitem.cpp + llfolderviewmodelinventory.cpp llfollowcam.cpp llfriendcard.cpp llgesturelistener.cpp @@ -280,6 +282,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimconversation.cpp llimfloater.cpp llimfloatercontainer.cpp llimhandler.cpp @@ -334,7 +337,6 @@ set(viewer_SOURCE_FILES llnamelistctrl.cpp llnavigationbar.cpp llnearbychat.cpp - llnearbychatbar.cpp llnearbychathandler.cpp llnearbychatbarlistener.cpp llnetmap.cpp @@ -366,7 +368,6 @@ set(viewer_SOURCE_FILES llpanelgroupnotices.cpp llpanelgrouproles.cpp llpanelhome.cpp - llpanelimcontrolpanel.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarkinfo.cpp @@ -664,6 +665,8 @@ set(viewer_HEADER_FILES llavatarlist.h llavatarlistitem.h llavatarpropertiesprocessor.h + llblockedlistitem.h + llblocklist.h llbox.h llbreadcrumbview.h llbuycurrencyhtml.h @@ -808,14 +811,13 @@ set(viewer_HEADER_FILES llfloateruipreview.h llfloaterurlentry.h llfloatervoiceeffect.h + llfloatervoicevolume.h llfloaterwebcontent.h llfloaterwebprofile.h llfloaterwhitelistentry.h llfloaterwindowsize.h llfloaterworldmap.h - llfolderview.h - llfoldervieweventlistener.h - llfolderviewitem.h + llfolderviewmodelinventory.h llfollowcam.h llfriendcard.h llgesturelistener.h @@ -840,6 +842,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimconversation.h llimfloater.h llimfloatercontainer.h llimview.h @@ -894,7 +897,6 @@ set(viewer_HEADER_FILES llnamelistctrl.h llnavigationbar.h llnearbychat.h - llnearbychatbar.h llnearbychathandler.h llnearbychatbarlistener.h llnetmap.h @@ -920,7 +922,6 @@ set(viewer_HEADER_FILES llpanelgroupnotices.h llpanelgrouproles.h llpanelhome.h - llpanelimcontrolpanel.h llpanelland.h llpanellandaudio.h llpanellandmarkinfo.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 73df064ab2..51211a8ce5 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -46,11 +46,11 @@ available_in_toybox="true" icon="Command_Chat_Icon" label_ref="Command_Chat_Label" - tooltip_ref="Command_Chat_Tooltip" + tooltip_ref="Command_Conversations_Tooltip" execute_function="Floater.ToggleOrBringToFront" - execute_parameters="chat_bar" + execute_parameters="im_container" is_running_function="Floater.IsOpen" - is_running_parameters="chat_bar" + is_running_parameters="im_container" /> <command name="compass" available_in_toybox="false" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5e50bd6e01..325cb1f583 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,6 +2,28 @@ <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> <map> + <key>IMShowTime</key> + <map> + <key>Comment</key> + <string>Enable(disable) timestamp showing in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>IMShowNamesForP2PConv</key> + <map> + <key>Comment</key> + <string>Enable(disable) showing of a names in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CrashHostUrl</key> <map> <key>Comment</key> @@ -1595,17 +1617,6 @@ <key>Value</key> <integer>1</integer> </map> - <key>ChatWindow</key> - <map> - <key>Comment</key> - <string>Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>0</integer> - </map> <key>CheesyBeacon</key> <map> <key>Comment</key> @@ -1628,6 +1639,17 @@ <key>Value</key> <string /> </map> + <key>NearbyChatIsNotTornOff</key> + <map> + <key>Comment</key> + <string>saving torn-off state of the nearby chat between sessions</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CloseChatOnReturn</key> <map> <key>Comment</key> @@ -9835,7 +9857,7 @@ <key>ShowScriptErrorsLocation</key> <map> <key>Comment</key> - <string>Show script error in chat or window</string> + <string>Show script error in chat (0) or window (1).</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -10019,6 +10041,17 @@ <key>Value</key> <integer>2</integer> </map> + <key>BlockPeopleSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort order for recent people (0 = by name, 1 = by type)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>2</integer> + </map> <key>ShowPGSearchAll</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 143126b334..1f637ef3ff 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -22,6 +22,61 @@ <key>Value</key> <string>The Resident you messaged is in 'busy mode' which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing.</string> </map> + <key>ConversationsExpandMessagePaneFirst</key> + <map> + <key>Comment</key> + <string>Expand either messages or conversations list pane from Conversations compact mode.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>ConversationsListPaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of the conversations list pane in Conversations floater.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsListPaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater list pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>268</integer> + </map> + <key>ConversationsMessagePaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of Conversations floater message pane.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsMessagePaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater message pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>412</integer> + </map> <key>InstantMessageLogPath</key> <map> <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index a6776d4c8d..8960ca7569 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -54,7 +54,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationsutil.h" #include "llpaneltopinfobar.h" #include "llparcel.h" @@ -1909,7 +1909,7 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1921,7 +1921,7 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index fdd4565e50..93e8b9ca40 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -235,7 +235,7 @@ void LLAvatarActions::startCall(const LLUUID& id) } // static -void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) +void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) { if (ids.size() == 0) { @@ -252,7 +252,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true); + ids[0], id_array, true, floater_id); if (session_id == LLUUID::null) { return; @@ -285,7 +285,7 @@ bool LLAvatarActions::canCall() } // static -void LLAvatarActions::startConference(const uuid_vec_t& ids) +void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) { // *HACK: Copy into dynamic array LLDynamicArray<LLUUID> id_array; @@ -294,7 +294,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids) id_array.push_back(*it); } const std::string title = LLTrans::getString("conference-title"); - LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array); + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, false, floater_id); if (session_id != LLUUID::null) { LLIMFloater::show(session_id); @@ -529,23 +529,6 @@ namespace action_give_inventory return acceptable; } - static void build_residents_string(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) - { - llassert(avatar_names.size() > 0); - - const std::string& separator = LLTrans::getString("words_separator"); - for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) - { - LLAvatarName av_name = *it; - residents_string.append(av_name.mDisplayName); - if (++it == avatar_names.end()) - { - break; - } - residents_string.append(separator); - } - } - static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string) { llassert(inventory_selected_uuids.size() > 0); @@ -681,7 +664,7 @@ namespace action_give_inventory } std::string residents; - build_residents_string(avatar_names, residents); + LLAvatarActions::buildResidentsString(avatar_names, residents); std::string items; build_items_string(inventory_selected_uuids, items); @@ -712,28 +695,51 @@ namespace action_give_inventory } } +// static +void LLAvatarActions::buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) +{ + llassert(avatar_names.size() > 0); + const std::string& separator = LLTrans::getString("words_separator"); + for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) + { + LLAvatarName av_name = *it; + residents_string.append(av_name.mDisplayName); + if (++it == avatar_names.end()) + { + break; + } + residents_string.append(separator); + } +} //static std::set<LLUUID> LLAvatarActions::getInventorySelectedUUIDs() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected; LLInventoryPanel* active_panel = action_give_inventory::get_active_inventory_panel(); if (active_panel) { - inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); + inventory_selected= active_panel->getRootFolder()->getSelectionList(); } - if (inventory_selected_uuids.empty()) + if (inventory_selected.empty()) { LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); if (sidepanel_inventory) { - inventory_selected_uuids = sidepanel_inventory->getInboxSelectionList(); + inventory_selected= sidepanel_inventory->getInboxSelectionList(); } } + std::set<LLUUID> inventory_selected_uuids; + for (std::set<LLFolderViewItem*>::iterator it = inventory_selected.begin(), end_it = inventory_selected.end(); + it != end_it; + ++it) + { + inventory_selected_uuids.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } return inventory_selected_uuids; } @@ -769,15 +775,15 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL // check selection in the panel LLFolderView* root_folder = inv_panel->getRootFolder(); - const std::set<LLUUID> inventory_selected_uuids = root_folder->getSelectionList(); - if (inventory_selected_uuids.empty()) return false; // nothing selected + const std::set<LLFolderViewItem*> inventory_selected = root_folder->getSelectionList(); + if (inventory_selected.empty()) return false; // nothing selected bool can_share = true; - std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); - const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end(); + std::set<LLFolderViewItem*>::const_iterator it = inventory_selected.begin(); + const std::set<LLFolderViewItem*>::const_iterator it_end = inventory_selected.end(); for (; it != it_end; ++it) { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); // any category can be offered. if (inv_cat) { @@ -785,9 +791,9 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL } // check if inventory item can be given - LLFolderViewItem* item = root_folder->getItemByID(*it); + LLFolderViewItem* item = *it; if (!item) return false; - LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getListener()); + LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getViewModelItem()); if (bridge && bridge->canShare()) { continue; @@ -820,6 +826,26 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) } // static +void LLAvatarActions::toggleMuteVoice(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); // needed for mute + + LLMuteList* mute_list = LLMuteList::getInstance(); + bool is_muted = mute_list->isMuted(id, LLMute::flagVoiceChat); + + LLMute mute(id, name, LLMute::AGENT); + if (!is_muted) + { + mute_list->add(mute, LLMute::flagVoiceChat); + } + else + { + mute_list->remove(mute, LLMute::flagVoiceChat); + } +} + +// static bool LLAvatarActions::canOfferTeleport(const LLUUID& id) { // First use LLAvatarTracker::isBuddy() @@ -1015,7 +1041,6 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri LLSD payload; payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipOffered", args, payload); } @@ -1034,6 +1059,12 @@ bool LLAvatarActions::isBlocked(const LLUUID& id) } // static +bool LLAvatarActions::isVoiceMuted(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); +} + +// static bool LLAvatarActions::canBlock(const LLUUID& id) { std::string full_name; diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 748b7cb3d1..259e87c336 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -34,6 +34,7 @@ #include <string> #include <vector> +class LLAvatarName; class LLInventoryPanel; class LLFloater; @@ -81,14 +82,14 @@ public: static void startCall(const LLUUID& id); /** - * Start an ad-hoc conference voice call with multiple users + * Start an ad-hoc conference voice call with multiple users in a specific IM floater. */ - static void startAdhocCall(const uuid_vec_t& ids); + static void startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** - * Start conference chat with the given avatars. + * Start conference chat with the given avatars in a specific IM floater. */ - static void startConference(const uuid_vec_t& ids); + static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** * Show avatar profile. @@ -124,6 +125,11 @@ public: static void toggleBlock(const LLUUID& id); /** + * Block/unblock the avatar voice. + */ + static void toggleMuteVoice(const LLUUID& id); + + /** * Return true if avatar with "id" is a friend */ static bool isFriend(const LLUUID& id); @@ -134,6 +140,11 @@ public: static bool isBlocked(const LLUUID& id); /** + * @return true if the avatar voice is blocked + */ + static bool isVoiceMuted(const LLUUID& id); + + /** * @return true if you can block the avatar */ static bool canBlock(const LLUUID& id); @@ -198,6 +209,14 @@ public: */ static bool canShareSelectedItems(LLInventoryPanel* inv_panel = NULL); + /** + * Builds a string of residents' display names separated by "words_separator" string. + * + * @param avatar_names - a vector of given avatar names from which resulting string is built + * @param residents_string - the resulting string + */ + static void buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string); + static std::set<LLUUID> getInventorySelectedUUIDs(); private: diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 30eecfe323..7b5229b5e6 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -38,6 +38,7 @@ #include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "lloutputmonitorctrl.h" +#include "lltooldraganddrop.h" bool LLAvatarListItem::sStaticInitialized = false; S32 LLAvatarListItem::sLeftPadding = 0; @@ -334,6 +335,61 @@ BOOL LLAvatarListItem::handleDoubleClick(S32 x, S32 y, MASK mask) return LLPanel::handleDoubleClick(x, y, mask); } +BOOL LLAvatarListItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (LLUICtrl::handleMouseDown(x, y, mask)) + { + return TRUE; + } + + gFocusMgr.setMouseCapture(this); + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); + + return TRUE; +} + +BOOL LLAvatarListItem::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + if (LLUICtrl::childrenHandleMouseUp(x, y, mask)) + { + return TRUE; + } + + if(hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + return TRUE; +} + +BOOL LLAvatarListItem::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = hasMouseCapture(); + if(handled) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y)) + { + // First, create the global drag and drop object + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + types.push_back(DAD_PERSON); + cargo_ids.push_back(mAvatarId); + LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_PEOPLE; + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src); + } + } + + return handled; +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index c95ac39696..28a50870d4 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -112,6 +112,9 @@ public: void onProfileBtnClick(); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); protected: /** diff --git a/indra/newview/llblockedlistitem.cpp b/indra/newview/llblockedlistitem.cpp new file mode 100644 index 0000000000..d9afd2b629 --- /dev/null +++ b/indra/newview/llblockedlistitem.cpp @@ -0,0 +1,114 @@ +/** + * @file llviewerobjectlistitem.cpp + * @brief viewer object list item implementation + * + * Class LLPanelInventoryListItemBase displays inventory item as an element + * of LLInventoryItemsList. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblockedlistitem.h" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavatariconctrl.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" +#include "llviewerobject.h" + +LLBlockedListItem::LLBlockedListItem(const LLMute* item) +: LLPanel(), + mItemID(item->mID), + mItemName(item->mName), + mMuteType(item->mType) +{ + buildFromFile("panel_blocked_list_item.xml"); +} + +BOOL LLBlockedListItem::postBuild() +{ + mTitleCtrl = getChild<LLTextBox>("item_name"); + mTitleCtrl->setValue(mItemName); + + switch (mMuteType) + { + case LLMute::AGENT: + case LLMute::EXTERNAL: + { + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mItemID); + } + break; + case LLMute::GROUP: + { + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mItemID); + } + break; + case LLMute::OBJECT: + case LLMute::BY_NAME: + getChild<LLUICtrl>("object_icon")->setVisible(TRUE); + break; + + default: + break; + } + + return TRUE; +} + +void LLBlockedListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLBlockedListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLBlockedListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLBlockedListItem::highlightName(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mTitleCtrl, params, mItemName, highlited_text); +} diff --git a/indra/newview/llblockedlistitem.h b/indra/newview/llblockedlistitem.h new file mode 100644 index 0000000000..05409e8a3b --- /dev/null +++ b/indra/newview/llblockedlistitem.h @@ -0,0 +1,73 @@ +/** + * @file llviewerobjectlistitem.h + * @brief viewer object list item header file + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLVIEWEROBJECTLISTITEM_H_ +#define LLVIEWEROBJECTLISTITEM_H_ + +#include "llmutelist.h" +#include "llpanel.h" +#include "llstyle.h" +#include "lltextbox.h" +#include "lliconctrl.h" + +/** + * This class represents items of LLBlockList, which represents + * contents of LLMuteList. LLMuteList "consists" of LLMute items. + * Each LLMute represents either blocked avatar or object and + * stores info about mute type (avatar or object) + * + * Each item consists if object/avatar icon and object/avatar name + * + * To create a blocked list item just need to pass LLMute pointer + * and appropriate block list item will be created depending on + * LLMute type (LLMute::EType) and other LLMute's info + */ +class LLBlockedListItem : public LLPanel +{ +public: + + LLBlockedListItem(const LLMute* item); + virtual BOOL postBuild(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + void highlightName(const std::string& highlited_text); + const std::string& getName() const { return mItemName; } + const LLMute::EType& getType() const { return mMuteType; } + const LLUUID& getUUID() const { return mItemID; } + +private: + + LLTextBox* mTitleCtrl; + const LLUUID mItemID; + std::string mItemName; + LLMute::EType mMuteType; + +}; + +#endif /* LLVIEWEROBJECTLISTITEM_H_ */ diff --git a/indra/newview/llblocklist.cpp b/indra/newview/llblocklist.cpp new file mode 100644 index 0000000000..066cb71677 --- /dev/null +++ b/indra/newview/llblocklist.cpp @@ -0,0 +1,284 @@ +/** + * @file llblocklist.cpp + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblocklist.h" + +#include "llavataractions.h" +#include "llblockedlistitem.h" +#include "llfloatersidepanelcontainer.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register<LLBlockList> r("block_list"); + +static const LLBlockListNameComparator NAME_COMPARATOR; +static const LLBlockListNameTypeComparator NAME_TYPE_COMPARATOR; + +LLBlockList::LLBlockList(const Params& p) +: LLFlatListViewEx(p), + mSelectedItem(NULL), + mDirty(true) +{ + + LLMuteList::getInstance()->addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Block.Action", boost::bind(&LLBlockList::onCustomAction, this, _2)); + enable_registrar.add("Block.Enable", boost::bind(&LLBlockList::isActionEnabled, this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_people_blocked_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } +} + +LLBlockList::~LLBlockList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLMuteList::getInstance()->removeObserver(this); +} + +BOOL LLBlockList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + if (context_menu && size()) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLBlockList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +void LLBlockList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLBlockList::sortByType() +{ + setComparator(&NAME_TYPE_COMPARATOR); + sort(); +} + +void LLBlockList::draw() +{ + if (mDirty) + { + refresh(); + } + + LLFlatListView::draw(); +} + +void LLBlockList::addNewItem(const LLMute* mute) +{ + LLBlockedListItem* item = new LLBlockedListItem(mute); + if (!mNameFilter.empty()) + { + item->highlightName(mNameFilter); + } + addItem(item, item->getUUID(), ADD_BOTTOM); +} + +void LLBlockList::refresh() +{ + bool have_filter = !mNameFilter.empty(); + + // save selection to restore it after list rebuilt + LLUUID selected = getSelectedUUID(); + + // calling refresh may be initiated by removing currently selected item + // so select next item and save the selection to restore it after list rebuilt + if (!selectNextItemPair(false, true)) + { + selectNextItemPair(true, true); + } + LLUUID next_selected = getSelectedUUID(); + + clear(); + + std::vector<LLMute> mutes = LLMuteList::instance().getMutes(); + std::vector<LLMute>::const_iterator mute_it = mutes.begin(); + + for (; mute_it != mutes.end(); ++mute_it) + { + if (have_filter && !findInsensitive(mute_it->mName, mNameFilter)) + continue; + + addNewItem(&*mute_it); + } + + if (getItemPair(selected)) + { + // restore previously selected item + selectItemPair(getItemPair(selected), true); + } + else if (getItemPair(next_selected)) + { + // previously selected item was removed, so select next item + selectItemPair(getItemPair(next_selected), true); + } + + // Sort the list. + sort(); + + setDirty(false); +} + +bool LLBlockList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +LLBlockedListItem* LLBlockList::getBlockedItem() const +{ + LLPanel* panel = LLFlatListView::getSelectedItem(); + LLBlockedListItem* item = dynamic_cast<LLBlockedListItem*>(panel); + return item; +} + +bool LLBlockList::isActionEnabled(const LLSD& userdata) +{ + bool action_enabled = true; + + const std::string command_name = userdata.asString(); + + if ("profile_item" == command_name) + { + LLBlockedListItem* item = getBlockedItem(); + action_enabled = item && (LLMute::AGENT == item->getType()); + } + + if ("unblock_item" == command_name) + { + action_enabled = getSelectedItem() != NULL; + } + + return action_enabled; +} + +void LLBlockList::onCustomAction(const LLSD& userdata) +{ + if (!isActionEnabled(userdata)) + { + return; + } + + LLBlockedListItem* item = getBlockedItem(); + const std::string command_name = userdata.asString(); + + if ("unblock_item" == command_name) + { + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::getInstance()->remove(mute); + } + else if ("profile_item" == command_name) + { + switch(item->getType()) + { + + case LLMute::AGENT: + LLAvatarActions::showProfile(item->getUUID()); + break; + + default: + break; + } + } +} + +bool LLBlockListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLBlockedListItem* blocked_item1 = dynamic_cast<const LLBlockedListItem*>(item1); + const LLBlockedListItem* blocked_item2 = dynamic_cast<const LLBlockedListItem*>(item2); + + if (!blocked_item1 || !blocked_item2) + { + llerror("blocked_item1 and blocked_item2 cannot be null", 0); + return true; + } + + return doCompare(blocked_item1, blocked_item2); +} + +bool LLBlockListNameComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + std::string name1 = blocked_item1->getName(); + std::string name2 = blocked_item2->getName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; +} + +bool LLBlockListNameTypeComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + LLMute::EType type1 = blocked_item1->getType(); + LLMute::EType type2 = blocked_item2->getType(); + + // if mute type is LLMute::BY_NAME or LLMute::OBJECT it means that this mute is an object + bool both_mutes_are_objects = (LLMute::OBJECT == type1 || LLMute::BY_NAME == type1) && (LLMute::OBJECT == type2 || LLMute::BY_NAME == type2); + + // mute types may be different, but since both LLMute::BY_NAME and LLMute::OBJECT types represent objects + // it's needed to perform additional checking of both_mutes_are_objects variable + if (type1 != type2 && !both_mutes_are_objects) + { + // objects in block list go first, so return true if mute type is not an avatar + return LLMute::AGENT != type1; + } + + return NAME_COMPARATOR.compare(blocked_item1, blocked_item2); +} diff --git a/indra/newview/llblocklist.h b/indra/newview/llblocklist.h new file mode 100644 index 0000000000..1a215710f4 --- /dev/null +++ b/indra/newview/llblocklist.h @@ -0,0 +1,138 @@ +/** + * @file llblocklist.h + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLBLOCKLIST_H_ +#define LLBLOCKLIST_H_ + +#include "llflatlistview.h" +#include "lllistcontextmenu.h" +#include "llmutelist.h" +#include "lltoggleablemenu.h" + +class LLBlockedListItem; +class LLMute; + +/** + * List of blocked avatars and objects. + * This list represents contents of the LLMuteList. + * Each change in LLMuteList leads to rebuilding this list, so + * it's always in actual state. + */ +class LLBlockList: public LLFlatListViewEx, public LLMuteListObserver +{ + LOG_CLASS(LLBlockList); +public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; + + LLBlockList(const Params& p); + virtual ~LLBlockList(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + LLBlockedListItem* getBlockedItem() const; + + virtual void onChange() { refresh(); } + virtual void draw(); + + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByType(); + void refresh(); + +private: + + void addNewItem(const LLMute* mute); + void setDirty(bool dirty = true) { mDirty = dirty; } + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + bool isActionEnabled(const LLSD& userdata); + void onCustomAction (const LLSD& userdata); + + + LLHandle<LLToggleableMenu> mContextMenu; + + LLBlockedListItem* mSelectedItem; + std::string mNameFilter; + bool mDirty; + +}; + + +/* + * Abstract comparator for blocked items + */ +class LLBlockListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLBlockListItemComparator); + +public: + LLBlockListItemComparator() {}; + virtual ~LLBlockListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const = 0; +}; + + +/* + * Compares items by name + */ +class LLBlockListNameComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameComparator); + +public: + LLBlockListNameComparator() {}; + virtual ~LLBlockListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +/* + * Compares items by type and then by name within type + * Objects come first then avatars + */ +class LLBlockListNameTypeComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameTypeComparator); + +public: + LLBlockListNameTypeComparator() {}; + virtual ~LLBlockListNameTypeComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +#endif /* LLBLOCKLIST_H_ */ diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp index 6e77d1e336..9e608d2c8b 100644 --- a/indra/newview/llbrowsernotification.cpp +++ b/indra/newview/llbrowsernotification.cpp @@ -35,11 +35,8 @@ using namespace LLNotificationsUI; -bool LLBrowserNotification::processNotification(const LLSD& notify) +bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - if (!notification) return false; - LLUUID media_id = notification->getPayload()["media_id"].asUUID(); LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id); if (media_instance) diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 0d55c4429a..60d60abd45 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -54,6 +54,7 @@ #include "llresmgr.h" #include "llslurl.h" #include "llimview.h" +#include "lltrans.h" #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobjectlist.h" @@ -723,12 +724,13 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, // Use display name only because this user is your friend LLSD args; args["NAME"] = av_name.mDisplayName; + args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus"); LLNotificationPtr notification; if (online) { notification = - LLNotificationsUtil::add("FriendOnline", + LLNotificationsUtil::add("FriendOnlineOffline", args, payload.with("respond_on_mousedown", TRUE), boost::bind(&LLAvatarActions::startIM, agent_id)); @@ -736,7 +738,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, else { notification = - LLNotificationsUtil::add("FriendOffline", args, payload); + LLNotificationsUtil::add("FriendOnlineOffline", args, payload); } // If there's an open IM session with this agent, send a notification there too. diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 84e73e96fa..e3d57ab7ae 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -143,7 +143,8 @@ public: { LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with("blocked_to_select", getAvatarId())); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with("blocked_to_select", getAvatarId())); } } @@ -695,6 +696,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL { LLFastTimer _(FTM_APPEND_MESSAGE); bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean(); + bool square_brackets = false; // square brackets necessary for a system messages llassert(mEditor); if (!mEditor) @@ -702,9 +704,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL return; } + bool from_me = chat.mFromID == gAgent.getID(); mEditor->setPlainText(use_plain_text_chat_history); - if (!mEditor->scrolledToEnd() && chat.mFromID != gAgent.getID() && !chat.mFromName.empty()) + if (!mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) { mUnreadChatSources.insert(chat.mFromName); mMoreChatPanel->setVisible(TRUE); @@ -734,16 +737,23 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); + LLColor4 name_color(txt_color); + LLViewerChat::getChatColor(chat,txt_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); std::string font_size = LLFontGL::sizeFromFont(fontp); - LLStyle::Params style_params; - style_params.color(txt_color); - style_params.readonly_color(txt_color); - style_params.font.name(font_name); - style_params.font.size(font_size); - style_params.font.style(input_append_params.font.style); + + LLStyle::Params body_message_params; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + body_message_params.font.name(font_name); + body_message_params.font.size(font_size); + body_message_params.font.style(input_append_params.font.style); + + LLStyle::Params name_params(body_message_params); + name_params.color(name_color); + name_params.readonly_color(name_color); std::string prefix = chat.mText.substr(0, 4); @@ -766,29 +776,51 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) { delimiter = LLStringUtil::null; - style_params.font.style = "ITALIC"; + name_params.font.style = "ITALIC"; } bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY; // We graying out chat history by graying out messages that contains full date in a time string if (message_from_log) { - style_params.color(LLColor4::grey); - style_params.readonly_color(LLColor4::grey); + txt_color = LLColor4::grey; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + name_params.color(txt_color); + name_params.readonly_color(txt_color); } + bool prependNewLineState = mEditor->getText().size() != 0; + + // compact mode: show a timestamp and name if (use_plain_text_chat_history) { - LLStyle::Params timestamp_style(style_params); + square_brackets = chat.mFromName == SYSTEM_FROM; + + LLStyle::Params timestamp_style(body_message_params); + + // out of the timestamp + if (args["show_time"].asBoolean()) + { if (!message_from_log) { LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); timestamp_style.color(timestamp_color); timestamp_style.readonly_color(timestamp_color); } - mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getLength() != 0, timestamp_style); + mEditor->appendText("[" + chat.mTimeStr + "] ", prependNewLineState, timestamp_style); + prependNewLineState = false; + } + + // out the opening square bracket (if need) + if (square_brackets) + { + mEditor->appendText("[", prependNewLineState, body_message_params); + prependNewLineState = false; + } - if (utf8str_trim(chat.mFromName).size() != 0) + // names showing + if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size() != 0) { // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) @@ -798,32 +830,47 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // set the link for the object name to be the objectim SLapp // (don't let object names with hyperlinks override our objectim Url) - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); link_params.color = link_color; link_params.readonly_color = link_color; link_params.is_link = true; link_params.link_href = url; - mEditor->appendText(chat.mFromName + delimiter, - false, link_params); + mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); + prependNewLineState = false; } else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) { - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + if (from_me) + { std::string localized_name; + bool is_localized = LLTrans::findString(localized_name, "AgentNameSubst"); + mEditor->appendText((is_localized? localized_name:"(You)") + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } + else + { // Add link to avatar's inspector and delimiter to message. - mEditor->appendText(std::string(link_params.link_href) + delimiter, false, link_params); + mEditor->appendText(std::string(link_params.link_href) + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } } else { - mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, false, style_params); + mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, + prependNewLineState, body_message_params); + prependNewLineState = false; } } } - else + else // showing timestamp and name in the expanded mode { + prependNewLineState = false; LLView* view = NULL; LLInlineViewSegment::Params p; p.force_newline = true; @@ -844,7 +891,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } else { - view = getHeader(chat, style_params, args); + view = getHeader(chat, name_params, args); if (mEditor->getLength() == 0) p.top_pad = 0; else @@ -873,41 +920,16 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mIsLastMessageFromLog = message_from_log; } + // body of the message processing + + // notify processing if (chat.mNotifId.notNull()) { LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); if (notification != NULL) { LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( - notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history); - //we can't set follows in xml since it broke toasts behavior - notify_box->setFollowsLeft(); - notify_box->setFollowsRight(); - notify_box->setFollowsTop(); - - ctrl_list_t ctrls = notify_box->getControlPanel()->getCtrlList(); - S32 offset = 0; - // Children were added by addChild() which uses push_front to insert them into list, - // so to get buttons in correct order reverse iterator is used (EXT-5906) - for (ctrl_list_t::reverse_iterator it = ctrls.rbegin(); it != ctrls.rend(); it++) - { - LLButton * button = dynamic_cast<LLButton*> (*it); - if (button != NULL) - { - button->setOrigin( offset, - button->getRect().mBottom); - button->setLeftHPad(2 * HPAD); - button->setRightHPad(2 * HPAD); - // set zero width before perform autoResize() - button->setRect(LLRect(button->getRect().mLeft, - button->getRect().mTop, button->getRect().mLeft, - button->getRect().mBottom)); - button->setAutoResize(true); - button->autoResize(); - offset += HPAD + button->getRect().getWidth(); - button->setFollowsNone(); - } - } + notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); //Prepare the rect for the view LLRect target_rect = mEditor->getDocumentView()->getRect(); @@ -924,6 +946,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mEditor->appendWidget(params, "\n", false); } } + + // usual messages showing else { std::string message = irc_me ? chat.mText.substr(3) : chat.mText; @@ -931,7 +955,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL //MESSAGE TEXT PROCESSING //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010) - if (use_plain_text_chat_history && gAgentID != chat.mFromID && chat.mFromID.notNull()) + if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) { std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT; if (message.length() > slurl_about.length() && @@ -946,13 +970,19 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL message = chat.mFromName + message; } - mEditor->appendText(message, FALSE, style_params); + if (square_brackets) + { + message += "]"; + } + + mEditor->appendText(message, prependNewLineState, body_message_params); + prependNewLineState = false; } mEditor->blockUndo(); // automatically scroll to end when receiving chat from myself - if (chat.mFromID == gAgentID) + if (from_me) { mEditor->setCursorAndScrollToEnd(); } diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 9a84280f25..61772b4bb7 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -35,7 +35,7 @@ #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -96,8 +96,15 @@ void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_p { LLPanel::reshape(width, height,called_from_parent); - LLUICtrl* msg_text = getChild<LLUICtrl>("msg_text", false); - LLUICtrl* icon = getChild<LLUICtrl>("avatar_icon", false); + // reshape() may be called from LLView::initFromParams() before the children are created. + // We call findChild() instead of getChild() here to avoid creating dummy controls. + LLUICtrl* msg_text = findChild<LLUICtrl>("msg_text", false); + LLUICtrl* icon = findChild<LLUICtrl>("avatar_icon", false); + + if (!msg_text || !icon) + { + return; + } LLRect msg_text_rect = msg_text->getRect(); LLRect avatar_rect = icon->getRect(); @@ -316,12 +323,12 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return FALSE; } } - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } @@ -355,6 +362,8 @@ BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) } void LLNearbyChatToastPanel::draw() { + LLPanel::draw(); + if(mIsDirty) { LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon", false); @@ -372,7 +381,6 @@ void LLNearbyChatToastPanel::draw() } mIsDirty = false; } - LLToastPanelBase::draw(); } diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index 1d700dcede..89b0c4f37a 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -40,7 +40,7 @@ typedef enum e_show_item_header CHATITEMHEADER_SHOW_BOTH } EShowItemHeader; -class LLNearbyChatToastPanel: public LLToastPanelBase +class LLNearbyChatToastPanel : public LLPanel { protected: LLNearbyChatToastPanel() diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index a661808d1f..17181edffc 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -335,30 +335,15 @@ void LLIMWellChiclet::messageCountChanged(const LLSD& session_data) /* LLNotificationChiclet implementation */ /************************************************************************/ LLNotificationChiclet::LLNotificationChiclet(const Params& p) -: LLSysWellChiclet(p) -, mUreadSystemNotifications(0) +: LLSysWellChiclet(p), + mUreadSystemNotifications(0) { - // connect counter handlers to the signals - connectCounterUpdatersToSignal("notify"); - connectCounterUpdatersToSignal("groupnotify"); - connectCounterUpdatersToSignal("offer"); - + mNotificationChannel.reset(new ChicletNotificationChannel(this)); // ensure that notification well window exists, to synchronously // handle toast add/delete events. LLNotificationWellWindow::getInstance()->setSysWellChiclet(this); } -void LLNotificationChiclet::connectCounterUpdatersToSignal(const std::string& notification_type) -{ - LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); - LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); - if(n_handler) - { - n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this)); - n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this)); - } -} - void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data) { std::string action = user_data.asString(); @@ -407,6 +392,18 @@ void LLNotificationChiclet::setCounter(S32 counter) updateWidget(getCounter() == 0); } + +bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification ) +{ + if( !(notification->canLogToIM() && notification->hasFormElements()) + && (!notification->getPayload().has("give_inventory_notification") + || notification->getPayload()["give_inventory_notification"])) + { + return true; + } + return false; +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 19683492c2..f51d7b622c 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -34,6 +34,7 @@ #include "lloutputmonitorctrl.h" #include "llgroupmgr.h" #include "llimview.h" +#include "llnotifications.h" class LLMenuGL; class LLIMFloater; @@ -872,9 +873,10 @@ class LLIMWellChiclet : public LLSysWellChiclet, LLIMSessionObserver { friend class LLUICtrlFactory; public: - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} - virtual void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } - virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} ~LLIMWellChiclet(); protected: @@ -911,11 +913,35 @@ protected: class LLNotificationChiclet : public LLSysWellChiclet { + LOG_CLASS(LLNotificationChiclet); + friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{}; protected: + struct ChicletNotificationChannel : public LLNotificationChannel + { + ChicletNotificationChannel(LLNotificationChiclet* chiclet) + : LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString())), + mChiclet(chiclet) + { + // connect counter handlers to the signals + connectToChannel("Group Notifications"); + connectToChannel("Offer"); + connectToChannel("Notifications"); + } + + static bool filterNotification(LLNotificationPtr notify); + // connect counter updaters to the corresponding signals + /*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } + /*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); } + + LLNotificationChiclet* const mChiclet; + }; + + boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel; + LLNotificationChiclet(const Params& p); /** @@ -933,12 +959,6 @@ protected: */ /*virtual*/ void createMenu(); - // connect counter updaters to the corresponding signals - void connectCounterUpdatersToSignal(const std::string& notification_type); - - // methods for updating a number of unread System notifications - void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications); } - void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications); } /*virtual*/ void setCounter(S32 counter); S32 mUreadSystemNotifications; }; diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index f1bc51fbe7..39f5d0b8f6 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -57,19 +57,11 @@ LLChicletBar::LLChicletBar(const LLSD&) : mChicletPanel(NULL), mToolbarStack(NULL) { - // Firstly add our self to IMSession observers, so we catch session events - // before chiclets do that. - LLIMMgr::getInstance()->addSessionObserver(this); - buildFromFile("panel_chiclet_bar.xml"); } LLChicletBar::~LLChicletBar() { - if (!LLSingleton<LLIMMgr>::destroyed()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } } LLIMChiclet* LLChicletBar::createIMChiclet(const LLUUID& session_id) @@ -102,6 +94,13 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam // no need to spawn chiclets for participants in P2P calls called through Avaline if (session->isP2P() && session->isOtherParticipantAvaline()) return; + // Do not spawn chiclet when using the new multitab conversation UI + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloater::addToHost(session_id); + return; + } + if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; LLIMChiclet* chiclet = createIMChiclet(session_id); @@ -109,7 +108,7 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam { chiclet->setIMSessionName(name); chiclet->setOtherParticipantId(other_participant_id); - + LLIMFloater::onIMChicletCreated(session_id); } @@ -125,10 +124,12 @@ void LLChicletBar::sessionRemoved(const LLUUID& session_id) if(getChicletPanel()) { // IM floater should be closed when session removed and associated chiclet closed - LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (iMfloater != NULL) + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (im_floater != NULL && !im_floater->getStartConferenceInSameFloater()) { - iMfloater->closeFloater(); + // Close the IM floater only if we are not planning to close the P2P chat + // and start a new conference in the same floater. + im_floater->closeFloater(); } getChicletPanel()->removeChiclet(session_id); diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h index 1427bf95e0..7d0d904810 100644 --- a/indra/newview/llchicletbar.h +++ b/indra/newview/llchicletbar.h @@ -50,9 +50,10 @@ public: LLChicletPanel* getChicletPanel() { return mChicletPanel; } // LLIMSessionObserver observe triggers - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); - virtual void sessionRemoved(const LLUUID& session_id); - void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); S32 getTotalUnreadIMCount(); diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 935dcb74b0..a50184460b 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -150,7 +150,7 @@ void LLExpandableTextBox::LLTextBoxEx::showExpandText() std::pair<S32, S32> visible_lines = getVisibleLines(true); S32 last_line = visible_lines.second - 1; - LLStyle::Params expander_style(getDefaultStyleParams()); + LLStyle::Params expander_style(getStyleParams()); expander_style.font.style = "UNDERLINE"; expander_style.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); LLExpanderSegment* expanderp = new LLExpanderSegment(new LLStyle(expander_style), getLineStart(last_line), getLength() + 1, mExpanderLabel, *this); @@ -166,7 +166,7 @@ void LLExpandableTextBox::LLTextBoxEx::hideExpandText() if (mExpanderVisible) { // this will overwrite the expander segment and all text styling with a single style - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLNormalTextSegment* segmentp = new LLNormalTextSegment(sp, 0, getLength() + 1, *this); insertSegment(segmentp); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 4cbc9cab4a..c68577db75 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -38,6 +38,7 @@ #include "lltooltip.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llclipboard.h" #include "llclipboard.h" #include "llinventorybridge.h" @@ -45,12 +46,14 @@ #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "lllandmarkactions.h" +#include "lllogininstance.h" #include "llnotificationsutil.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewermenu.h" #include "lltooldraganddrop.h" +#include "llsdserialize.h" static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); @@ -317,7 +320,8 @@ public: if (item) { - item->setSortField(mSortField); + LLFavoritesOrderStorage::instance().setSortIndex(item, mSortField); + item->setComplete(TRUE); item->updateServer(FALSE); @@ -339,8 +343,8 @@ struct LLFavoritesSort // TODO - made it customizible using gSavedSettings bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) { - S32 sortField1 = a->getSortField(); - S32 sortField2 = b->getSortField(); + S32 sortField1 = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()); + S32 sortField2 = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); if (!(sortField1 < 0 && sortField2 < 0)) { @@ -528,7 +532,7 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) mItems.push_back(gInventory.getItem(mDragItemId)); } - gInventory.saveItemsOrder(mItems); + LLFavoritesOrderStorage::instance().saveItemsOrder(mItems); LLToggleableMenu* menu = (LLToggleableMenu*) mOverflowMenuHandle.get(); @@ -587,7 +591,8 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con } else { - currItem->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex(currItem, ++sortField); + currItem->setComplete(TRUE); currItem->updateServer(FALSE); @@ -640,7 +645,7 @@ void LLFavoritesBarCtrl::changed(U32 mask) for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->getSLURL(); + LLFavoritesOrderStorage::instance().getSLURL((*i)->getAssetUUID()); } updateButtons(); } @@ -909,7 +914,7 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it S32 sortField = 0; for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex((*i), ++sortField); } } @@ -1355,7 +1360,7 @@ BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array // if there is an item without sort order field set, we need to save items order for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) { - if ((*i)->getSortField() < 0) + if (LLFavoritesOrderStorage::instance().getSortIndex((*i)->getUUID()) < 0) { result = TRUE; break; @@ -1390,4 +1395,291 @@ void LLFavoritesBarCtrl::insertItem(LLInventoryModel::item_array_t& items, const } } +const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; +const S32 LLFavoritesOrderStorage::NO_INDEX = -1; + +void LLFavoritesOrderStorage::setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index) +{ + mSortIndexes[inv_item->getUUID()] = sort_index; + mIsDirty = true; + getSLURL(inv_item->getAssetUUID()); +} + +S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) +{ + sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); + if (it != mSortIndexes.end()) + { + return it->second; + } + return NO_INDEX; +} + +void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) +{ + mSortIndexes.erase(inv_item_id); + mIsDirty = true; +} + +void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) +{ + slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); + if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached + + LLLandmark* lm = gLandmarkList.getAsset(asset_id, + boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); + if (lm) + { + onLandmarkLoaded(asset_id, lm); + } +} + +// static +void LLFavoritesOrderStorage::destroyClass() +{ + LLFavoritesOrderStorage::instance().cleanup(); + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } + else + { + LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); + } +} + +void LLFavoritesOrderStorage::load() +{ + // load per-resident sorting information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + } +} + +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (!LLLoginInstance::getInstance()->authSuccess()) + { + llwarns << "Cannot save favorites: not logged in" << llendl; + return; + } + + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + if (user_dir.empty()) + { + llwarns << "Cannot save favorites: empty user dir name" << llendl; + return; + } + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream in_file; + in_file.open(filename); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + } + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLSD user_llsd; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + lldebugs << "Saving favorite: idx=" << LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID()) << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; + value["slurl"] = slurl_iter->second; + user_llsd[LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID())] = value; + } + else + { + llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; + } + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; + fav_llsd[av_name.getLegacyName()] = user_llsd; + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, file); +} + +void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; + if (fav_llsd.has(av_name.getLegacyName())) + { + fav_llsd.erase(av_name.getLegacyName()); + } + + llofstream out_file; + out_file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (!landmark) return; + + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } + + if (!pos_global.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + lldebugs << "Saving landmark SLURL: " << slurl << llendl; + mSLURLs[asset_id] = slurl; +} + +void LLFavoritesOrderStorage::save() +{ + // nothing to save if clean + if (!mIsDirty) return; + + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(); + if (!user_dir.empty()) + { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + LLSD settings_llsd; + + for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; + } + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); + } +} + +void LLFavoritesOrderStorage::cleanup() +{ + // nothing to clean + if (!mIsDirty) return; + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + IsNotInFavorites is_not_in_fav(items); + + sort_index_map_t aTempMap; + //copy unremoved values from mSortIndexes to aTempMap + std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), + inserter(aTempMap, aTempMap.begin()), + is_not_in_fav); + + //Swap the contents of mSortIndexes and aTempMap + mSortIndexes.swap(aTempMap); +} + +void LLFavoritesOrderStorage::saveItemsOrder( const LLInventoryModel::item_array_t& items ) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + setSortIndex(item, ++sortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + + // Tell the parent folder to refresh its sort order. + gInventory.addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + gInventory.notifyObservers(); +} + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) + { + return LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()) + < LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); + } +}; + +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +void LLFavoritesOrderStorage::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + // ensure items are sorted properly before changing order. EXT-3498 + std::sort(items.begin(), items.end(), LLViewerInventoryItemSort()); + + // update order + gInventory.updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +} + +void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) +{ + if (mTargetLandmarkId.isNull()) return; + + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); +} // EOF diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 447d30f1f4..e000adc4aa 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -33,6 +33,7 @@ #include "llinventoryobserver.h" #include "llinventorymodel.h" +#include "llviewerinventory.h" class LLMenuItemCallGL; class LLToggleableMenu; @@ -161,5 +162,115 @@ private: boost::signals2::connection mEndDragConnection; }; +class AddFavoriteLandmarkCallback : public LLInventoryCallback +{ +public: + AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} + void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } + +private: + void fire(const LLUUID& inv_item); + + LLUUID mTargetLandmarkId; +}; + +/** + * Class to store sorting order of favorites landmarks in a local file. EXT-3985. + * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. + * Data are stored in user home directory. + */ +class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> + , public LLDestroyClass<LLFavoritesOrderStorage> +{ + LOG_CLASS(LLFavoritesOrderStorage); +public: + /** + * Sets sort index for specified with LLUUID favorite landmark + */ + void setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index); + + /** + * Gets sort index for specified with LLUUID favorite landmark + */ + S32 getSortIndex(const LLUUID& inv_item_id); + void removeSortIndex(const LLUUID& inv_item_id); + + void getSLURL(const LLUUID& asset_id); + + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + void saveItemsOrder(const LLInventoryModel::item_array_t& items); + + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + + /** + * Implementation of LLDestroyClass. Calls cleanup() instance method. + * + * It is important this callback is called before gInventory is cleaned. + * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), + * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. + * @see cleanup() + */ + static void destroyClass(); + + const static S32 NO_INDEX; +private: + friend class LLSingleton<LLFavoritesOrderStorage>; + LLFavoritesOrderStorage() : mIsDirty(false) { load(); } + ~LLFavoritesOrderStorage() { save(); } + + /** + * Removes sort indexes for items which are not in Favorites bar for now. + */ + void cleanup(); + + const static std::string SORTING_DATA_FILE_NAME; + + void load(); + void save(); + + void saveFavoritesSLURLs(); + + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); + + void onLandmarkLoaded(const LLUUID& asset_id, class LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + + typedef std::map<LLUUID, S32> sort_index_map_t; + sort_index_map_t mSortIndexes; + + typedef std::map<LLUUID, std::string> slurls_map_t; + slurls_map_t mSLURLs; + + bool mIsDirty; + + struct IsNotInFavorites + { + IsNotInFavorites(const LLInventoryModel::item_array_t& items) + : mFavoriteItems(items) + { + + } + + /** + * Returns true if specified item is not found among inventory items + */ + bool operator()(const sort_index_map_t::value_type& id_index_pair) const + { + LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); + if (item.isNull()) return true; + + LLInventoryModel::item_array_t::const_iterator found_it = + std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); + + return found_it == mFavoriteItems.end(); + } + private: + LLInventoryModel::item_array_t mFavoriteItems; + }; + +}; #endif // LL_LLFAVORITESBARCTRL_H diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index 2681d4b34d..4f35c325a8 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -44,21 +44,16 @@ public: BOOL postBuild(); private: - bool update(const LLSD& payload, bool passed_filter); + bool update(const LLSD& payload); static void toggleClick(void* user_data); static void onClickNotification(void* user_data); - static void onClickNotificationReject(void* user_data); LLNotificationChannelPtr mChannelPtr; - LLNotificationChannelPtr mChannelRejectsPtr; }; LLNotificationChannelPanel::LLNotificationChannelPanel(const LLNotificationChannelPanel::Params& p) : LLLayoutPanel(p) { mChannelPtr = LLNotifications::instance().getChannel(p.name); - mChannelRejectsPtr = LLNotificationChannelPtr( - LLNotificationChannel::buildChannel(p.name() + "rejects", mChannelPtr->getParentChannelName(), - !boost::bind(mChannelPtr->getFilter(), _1))); buildFromFile( "panel_notifications_channel.xml"); } @@ -68,15 +63,11 @@ BOOL LLNotificationChannelPanel::postBuild() header_button->setLabel(mChannelPtr->getName()); header_button->setClickedCallback(toggleClick, this); - mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, true)); - mChannelRejectsPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, false)); + mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1)); LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("notifications_list"); scroll->setDoubleClickCallback(onClickNotification, this); scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); - scroll = getChild<LLScrollListCtrl>("notification_rejects_list"); - scroll->setDoubleClickCallback(onClickNotificationReject, this); - scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); return TRUE; } @@ -97,8 +88,6 @@ void LLNotificationChannelPanel::toggleClick(void *user_data) // turn off tab stop for collapsed panel self->getChild<LLScrollListCtrl>("notifications_list")->setTabStop(!header_button->getToggleState()); self->getChild<LLScrollListCtrl>("notifications_list")->setVisible(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setTabStop(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setVisible(!header_button->getToggleState()); } /*static*/ @@ -118,24 +107,7 @@ void LLNotificationChannelPanel::onClickNotification(void* user_data) } } -/*static*/ -void LLNotificationChannelPanel::onClickNotificationReject(void* user_data) -{ - LLNotificationChannelPanel* self = (LLNotificationChannelPanel*)user_data; - if (!self) return; - LLScrollListItem* firstselected = self->getChild<LLScrollListCtrl>("notification_rejects_list")->getFirstSelected(); - llassert(firstselected); - if (firstselected) - { - void* data = firstselected->getUserdata(); - if (data) - { - gFloaterView->getParentFloater(self)->addDependentFloater(new LLFloaterNotification((LLNotification*)data), TRUE); - } - } -} - -bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) +bool LLNotificationChannelPanel::update(const LLSD& payload) { LLNotificationPtr notification = LLNotifications::instance().find(payload["id"].asUUID()); if (notification) @@ -151,9 +123,7 @@ bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) row["columns"][2]["column"] = "date"; row["columns"][2]["type"] = "date"; - LLScrollListItem* sli = passed_filter ? - getChild<LLScrollListCtrl>("notifications_list")->addElement(row) : - getChild<LLScrollListCtrl>("notification_rejects_list")->addElement(row); + LLScrollListItem* sli = getChild<LLScrollListCtrl>("notifications_list")->addElement(row); sli->setUserdata(&(*notification)); } diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 540f977305..04a55b261c 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -44,14 +44,12 @@ #include "llviewernetwork.h" #include "llwindowshade.h" -#define USE_WINDOWSHADE_DIALOGS 0 - ///---------------------------------------------------------------------------- /// LLOutboxNotification class ///---------------------------------------------------------------------------- -bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& notify) +bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify) { LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox"); @@ -60,6 +58,14 @@ bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& no return false; } +void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p) +{ + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); + if (sys_handler) + { + sys_handler->onDelete(p); + } +} ///---------------------------------------------------------------------------- /// LLOutboxAddedObserver helper class @@ -244,7 +250,8 @@ void LLFloaterOutbox::setupOutbox(const LLUUID& outboxId) mOutboxInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mOutboxInventoryPanel->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); + + mOutboxInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); mOutboxInventoryPanel->getFilter()->markDefault(); fetchOutboxContents(); @@ -380,7 +387,7 @@ BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // Determine if the mouse is inside the inventory panel itself or just within the floater bool pointInInventoryPanel = false; bool pointInInventoryPanelChild = false; - LLFolderView * root_folder = mOutboxInventoryPanel->getRootFolder(); + LLFolderView* root_folder = mOutboxInventoryPanel->getRootFolder(); if (mOutboxInventoryPanel->getVisible()) { S32 inv_x, inv_y; @@ -437,10 +444,10 @@ void LLFloaterOutbox::onOutboxChanged() { llassert(!mOutboxId.isNull()); - if (mOutboxInventoryPanel) - { - mOutboxInventoryPanel->requestSort(); - } + //if (mOutboxInventoryPanel) + //{ + // mOutboxInventoryPanel->requestSort(); + //} fetchOutboxContents(); @@ -516,52 +523,11 @@ void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content) updateView(); } -void LLFloaterOutbox::showNotification(const LLSD& notify) +void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - { - llerrs << "Unable to find outbox notification!" << notify.asString() << llendl; - - return; - } - -#if USE_WINDOWSHADE_DIALOGS - - if (mWindowShade) - { - delete mWindowShade; - } - - LLRect floater_rect = getLocalRect(); - floater_rect.mTop -= getHeaderHeight(); - floater_rect.stretch(-5, 0); - - LLWindowShade::Params params; - params.name = "notification_shade"; - params.rect = floater_rect; - params.follows.flags = FOLLOWS_ALL; - params.modal = true; - params.can_close = false; - params.shade_color = LLColor4::white % 0.25f; - params.text_color = LLColor4::white; - - mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params); - - addChild(mWindowShade); - mWindowShade->show(notification); - -#else - - LLNotificationsUI::LLEventHandler * handler = - LLNotificationsUI::LLNotificationManager::instance().getHandlerForNotification("alertmodal"); - - LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler *>(handler); + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); llassert(sys_handler); - sys_handler->processNotification(notify); - -#endif + sys_handler->processNotification(notification); } diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h index 18baccf1c9..a91d8c1139 100644 --- a/indra/newview/llfloateroutbox.h +++ b/indra/newview/llfloateroutbox.h @@ -64,7 +64,7 @@ public: EAcceptance* accept, std::string& tooltip_msg); - void showNotification(const LLSD& notify); + void showNotification(const LLNotificationPtr& notification); BOOL handleHover(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 5edd920c70..3df68a6ea1 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -425,13 +425,7 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); @@ -461,14 +455,11 @@ BOOL LLFloaterPreference::postBuild() void LLFloaterPreference::onBusyResponseChanged() { // set "BusyResponseChanged" TRUE if user edited message differs from default, FALSE otherwise - if (LLTrans::getString("BusyModeResponseDefault") != getChild<LLUICtrl>("busy_response")->getValue().asString()) - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", TRUE ); - } - else - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", FALSE ); - } + bool busy_flag = + LLTrans::getString("BusyModeResponseDefault") + != getChild<LLUICtrl>("busy_response")->getValue().asString(); + + gSavedPerAccountSettings.setBOOL("BusyResponseChanged", busy_flag ); } LLFloaterPreference::~LLFloaterPreference() @@ -553,8 +544,6 @@ void LLFloaterPreference::apply() // LLWString busy_response = utf8str_to_wstring(getChild<LLUICtrl>("busy_response")->getValue().asString()); // LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); - - gSavedSettings.setBOOL("PlainTextChatHistory", getChild<LLUICtrl>("plain_text_chat_history")->getValue().asBoolean()); if (mGotPersonalInfo) { @@ -1425,8 +1414,6 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im getChild<LLUICtrl>("online_visibility")->setLabelArg("[DIR_VIS]", mDirectoryVisibility); getChildView("send_im_to_email")->setEnabled(TRUE); getChild<LLUICtrl>("send_im_to_email")->setValue(im_via_email); - getChildView("plain_text_chat_history")->setEnabled(TRUE); - getChild<LLUICtrl>("plain_text_chat_history")->setValue(gSavedSettings.getBOOL("PlainTextChatHistory")); getChildView("log_instant_messages")->setEnabled(TRUE); // getChildView("log_chat")->setEnabled(TRUE); // getChildView("busy_response")->setEnabled(TRUE); @@ -1508,7 +1495,8 @@ void LLFloaterPreference::onChangeMaturity() // but the UI for this will still be enabled void LLFloaterPreference::onClickBlockList() { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel")); } void LLFloaterPreference::onClickProxySettings() diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index 5385977d95..96b5c6b09b 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -61,7 +61,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na if (!getVisible()) { - openFloater(); + openFloater(); } LLPanel* panel = NULL; @@ -69,10 +69,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); if (container) { - LLSD new_params = params; - new_params[LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME] = panel_name; - container->onOpen(new_params); - + container->openPanel(panel_name, params); panel = container->getCurrentPanel(); } else if ((panel = dynamic_cast<LLPanel*>(view)) != NULL) diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 6978e6a430..43465d4209 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -660,8 +660,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) mBtnEdit ->setToggleState( edit_visible ); mRadioGroupEdit->setVisible( edit_visible ); - bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); - getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); + //bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); + //getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); mBtnLink->setVisible(edit_visible); mBtnUnlink->setVisible(edit_visible); diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 1a17183efd..bb01ce5a7e 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings @@ -293,6 +293,6 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + LLNearbyChat::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llfloatervoicevolume.cpp b/indra/newview/llfloatervoicevolume.cpp new file mode 100644 index 0000000000..87b388b30a --- /dev/null +++ b/indra/newview/llfloatervoicevolume.cpp @@ -0,0 +1,209 @@ +/** + * @file llfloatervoicevolume.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoicevolume.h" + +// Linden libraries +#include "llavatarname.h" +#include "llavatarnamecache.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lltextbox.h" + +// viewer files +#include "llagent.h" +#include "llavataractions.h" +#include "llinspect.h" +#include "lltransientfloatermgr.h" +#include "llvoiceclient.h" + +class LLAvatarName; + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolume +////////////////////////////////////////////////////////////////////////////// + +// Avatar Inspector, a small information window used when clicking +// on avatar names in the 2D UI and in the ambient inspector widget for +// the 3D world. +class LLFloaterVoiceVolume : public LLInspect, LLTransientFloater +{ + friend class LLFloaterReg; + +public: + // avatar_id - Avatar ID for which to show information + // Inspector will be positioned relative to current mouse position + LLFloaterVoiceVolume(const LLSD& avatar_id); + virtual ~LLFloaterVoiceVolume(); + + /*virtual*/ BOOL postBuild(void); + + // Because floater is single instance, need to re-parse data on each spawn + // (for example, inspector about same avatar but in different position) + /*virtual*/ void onOpen(const LLSD& avatar_id); + + /*virtual*/ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } + +private: + // Set the volume slider to this user's current client-side volume setting, + // hiding/disabling if the user is not nearby. + void updateVolumeControls(); + + void onClickMuteVolume(); + void onVolumeChange(const LLSD& data); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + +private: + LLUUID mAvatarID; + // Need avatar name information to spawn friend add request + LLAvatarName mAvatarName; +}; + +LLFloaterVoiceVolume::LLFloaterVoiceVolume(const LLSD& sd) +: LLInspect(LLSD()) // single_instance, doesn't really need key +, mAvatarID() // set in onOpen() *Note: we used to show partner's name but we dont anymore --angela 3rd Dec* +, mAvatarName() +{ + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); + LLTransientFloater::init(this); +} + +LLFloaterVoiceVolume::~LLFloaterVoiceVolume() +{ + LLTransientFloaterMgr::getInstance()->removeControlView(this); +} + +/*virtual*/ +BOOL LLFloaterVoiceVolume::postBuild(void) +{ + getChild<LLUICtrl>("mute_btn")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onClickMuteVolume, this) ); + + getChild<LLUICtrl>("volume_slider")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onVolumeChange, this, _2)); + + return TRUE; +} + + +// Multiple calls to showInstance("floater_voice_volume", foo) will provide different +// LLSD for foo, which we will catch here. +//virtual +void LLFloaterVoiceVolume::onOpen(const LLSD& data) +{ + // Start open animation + LLInspect::onOpen(data); + + // Extract appropriate avatar id + mAvatarID = data["avatar_id"]; + + LLUI::positionViewNearMouse(this); + + getChild<LLUICtrl>("avatar_name")->setValue(""); + updateVolumeControls(); + + LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLFloaterVoiceVolume::onAvatarNameCache, this, _1, _2)); +} + +void LLFloaterVoiceVolume::updateVolumeControls() +{ + bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); + + LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); + LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); + + // Do not display volume slider and mute button if it + // is ourself or we are not in a voice channel together + if (!voice_enabled || (mAvatarID == gAgent.getID())) + { + mute_btn->setVisible(false); + volume_slider->setVisible(false); + } + else + { + mute_btn->setVisible(true); + volume_slider->setVisible(true); + + // By convention, we only display and toggle voice mutes, not all mutes + bool is_muted = LLAvatarActions::isVoiceMuted(mAvatarID); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); + + mute_btn->setEnabled(!is_linden); + mute_btn->setValue(is_muted); + + volume_slider->setEnabled(!is_muted); + + F32 volume; + if (is_muted) + { + // it's clearer to display their volume as zero + volume = 0.f; + } + else + { + // actual volume + volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); + } + volume_slider->setValue((F64)volume); + } + +} + +void LLFloaterVoiceVolume::onClickMuteVolume() +{ + LLAvatarActions::toggleMuteVoice(mAvatarID); + updateVolumeControls(); +} + +void LLFloaterVoiceVolume::onVolumeChange(const LLSD& data) +{ + F32 volume = (F32)data.asReal(); + LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); +} + +void LLFloaterVoiceVolume::onAvatarNameCache( + const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + if (agent_id != mAvatarID) + { + return; + } + + getChild<LLUICtrl>("avatar_name")->setValue(av_name.getCompleteName()); + mAvatarName = av_name; +} + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolumeUtil +////////////////////////////////////////////////////////////////////////////// +void LLFloaterVoiceVolumeUtil::registerFloater() +{ + LLFloaterReg::add("floater_voice_volume", "floater_voice_volume.xml", + &LLFloaterReg::build<LLFloaterVoiceVolume>); +} diff --git a/indra/newview/llfloatervoicevolume.h b/indra/newview/llfloatervoicevolume.h new file mode 100644 index 0000000000..8fcf7f250b --- /dev/null +++ b/indra/newview/llfloatervoicevolume.h @@ -0,0 +1,35 @@ +/** + * @file llfloatervoicevolume.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEVOLUME_H +#define LL_LLFLOATERVOICEVOLUME_H + +namespace LLFloaterVoiceVolumeUtil +{ + // Register with LLFloaterReg + void registerFloater(); +} + +#endif // LL_LLFLOATERVOICEVOLUME_H diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h deleted file mode 100644 index 06682dcbf1..0000000000 --- a/indra/newview/llfoldervieweventlistener.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file llfoldervieweventlistener.h - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LLFOLDERVIEWEVENTLISTENER_H -#define LLFOLDERVIEWEVENTLISTENER_H - -#include "lldarray.h" // *TODO: convert to std::vector -#include "llfoldertype.h" -#include "llfontgl.h" // just for StyleFlags enum -#include "llinventorytype.h" -#include "llpermissionsflags.h" -#include "llpointer.h" -#include "llwearabletype.h" - - -class LLFolderViewItem; -class LLFolderView; -class LLFontGL; -class LLInventoryModel; -class LLMenuGL; -class LLScrollContainer; -class LLUIImage; -class LLUUID; - -// This is an abstract base class that users of the folderview classes -// would use to catch the useful events emitted from the folder -// views. -class LLFolderViewEventListener -{ -public: - virtual ~LLFolderViewEventListener( void ) {} - virtual const std::string& getName() const = 0; - virtual const std::string& getDisplayName() const = 0; - virtual const LLUUID& getUUID() const = 0; - virtual time_t getCreationDate() const = 0; // UTC seconds - virtual PermissionMask getPermissionMask() const = 0; - virtual LLFolderType::EType getPreferredType() const = 0; - virtual LLPointer<LLUIImage> getIcon() const = 0; - virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } - virtual LLFontGL::StyleFlags getLabelStyle() const = 0; - virtual std::string getLabelSuffix() const = 0; - virtual void openItem( void ) = 0; - virtual void closeItem( void ) = 0; - virtual void previewItem( void ) = 0; - virtual void selectItem(void) = 0; - virtual void showProperties(void) = 0; - virtual BOOL isItemRenameable() const = 0; - virtual BOOL renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder - virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed - virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. - virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0; - virtual void move( LLFolderViewEventListener* parent_listener ) = 0; - virtual BOOL isItemCopyable() const = 0; - virtual BOOL copyToClipboard() const = 0; - virtual BOOL cutToClipboard() const = 0; - virtual BOOL isClipboardPasteable() const = 0; - virtual void pasteFromClipboard() = 0; - virtual void pasteLinkFromClipboard() = 0; - virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; - virtual BOOL isUpToDate() const = 0; - virtual BOOL hasChildren() const = 0; - virtual LLInventoryType::EType getInventoryType() const = 0; - virtual void performAction(LLInventoryModel* model, std::string action) = 0; - virtual LLWearableType::EType getWearableType() const = 0; - - // This method should be called when a drag begins. returns TRUE - // if the drag can begin, otherwise FALSE. - virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; - - // This method will be called to determine if a drop can be - // performed, and will set drop to TRUE if a drop is - // requested. Returns TRUE if a drop is possible/happened, - // otherwise FALSE. - virtual BOOL dragOrDrop(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - std::string& tooltip_msg) = 0; -}; - -#endif diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp new file mode 100644 index 0000000000..13ca73917b --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -0,0 +1,326 @@ +/* + * @file llfolderviewmodelinventory.cpp + * @brief Implementation of the inventory-specific view model + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llfolderviewmodelinventory.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventorypanel.h" +#include "lltooldraganddrop.h" + +// +// class LLFolderViewModelInventory +// +static LLFastTimer::DeclareTimer FTM_INVENTORY_SORT("Sort"); + +bool LLFolderViewModelInventory::startDrag(std::vector<LLFolderViewModelItem*>& items) +{ + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + std::vector<LLFolderViewModelItem*>::iterator item_it; + bool can_drag = true; + if (!items.empty()) + { + for (item_it = items.begin(); item_it != items.end(); ++item_it) + { + EDragAndDropType type = DAD_NONE; + LLUUID id = LLUUID::null; + can_drag = can_drag && static_cast<LLFolderViewModelItemInventory*>(*item_it)->startDrag(&type, &id); + + types.push_back(type); + cargo_ids.push_back(id); + } + + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, + static_cast<LLFolderViewModelItemInventory*>(items.front())->getDragSource(), mTaskID); + } + return can_drag; +} + + +void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) +{ + LLFastTimer _(FTM_INVENTORY_SORT); + + if (!needsSort(folder->getViewModelItem())) return; + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem()); + if (modelp->getUUID().isNull()) return; + + for (std::list<LLFolderViewFolder*>::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd(); + it != end_it; + ++it) + { + LLFolderViewFolder* child_folderp = *it; + sort(child_folderp); + + if (child_folderp->getFoldersCount() > 0) + { + time_t most_recent_folder_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_folder_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_folder_time); + } + } + if (child_folderp->getItemsCount() > 0) + { + time_t most_recent_item_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_item_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_item_time); + } + } + } + base_t::sort(folder); +} + +bool LLFolderViewModelInventory::contentsReady() +{ + return !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); +} + +void LLFolderViewModelItemInventory::requestSort() +{ + LLFolderViewModelItemCommon::requestSort(); + if (mRootViewModel.getSorter().isByDate()) + { + // sort by date potentially affects parent folders which use a date + // derived from newest item in them + if (mParent) + { + mParent->requestSort(); + } + } +} + +bool LLFolderViewModelItemInventory::potentiallyVisible() +{ + return passedFilter() // we've passed the filter + || getLastFilterGeneration() < mRootViewModel.getFilter()->getFirstSuccessGeneration() // or we don't know yet + || descendantsPassedFilter(); +} + +bool LLFolderViewModelItemInventory::passedFilter(S32 filter_generation) +{ + if (filter_generation < 0) + filter_generation = mRootViewModel.getFilter()->getFirstSuccessGeneration(); + + return mPassedFolderFilter + && (descendantsPassedFilter(filter_generation) + || (mLastFilterGeneration >= filter_generation + && mPassedFilter)); +} + +bool LLFolderViewModelItemInventory::descendantsPassedFilter(S32 filter_generation) +{ + if (filter_generation < 0) filter_generation = mRootViewModel.getFilter()->getFirstSuccessGeneration(); + return mMostFilteredDescendantGeneration >= filter_generation; +} + +void LLFolderViewModelItemInventory::setPassedFilter(bool passed, bool passed_folder, S32 filter_generation) +{ + mPassedFilter = passed; + mPassedFolderFilter = passed_folder; + mLastFilterGeneration = filter_generation; + + bool passed_filter_before = mPrevPassedAllFilters; + mPrevPassedAllFilters = passedFilter(filter_generation); + + if (passed_filter_before != mPrevPassedAllFilters) + { + //TODO RN: ensure this still happens, but without dependency on folderview + LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); + if (parent_folder) + { + parent_folder->requestArrange(); + } + } +} + +void LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter ) +{ + S32 filter_generation = filter.getCurrentGeneration(); + + if (item->getLastFilterGeneration() < filter_generation) + { + // recursive application of the filter for child items + item->filter( filter ); + } + + // track latest generation to pass any child items, for each folder up to root + if (item->passedFilter()) + { + LLFolderViewModelItemInventory* view_model = this; + + while(view_model && view_model->mMostFilteredDescendantGeneration < filter_generation) + { + view_model->mMostFilteredDescendantGeneration = filter_generation; + view_model = static_cast<LLFolderViewModelItemInventory*>(view_model->mParent); + } + } +} + +void LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) +{ + const S32 filter_generation = filter.getCurrentGeneration(); + const S32 must_pass_generation = filter.getFirstRequiredGeneration(); + + if (getLastFilterGeneration() >= must_pass_generation + && !passedFilter(must_pass_generation)) + { + // failed to pass an earlier filter that was a subset of the current one + // go ahead and flag this item as done + setPassedFilter(false, false, filter_generation); + return; + } + + if(!mChildren.empty() + && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass + || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement + { + // now query children + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); + iter != end_iter && filter.getFilterCount() > 0; + ++iter) + { + filterChildItem((*iter), filter); + } + } + + // if we didn't use all filter iterations + // that means we filtered all of our descendants + // so filter ourselves now + if (filter.getFilterCount() > 0) + { + filter.decrementFilterCount(); + + const BOOL passed_filter = filter.check(this); + const BOOL passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) + ? filter.checkFolder(this) + : true; + + setPassedFilter(passed_filter, passed_filter_folder, filter_generation); + //TODO RN: create interface for string highlighting + //mStringMatchOffset = filter.getStringMatchOffset(this); + } +} + +LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() +{ + return &mInventoryViewModel; +} + + +const LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() const +{ + return &mInventoryViewModel; +} + +bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const +{ + // ignore sort order for landmarks in the Favorites folder. + // they should be always sorted as in Favorites bar. See EXT-719 + //TODO RN: fix sorting in favorites folder + //if (a->getSortGroup() == SG_ITEM + // && b->getSortGroup() == SG_ITEM + // && a->getInventoryType() == LLInventoryType::IT_LANDMARK + // && b->getInventoryType() == LLInventoryType::IT_LANDMARK) + //{ + + // static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + // LLUUID a_uuid = a->getParentFolder()->getUUID(); + // LLUUID b_uuid = b->getParentFolder()->getUUID(); + + // if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) + // { + // // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem + // // or to LLInvFVBridge + // LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a))->getItem(); + // LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b))->getItem(); + // if (!aitem || !bitem) + // return false; + // S32 a_sort = aitem->getSortField(); + // S32 b_sort = bitem->getSortField(); + // return a_sort < b_sort; + // } + //} + + // We sort by name if we aren't sorting by date + // OR if these are folders and we are sorting folders by name. + bool by_name = (!mByDate + || (mFoldersByName + && (a->getSortGroup() != SG_ITEM))); + + if (a->getSortGroup() != b->getSortGroup()) + { + if (mSystemToTop) + { + // Group order is System Folders, Trash, Normal Folders, Items + return (a->getSortGroup() < b->getSortGroup()); + } + else if (mByDate) + { + // Trash needs to go to the bottom if we are sorting by date + if ( (a->getSortGroup() == SG_TRASH_FOLDER) + || (b->getSortGroup() == SG_TRASH_FOLDER)) + { + return (b->getSortGroup() == SG_TRASH_FOLDER); + } + } + } + + if (by_name) + { + S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + if (0 == compare) + { + return (a->getCreationDate() > b->getCreationDate()); + } + else + { + return (compare < 0); + } + } + else + { + time_t first_create = a->getCreationDate(); + time_t second_create = b->getCreationDate(); + if (first_create == second_create) + { + return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); + } + else + { + return (first_create > second_create); + } + } +} + diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h new file mode 100644 index 0000000000..ab67c93897 --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.h @@ -0,0 +1,112 @@ +/** + * @file llfolderviewmodelinventory.h + * @brief view model implementation specific to inventory + * class definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFOLDERVIEWMODELINVENTORY_H +#define LL_LLFOLDERVIEWMODELINVENTORY_H + +#include "llinventoryfilter.h" +#include "llinventory.h" +#include "llwearabletype.h" +#include "lltooldraganddrop.h" + +class LLFolderViewModelItemInventory + : public LLFolderViewModelItemCommon +{ +public: + LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model) + : mRootViewModel(root_view_model) + {} + virtual const LLUUID& getUUID() const = 0; + virtual time_t getCreationDate() const = 0; // UTC seconds + virtual void setCreationDate(time_t creation_date_utc) = 0; + virtual PermissionMask getPermissionMask() const = 0; + virtual LLFolderType::EType getPreferredType() const = 0; + virtual void showProperties(void) = 0; + virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. + virtual BOOL isUpToDate() const = 0; + virtual bool hasChildren() const = 0; + virtual LLInventoryType::EType getInventoryType() const = 0; + virtual void performAction(LLInventoryModel* model, std::string action) = 0; + virtual LLWearableType::EType getWearableType() const = 0; + virtual EInventorySortGroup getSortGroup() const = 0; + virtual LLInventoryObject* getInventoryObject() const = 0; + virtual void requestSort(); + virtual bool potentiallyVisible(); + virtual bool passedFilter(S32 filter_generation = -1); + virtual bool descendantsPassedFilter(S32 filter_generation = -1); + virtual void setPassedFilter(bool filtered, bool filtered_folder, S32 filter_generation); + virtual void filter( LLFolderViewFilter& filter); + virtual void filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter); + + virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; + virtual LLToolDragAndDrop::ESource getDragSource() const = 0; + +protected: + class LLFolderViewModelInventory& mRootViewModel; +}; + +class LLInventorySort +{ +public: + LLInventorySort(U32 order = 0) + : mSortOrder(order), + mByDate(false), + mSystemToTop(false), + mFoldersByName(false) + { + mByDate = (order & LLInventoryFilter::SO_DATE); + mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); + mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + + bool isByDate() const { return mByDate; } + U32 getSortOrder() const { return mSortOrder; } + + bool operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const; +private: + U32 mSortOrder; + bool mByDate; + bool mSystemToTop; + bool mFoldersByName; +}; + +class LLFolderViewModelInventory + : public LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> +{ +public: + typedef LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> base_t; + + void setTaskID(const LLUUID& id) {mTaskID = id;} + + void sort(LLFolderViewFolder* folder); + bool contentsReady(); + bool startDrag(std::vector<LLFolderViewModelItem*>& items); + +private: + LLUUID mTaskID; +}; +#endif // LL_LLFOLDERVIEWMODELINVENTORY_H diff --git a/indra/newview/llfollowcam.cpp b/indra/newview/llfollowcam.cpp index b670af1782..47612fe25c 100644 --- a/indra/newview/llfollowcam.cpp +++ b/indra/newview/llfollowcam.cpp @@ -38,7 +38,6 @@ std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack; //------------------------------------------------------- // constants //------------------------------------------------------- -const F32 ONE_HALF = 0.5; const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f; const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f; const F32 DISTANCE_EPSILON = 0.0001f; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 11401d6c68..a64ddd185d 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -47,13 +47,13 @@ static const std::string INVENTORY_STRING_FRIENDS_ALL_SUBFOLDER = "All"; // helper functions // NOTE: For now Friends & All folders are created as protected folders of the LLFolderType::FT_CALLINGCARD type. -// So, their names will be processed in the LLFolderViewItem::refreshFromListener() to be localized +// So, their names will be processed in the LLFolderViewItem::refresh() to be localized // using "InvFolder LABEL_NAME" as LLTrans::findString argument. // We must use in this file their hard-coded names to ensure found them on different locales. EXT-5829. // These hard-coded names will be stored in InventoryItems but shown localized in FolderViewItems -// If hack in the LLFolderViewItem::refreshFromListener() to localize protected folder is removed +// If hack in the LLFolderViewItem::refresh() to localize protected folder is removed // or these folders are not protected these names should be localized in another place/way. inline const std::string get_friend_folder_name() { diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 66ca76bfb0..26b63bdacb 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -51,7 +51,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -997,7 +997,7 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + LLNearbyChat::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 129cddda45..aba3d74d87 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -86,7 +86,7 @@ LLGroupList::LLGroupList(const Params& p) registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2)); enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2)); - LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml", + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(context_menu) mContextMenuHandle = context_menu->getHandle(); @@ -112,7 +112,7 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); + LLToggleableMenu* context_menu = mContextMenuHandle.get(); if (context_menu && size() > 0) { context_menu->buildDrawLabels(); @@ -406,7 +406,7 @@ void LLGroupListItem::setActive(bool active) // *BUG: setName() overrides the style params. // Active group should be bold. - LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc()); + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 8abf14b3d0..e96a720886 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -28,10 +28,13 @@ #define LL_LLGROUPLIST_H #include "llevent.h" +#include "llpointer.h" + #include "llflatlistview.h" #include "llpanel.h" -#include "llpointer.h" #include "llstyle.h" +#include "lltoggleablemenu.h" + #include "llgroupmgr.h" /** @@ -45,6 +48,10 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener { LOG_CLASS(LLGroupList); public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; LLGroupList(const Params& p); virtual ~LLGroupList(); @@ -57,6 +64,8 @@ public: void toggleIcons(); bool getIconsVisible() const { return mShowIcons; } + LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); } + private: void setDirty(bool val = true) { mDirty = val; } void refresh(); @@ -66,7 +75,7 @@ private: bool onContextMenuItemClick(const LLSD& userdata); bool onContextMenuItemEnable(const LLSD& userdata); - LLHandle<LLView> mContextMenuHandle; + LLHandle<LLToggleableMenu> mContextMenuHandle; bool mShowIcons; bool mDirty; diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp index 482294c8a6..3336097955 100644 --- a/indra/newview/llhudnametag.cpp +++ b/indra/newview/llhudnametag.cpp @@ -166,7 +166,6 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& } // scale screen size of borders down - //RN: for now, text on hud objects is never occluded LLVector3 x_pixel_vec; LLVector3 y_pixel_vec; @@ -187,45 +186,29 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& + (y_pixel_vec * screen_offset.mV[VY]); - //if (mUseBubble) + LLVector3 bg_pos = render_position + + (F32)mOffsetY * y_pixel_vec + - (width_vec / 2.f) + - (height_vec); + + LLVector3 v[] = { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); + bg_pos, + bg_pos + width_vec, + bg_pos + width_vec + height_vec, + bg_pos + height_vec, + }; - LLVector3 v[] = - { - bg_pos, - bg_pos + width_vec, - bg_pos + width_vec + height_vec, - bg_pos + height_vec, - }; + LLVector3 dir = end-start; + F32 a, b, t; - if (debug_render) + if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || + LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) + { + if (t <= 1.f) { - gGL.begin(LLRender::LINE_STRIP); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[1].mV); - gGL.vertex3fv(v[2].mV); - gGL.vertex3fv(v[3].mV); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[2].mV); - gGL.end(); - } - - LLVector3 dir = end-start; - F32 a, b, t; - - if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || - LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) - { - if (t <= 1.f) - { - intersection = start + dir*t; - return TRUE; - } + intersection = start + dir*t; + return TRUE; } } @@ -241,12 +224,6 @@ void LLHUDNameTag::render() } } -void LLHUDNameTag::renderForSelect() -{ - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - renderText(TRUE); -} - void LLHUDNameTag::renderText(BOOL for_select) { if (!mVisible || mHidden) @@ -299,24 +276,6 @@ void LLHUDNameTag::renderText(BOOL for_select) LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground"); bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - // maybe a no-op? - //const S32 border_height = 16; - //const S32 border_width = 16; - const S32 border_height = 8; - const S32 border_width = 8; - - // *TODO move this into helper function - F32 border_scale = 1.f; - - if (border_height * 2 > mHeight) - { - border_scale = (F32)mHeight / ((F32)border_height * 2.f); - } - if (border_width * 2 > mWidth) - { - border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f)); - } - // scale screen size of borders down //RN: for now, text on hud objects is never occluded @@ -325,152 +284,34 @@ void LLHUDNameTag::renderText(BOOL for_select) LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); - LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight()); LLVector3 width_vec = mWidth * x_pixel_vec; LLVector3 height_vec = mHeight * y_pixel_vec; - LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec; - LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec; mRadius = (width_vec + height_vec).magVec() * 0.5f; LLCoordGL screen_pos; LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); - LLVector2 screen_offset; -// if (!mUseBubble) -// { -// screen_offset = mPositionOffset; -// } -// else -// { - screen_offset = updateScreenPos(mPositionOffset); -// } + LLVector2 screen_offset = updateScreenPos(mPositionOffset); LLVector3 render_position = mPositionAgent + (x_pixel_vec * screen_offset.mV[VX]) + (y_pixel_vec * screen_offset.mV[VY]); -// if (mUseBubble) + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + LLRect screen_rect; + screen_rect.setCenterAndSize(0, static_cast<S32>(lltrunc(-mHeight / 2 + mOffsetY)), static_cast<S32>(lltrunc(mWidth)), static_cast<S32>(lltrunc(mHeight))); + imagep->draw3D(render_position, x_pixel_vec, y_pixel_vec, screen_rect, bg_color); + if (mLabelSegments.size()) { - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLUI::pushMatrix(); - { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); - - if (for_select) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - S32 name = mSourceObject->mGLName; - LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name); - gGL.color4ubv(coloru.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - LLUI::popMatrix(); - return; - } - else - { - gGL.getTexUnit(0)->bind(imagep->getImage()); - - gGL.color4fv(bg_color.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - - if ( mLabelSegments.size()) - { - LLUI::pushMatrix(); - { - gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec; - LLVector3 label_offset = height_vec - label_height; - LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]); - gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height); - } - LLUI::popMatrix(); - } - } - - BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f; - BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f); + LLUIImagePtr rect_top_image = LLUI::getUIImage("Rounded_Rect_Top"); + LLRect label_top_rect = screen_rect; + const S32 label_height = llround((mFontp->getLineHeight() * (F32)mLabelSegments.size() + (VERTICAL_PADDING / 3.f))); + label_top_rect.mBottom = label_top_rect.mTop - label_height; + LLColor4 label_top_color = text_color; + label_top_color.mV[VALPHA] = gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor; - // draw line segments pointing to parent object - if (!mOffscreen && (outside_width || outside_height)) - { - LLUI::pushMatrix(); - { - gGL.color4fv(bg_color.mV); - LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec); - target_pos += (width_vec / 2.f); - target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero; - target_pos -= 3.f * x_pixel_vec; - target_pos -= 6.f * y_pixel_vec; - LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]); - gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec); - } - LLUI::popMatrix(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE); - - LLVector3 box_center_offset; - box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f); - LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]); - gGL.color4fv(bg_color.mV); - LLUI::setLineWidth(2.0); - gGL.begin(LLRender::LINES); - { - if (outside_width) - { - LLVector3 vert; - // draw line in x then y - if (mPositionOffset.mV[VX] < 0.f) - { - // start at right edge - vert = width_vec * 0.5f; - gGL.vertex3fv(vert.mV); - } - else - { - // start at left edge - vert = width_vec * -0.5f; - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VX] * x_pixel_vec; - gGL.vertex3fv(vert.mV); - gGL.vertex3fv(vert.mV); - vert -= mPositionOffset.mV[VY] * y_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - else - { - LLVector3 vert; - // draw line in y then x - if (mPositionOffset.mV[VY] < 0.f) - { - // start at top edge - vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - else - { - // start at bottom edge - vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - } - gGL.end(); - LLUI::setLineWidth(1.0); - - } - } - LLUI::popMatrix(); + rect_top_image->draw3D(render_position, x_pixel_vec, y_pixel_vec, label_top_rect, label_top_color); } F32 y_offset = (F32)mOffsetY; @@ -874,29 +715,26 @@ void LLHUDNameTag::updateAll() for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it) { LLHUDNameTag* textp = (*r_it); -// if (textp->mUseBubble) -// { - if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) - { - textp->setLOD(3); - } - else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) - { - textp->setLOD(2); - } - else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) - { - textp->setLOD(1); - } - else - { - textp->setLOD(0); - } - textp->updateSize(); - // find on-screen position and initialize collision rectangle - textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); - current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); -// } + if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) + { + textp->setLOD(3); + } + else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) + { + textp->setLOD(2); + } + else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) + { + textp->setLOD(1); + } + else + { + textp->setLOD(0); + } + textp->updateSize(); + // find on-screen position and initialize collision rectangle + textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); + current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); } LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat(); @@ -914,20 +752,12 @@ void LLHUDNameTag::updateAll() { LLHUDNameTag* src_textp = (*src_it); -// if (!src_textp->mUseBubble) -// { -// continue; -// } VisibleTextObjectIterator dst_it = src_it; ++dst_it; for (; dst_it != sVisibleTextObjects.end(); ++dst_it) { LLHUDNameTag* dst_textp = (*dst_it); -// if (!dst_textp->mUseBubble) -// { -// continue; -// } if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) { LLRectf intersect_rect = src_textp->mSoftScreenRect; @@ -976,10 +806,6 @@ void LLHUDNameTag::updateAll() VisibleTextObjectIterator this_object_it; for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it) { -// if (!(*this_object_it)->mUseBubble) -// { -// continue; -// } (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC)); } } @@ -1037,10 +863,6 @@ void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list) VisibleTextObjectIterator text_it; for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it) { -// if (!(*text_it)->mUseBubble) -// { -// continue; -// } pick_list.insert((*text_it)->mSourceObject); } } diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h index 3325c22def..72647d5b26 100644 --- a/indra/newview/llhudnametag.h +++ b/indra/newview/llhudnametag.h @@ -118,7 +118,6 @@ public: /*virtual*/ void markDead(); friend class LLHUDObject; /*virtual*/ F32 getDistance() const { return mLastDistance; } - //void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; } S32 getLOD() { return mLOD; } BOOL getVisible() { return mVisible; } BOOL getHidden() const { return mHidden; } @@ -136,7 +135,6 @@ protected: LLHUDNameTag(const U8 type); /*virtual*/ void render(); - /*virtual*/ void renderForSelect(); void renderText(BOOL for_select); static void updateAll(); void setLOD(S32 lod); diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 95d57d08d8..0960846510 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -232,9 +232,11 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) case LL_HUD_EFFECT_LOOKAT: hud_objectp = new LLHUDEffectLookAt(type); break; +#ifdef XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_VOICE_VISUALIZER: hud_objectp = new LLVoiceVisualizer(type); break; +#endif // XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_POINTAT: hud_objectp = new LLHUDEffectPointAt(type); break; diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 2f7a98c86c..21cf5fe17c 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -94,7 +94,9 @@ public: LL_HUD_EFFECT_EDIT, LL_HUD_EFFECT_LOOKAT, LL_HUD_EFFECT_POINTAT, +#ifdef XXX_STINSON_CHUI_REWORK LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella +#endif // XXX_STINSON_CHUI_REWORK LL_HUD_NAME_TAG, LL_HUD_EFFECT_BLOB }; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp new file mode 100644 index 0000000000..ec534b903d --- /dev/null +++ b/indra/newview/llimconversation.cpp @@ -0,0 +1,400 @@ +/** + * @file llimconversation.cpp + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llimconversation.h" + +#include "llchatentry.h" +#include "llchathistory.h" +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llimfloater.h" +#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container +#include "lllayoutstack.h" +#include "llnearbychat.h" + +const F32 REFRESH_INTERVAL = 0.2f; + +LLIMConversation::LLIMConversation(const LLUUID& session_id) + : LLTransientDockableFloater(NULL, true, session_id) + , mIsP2PChat(false) + , mExpandCollapseBtn(NULL) + , mTearOffBtn(NULL) + , mCloseBtn(NULL) + , mSessionID(session_id) + , mParticipantList(NULL) + , mChatHistory(NULL) + , mInputEditor(NULL) + , mInputEditorTopPad(0) + , mRefreshTimer(new LLTimer()) +{ + mCommitCallbackRegistrar.add("IMSession.Menu.Action", + boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", + boost::bind(&LLIMConversation::onIMCompactExpandedMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", + boost::bind(&LLIMConversation::onIMShowModesMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", + boost::bind(&LLIMConversation::onIMShowModesMenuItemEnable, this, _2)); + + // Zero expiry time is set only once to allow initial update. + mRefreshTimer->setTimerExpirySec(0); + mRefreshTimer->start(); +} + +LLIMConversation::~LLIMConversation() +{ + if (mParticipantList) + { + delete mParticipantList; + mParticipantList = NULL; + } + + delete mRefreshTimer; +} + +BOOL LLIMConversation::postBuild() +{ + BOOL result; + + mCloseBtn = getChild<LLButton>("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMConversation::onSlide, this)); + + mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); + + mTearOffBtn = getChild<LLButton>("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); + + mChatHistory = getChild<LLChatHistory>("chat_history"); + mInputEditor = getChild<LLChatEntry>("chat_editor"); + + mInputEditor->setTextExpandedCallback(boost::bind(&LLIMConversation::reshapeChatHistory, this)); + mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; + + if (!getTornOff()) + { + setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); + } + + buildParticipantList(); + + updateHeaderAndToolbar(); + + if (isChatMultiTab()) + { + if (mIsNearbyChat) + { + setCanClose(FALSE); + } + result = LLFloater::postBuild(); + } + else + { + result = LLDockableFloater::postBuild(); + } + + return result; +} + +void LLIMConversation::draw() +{ + LLTransientDockableFloater::draw(); + + if (mRefreshTimer->hasExpired()) + { + if (mParticipantList) + { + mParticipantList->update(); + } + + refresh(); + + // Restart the refresh timer + mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); + } +} + +void LLIMConversation::buildParticipantList() +{ + if (mIsNearbyChat) + { + LLLocalSpeakerMgr* speaker_manager = LLLocalSpeakerMgr::getInstance(); + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true, false); + } + else + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + // for group and ad-hoc chat we need to include agent into list + if(!mIsP2PChat && mSessionID.notNull() && speaker_manager) + { + delete mParticipantList; // remove the old list and create a new one if the session id has changed + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true, false); + } + } +} + +void LLIMConversation::onSortMenuItemClicked(const LLSD& userdata) +{ + // TODO: Check this code when sort order menu will be added. (EM) + if (!mParticipantList) + { + return; + } + + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_name") + { + mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); + } + +} + +void LLIMConversation::onIMSessionMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == "compact_view" || item == "expanded_view") + { + gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view"); + } + else + { + bool prev_value = gSavedSettings.getBOOL(item); + gSavedSettings.setBOOL(item, !prev_value); + } + + LLIMConversation::processChatHistoryStyleUpdate(); +} + + +bool LLIMConversation::onIMCompactExpandedMenuItemCheck(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory"); + + return is_plain_text_mode? item == "compact_view" : item == "expanded_view"; +} + + +bool LLIMConversation::onIMShowModesMenuItemCheck(const LLSD& userdata) +{ + return gSavedSettings.getBOOL(userdata.asString()); +} + +// enable/disable states for the "show time" and "show names" items of the show-modes menu +bool LLIMConversation::onIMShowModesMenuItemEnable(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); + bool is_not_names = (item != "IMShowNamesForP2PConv"); + return (plain_text && (is_not_names || mIsP2PChat)); +} + +void LLIMConversation::hideOrShowTitle() +{ + bool is_hosted = getHost() != NULL; + + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + LLView* floater_contents = getChild<LLView>("contents_view"); + + LLRect floater_rect = getLocalRect(); + S32 top_border_of_contents = floater_rect.mTop - (is_hosted? 0 : floater_header_size); + LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); + LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); + mDragHandle->setShape(handle_rect); + mDragHandle->setVisible(! is_hosted); + floater_contents->setShape(contents_rect); +} + +void LLIMConversation::hideAllStandardButtons() +{ + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (mButtons[i]) + { + // Hide the standard header buttons in a docked IM floater. + mButtons[i]->setVisible(false); + } + } +} + +void LLIMConversation::updateHeaderAndToolbar() +{ + bool is_hosted = getHost() != NULL; + + if (is_hosted) + { + hideAllStandardButtons(); + } + + hideOrShowTitle(); + + // Participant list should be visible only in torn off floaters. + bool is_participant_list_visible = + !is_hosted + && gSavedSettings.getBOOL("IMShowControlPanel") + && !mIsP2PChat; + + mParticipantListPanel->setVisible(is_participant_list_visible); + + // Display collapse image (<<) if the floater is hosted + // or if it is torn off but has an open control panel. + bool is_expanded = is_hosted || is_participant_list_visible; + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); + + // toggle floater's drag handle and title visibility + if (mDragHandle) + { + mDragHandle->setTitleVisible(!is_hosted); + } + + // The button (>>) should be disabled for torn off P2P conversations. + mExpandCollapseBtn->setEnabled(is_hosted || !mIsP2PChat); + + mTearOffBtn->setImageOverlay(getString(is_hosted ? "tear_off_icon" : "return_icon")); + + mCloseBtn->setVisible(is_hosted && !mIsNearbyChat); + + enableDisableCallBtn(); + + showTranslationCheckbox(); +} + +void LLIMConversation::reshapeChatHistory() +{ + LLRect chat_rect = mChatHistory->getRect(); + LLRect input_rect = mInputEditor->getRect(); + + int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); + + chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); + mChatHistory->setShape(chat_rect); +} + +void LLIMConversation::showTranslationCheckbox(BOOL show) +{ + getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); +} + +// static +void LLIMConversation::processChatHistoryStyleUpdate() +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); + if (floater) + { + floater->reloadMessages(); + } + } + + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + if (nearby_chat) + { + nearby_chat->reloadMessages(); + } +} + +void LLIMConversation::updateCallBtnState(bool callIsActive) +{ + getChild<LLButton>("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLIMConversation::onSlide(LLIMConversation* self) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(self->getHost()); + if (host_floater) + { + // Hide the messages pane if a floater is hosted in the Conversations + host_floater->collapseMessagesPane(true); + } + else ///< floater is torn off + { + if (!self->mIsP2PChat) + { + bool expand = !self->mParticipantListPanel->getVisible(); + + // Expand/collapse the IM control panel + self->mParticipantListPanel->setVisible(expand); + + gSavedSettings.setBOOL("IMShowControlPanel", expand); + + self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); + } + } +} + +/*virtual*/ +void LLIMConversation::onOpen(const LLSD& key) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); + if (host_floater) + { + // Show the messages pane when opening a floater hosted in the Conversations + host_floater->collapseMessagesPane(false); + } + + updateHeaderAndToolbar(); +} + +// virtual +void LLIMConversation::onClose(bool app_quitting) +{ + // Always suppress the IM from the conversations list on close if present for any reason + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->removeConversationListItem(this); + } + } +} + +void LLIMConversation::onTearOffClicked() +{ + LLFloater::onClickTearOff(this); + updateHeaderAndToolbar(); +} + +// static +bool LLIMConversation::isChatMultiTab() +{ + // Restart is required in order to change chat window type. + return true; +} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h new file mode 100644 index 0000000000..c3dff96d5d --- /dev/null +++ b/indra/newview/llimconversation.h @@ -0,0 +1,128 @@ +/** + * @file llimconversation.h + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_IMCONVERSATION_H +#define LL_IMCONVERSATION_H + +#include "lllayoutstack.h" +#include "llparticipantlist.h" +#include "lltransientdockablefloater.h" +#include "llviewercontrol.h" +#include "lleventtimer.h" + +class LLPanelChatControlPanel; +class LLChatEntry; +class LLChatHistory; + +class LLIMConversation + : public LLTransientDockableFloater +{ + +public: + LOG_CLASS(LLIMConversation); + + LLIMConversation(const LLUUID& session_id); + ~LLIMConversation(); + + // reload all message with new settings of visual modes + static void processChatHistoryStyleUpdate(); + + /** + * Returns true if chat is displayed in multi tabbed floater + * false if chat is displayed in multiple windows + */ + static bool isChatMultiTab(); + + // show/hide the translation check box + void showTranslationCheckbox(const BOOL visible = FALSE); + + // LLFloater overrides + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + +protected: + + // callback for click on any items of the visual states menu + void onIMSessionMenuItemClicked(const LLSD& userdata); + + // callback for check/uncheck of the expanded/collapse mode's switcher + bool onIMCompactExpandedMenuItemCheck(const LLSD& userdata); + + // + bool onIMShowModesMenuItemCheck(const LLSD& userdata); + bool onIMShowModesMenuItemEnable(const LLSD& userdata); + static void onSlide(LLIMConversation *self); + virtual void onTearOffClicked(); + + // refresh a visual state of the Call button + void updateCallBtnState(bool callIsActive); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn() = 0; + + void buildParticipantList(); + void onSortMenuItemClicked(const LLSD& userdata); + + void hideOrShowTitle(); // toggle the floater's drag handle + void hideAllStandardButtons(); + + bool mIsNearbyChat; + bool mIsP2PChat; + + LLLayoutPanel* mParticipantListPanel; + LLParticipantList* mParticipantList; + LLUUID mSessionID; + + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + +private: + /// Refreshes the floater at a constant rate. + virtual void refresh() = 0; + + /// Update floater header and toolbar buttons when hosted/torn off state is toggled. + void updateHeaderAndToolbar(); + + /** + * Adjusts chat history height to fit vertically with input chat field + * and avoid overlapping, since input chat field can be vertically expanded. + * Implementation: chat history bottom "follows" top+top_pad of input chat field + */ + void reshapeChatHistory(); + + LLChatHistory* mChatHistory; + LLChatEntry* mInputEditor; + int mInputEditorTopPad; // padding between input field and chat history + + LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. +}; + + +#endif // LL_IMCONVERSATION_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 63eedcdfea..5e0e0973fc 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -28,84 +28,58 @@ #include "llimfloater.h" +#include "lldraghandle.h" #include "llnotificationsutil.h" #include "llagent.h" #include "llappviewer.h" +#include "llavataractions.h" #include "llavatarnamecache.h" #include "llbutton.h" #include "llchannelmanager.h" #include "llchiclet.h" #include "llchicletbar.h" #include "llfloaterreg.h" +#include "llfloateravatarpicker.h" #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" -#include "lllayoutstack.h" -#include "lllineeditor.h" +//#include "lllayoutstack.h" +#include "llchatentry.h" #include "lllogchat.h" -#include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" #include "lltrans.h" #include "llchathistory.h" #include "llnotifications.h" #include "llviewerwindow.h" -#include "llvoicechannel.h" #include "lltransientfloatermgr.h" #include "llinventorymodel.h" #include "llrootview.h" #include "llspeakers.h" #include "llviewerchat.h" +#include "llnotificationmanager.h" #include "llautoreplace.h" LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLTransientDockableFloater(NULL, true, session_id), - mControlPanel(NULL), - mSessionID(session_id), + : LLIMConversation(session_id), mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), - mChatHistory(NULL), mInputEditor(NULL), mSavedTitle(), mTypingStart(), mShouldSendTypingState(false), + mChatHistory(NULL), mMeTyping(false), mOtherTyping(false), mTypingTimer(), mTypingTimeoutTimer(), mPositioned(false), - mSessionInitialized(false) + mSessionInitialized(false), + mStartConferenceInSameFloater(false) { - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); - if (im_session) - { - mSessionInitialized = im_session->mSessionInitialized; + mIsNearbyChat = false; + initIMSession(session_id); - mDialog = im_session->mType; - switch(mDialog){ - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - break; - case IM_SESSION_CONFERENCE_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - break; - case IM_SESSION_GROUP_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - break; - case IM_SESSION_INVITE: - if (gAgent.isInGroup(mSessionID)) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - } - break; - default: break; - } - } setOverlapsScreenChannel(true); LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); @@ -133,30 +107,58 @@ void LLIMFloater::onFocusReceived() } // virtual -void LLIMFloater::onClose(bool app_quitting) +void LLIMFloater::refresh() +{ + if (mMeTyping) { + // Time out if user hasn't typed for a while. + if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS) + { setTyping(false); + } + } +} - // The source of much argument and design thrashing - // Should the window hide or the session close when the X is clicked? - // - // Last change: - // EXT-3516 X Button should end IM session, _ button should hide - gIMMgr->leaveSession(mSessionID); +// virtual +void LLIMFloater::onClickCloseBtn() +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( + mSessionID); + + if (session == NULL) + { + llwarns << "Empty session." << llendl; + return; +} + + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + + LLIMConversation::onClickCloseBtn(); } /* static */ -void LLIMFloater::newIMCallback(const LLSD& data){ - +void LLIMFloater::newIMCallback(const LLSD& data) +{ if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull()) { LLUUID session_id = data["session_id"].asUUID(); LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) return; // update if visible, otherwise will be updated when opened - if (floater->getVisible()) + if (floater && floater->getVisible()) { floater->updateMessages(); } @@ -189,17 +191,15 @@ void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) void LLIMFloater::sendMsg() { - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) + if (gAgent.isGodlike() + || (mDialog != IM_NOTHING_SPECIAL) + || !mOtherParticipantUUID.isNull()) { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - return; - } - if (mInputEditor) { - LLWString text = mInputEditor->getConvertedText(); + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) { // Truncate and convert to UTF8 for transport @@ -208,8 +208,7 @@ void LLIMFloater::sendMsg() if (mSessionInitialized) { - LLIMModel::sendMessage(utf8_text, mSessionID, - mOtherParticipantUUID,mDialog); + LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); } else { @@ -223,95 +222,316 @@ void LLIMFloater::sendMsg() } } } - - + else + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + } +} LLIMFloater::~LLIMFloater() { + mParticipantsListRefreshConnection.disconnect(); + mVoiceChannelStateChangeConnection.disconnect(); + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); } -//virtual -BOOL LLIMFloater::postBuild() +void LLIMFloater::initIMSession(const LLUUID& session_id) { - const LLUUID& other_party_id = LLIMModel::getInstance()->getOtherParticipantID(mSessionID); + // Change the floater key to bind it to a new session. + setKey(session_id); + + mSessionID = session_id; + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + if (mSession) +{ + mIsP2PChat = mSession->isP2PSessionType(); + mSessionInitialized = mSession->mSessionInitialized; + + mDialog = mSession->mType; + } +} + +void LLIMFloater::initIMFloater() +{ + const LLUUID& other_party_id = + LLIMModel::getInstance()->getOtherParticipantID(mSessionID); if (other_party_id.notNull()) { mOtherParticipantUUID = other_party_id; } - mControlPanel->setSessionId(mSessionID); - mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel")); + boundVoiceChannel(); + + mTypingStart = LLTrans::getString("IM_typing_start_string"); + + // Show control panel in torn off floaters only. + mParticipantListPanel->setVisible(!getHost() && gSavedSettings.getBOOL("IMShowControlPanel")); + + // Disable input editor if session cannot accept text + if ( mSession && !mSession->mTextIMPossible ) + { + mInputEditor->setEnabled(FALSE); + mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + } + + if (mIsP2PChat) + { + // look up display name for window title + LLAvatarNameCache::get(mSession->mOtherParticipantID, + boost::bind(&LLIMFloater::onAvatarNameCache, + this, _1, _2)); + } + else + { + std::string session_name(LLIMModel::instance().getName(mSessionID)); + updateSessionName(session_name, session_name); + + // For ad hoc conferences we should update the title with participants names. + if ((IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mDialog == IM_SESSION_CONFERENCE_START) + { + if (mParticipantsListRefreshConnection.connected()) + { + mParticipantsListRefreshConnection.disconnect(); + } - LLButton* slide_left = getChild<LLButton>("slide_left_btn"); - slide_left->setVisible(mControlPanel->getParent()->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + LLAvatarList* avatar_list = getChild<LLAvatarList>("speakers_list"); + mParticipantsListRefreshConnection = avatar_list->setRefreshCompleteCallback( + boost::bind(&LLIMFloater::onParticipantsListChanged, this, _1)); + } + } +} - LLButton* slide_right = getChild<LLButton>("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getParent()->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); +//virtual +BOOL LLIMFloater::postBuild() +{ + LLIMConversation::postBuild(); - mInputEditor = getChild<LLLineEditor>("chat_editor"); + mInputEditor = getChild<LLChatEntry>("chat_editor"); mInputEditor->setMaxTextLength(1023); // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); + // XXX stinson TODO : resolve merge conflict +#if 0 // *TODO Establish LineEditor with autoreplace callback mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); +#endif LLFontGL* font = LLViewerChat::getChatFont(); mInputEditor->setFont(font); mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); + mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); mInputEditor->setPassDelete( TRUE ); - childSetCommitCallback("chat_editor", onSendMsg, this); + mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); mChatHistory = getChild<LLChatHistory>("chat_history"); setDocked(true); - mTypingStart = LLTrans::getString("IM_typing_start_string"); + LLButton* add_btn = getChild<LLButton>("add_btn"); - // Disable input editor if session cannot accept text - LLIMModel::LLIMSession* im_session = - LLIMModel::instance().findIMSession(mSessionID); - if( im_session && !im_session->mTextIMPossible ) + // Allow to add chat participants depending on the session type + add_btn->setEnabled(isInviteAllowed()); + add_btn->setClickedCallback(boost::bind(&LLIMFloater::onAddButtonClicked, this)); + + childSetAction("voice_call_btn", boost::bind(&LLIMFloater::onCallButtonClicked, this)); + + LLVoiceClient::getInstance()->addObserver(this); + + //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" + //see LLFloaterIMPanel for how it is done (IB) + + initIMFloater(); + + // Add a conversation list item in the left pane + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + im_box->addConversationListItem(getTitle(), getKey(), this); + + return TRUE; +} + +void LLIMFloater::onAddButtonClicked() { - mInputEditor->setEnabled(FALSE); - mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE); + if (!picker) + { + return; } - if ( im_session && im_session->isP2PSessionType()) + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) { - // look up display name for window title - LLAvatarNameCache::get(im_session->mOtherParticipantID, - boost::bind(&LLIMFloater::onAvatarNameCache, - this, _1, _2)); + root_floater->addDependentFloater(picker); + } +} + +bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) +{ + if (!mSession + || mDialog == IM_SESSION_GROUP_START + || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID)) + { + return false; + } + + if (mIsP2PChat) + { + // For a P2P session just check if we are not adding the other participant. + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + if (*id == mOtherParticipantUUID) + { + return false; + } + } } else { - std::string session_name(LLIMModel::instance().getName(mSessionID)); - updateSessionName(session_name, session_name); + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); } - //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" - //see LLFloaterIMPanel for how it is done (IB) + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); + it != speaker_list.end(); ++it) + { + const LLPointer<LLSpeaker>& speaker = *it; + if (*id == speaker->mID) + { + return false; + } + } + } + } - if(isChatMultiTab()) + return true; +} + +void LLIMFloater::addSessionParticipants(const uuid_vec_t& uuids) + { + if (mIsP2PChat) { - return LLFloater::postBuild(); + mStartConferenceInSameFloater = true; + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + + // then we can close the current session + onClose(false); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids, mSessionID); + } + else + { + LLAvatarActions::startConference(temp_ids, mSessionID); } +} else { - return LLDockableFloater::postBuild(); + inviteToSession(uuids); } } +void LLIMFloater::boundVoiceChannel() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if(voice_channel) + { + mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback( + boost::bind(&LLIMFloater::onVoiceChannelStateChanged, this, _1, _2)); + + //call (either p2p, group or ad-hoc) can be already in started state + bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); + } +} + +void LLIMFloater::enableDisableCallBtn() +{ + bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking(); + + if (mSession) + { + bool session_initialized = mSession->mSessionInitialized; + bool callback_enabled = mSession->mCallBackEnabled; + + BOOL enable_connect = + session_initialized && voice_enabled && callback_enabled; + getChildView("voice_call_btn")->setEnabled(enable_connect); + } + else + { + getChildView("voice_call_btn")->setEnabled(false); + } +} + + +void LLIMFloater::onCallButtonClicked() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if (voice_channel) + { + bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + if (is_call_active) + { + gIMMgr->endCall(mSessionID); + } + else + { + gIMMgr->startCall(mSessionID); + } + } +} + +void LLIMFloater::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) + { + enableDisableCallBtn(); + } +} + +void LLIMFloater::onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) +{ + bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); +} + void LLIMFloater::updateSessionName(const std::string& ui_title, const std::string& ui_label) { @@ -329,58 +549,59 @@ void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, mTypingStart.setArg("[NAME]", ui_title); } -// virtual -void LLIMFloater::draw() +void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) { - if ( mMeTyping ) + LLAvatarList* avatar_list = dynamic_cast<LLAvatarList*>(ctrl); + if (!avatar_list) { - // Time out if user hasn't typed for a while. - if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS ) - { - setTyping(false); + return; } - } - LLTransientDockableFloater::draw(); -} + bool all_names_resolved = true; + std::vector<LLSD> participants_uuids; + avatar_list->getValues(participants_uuids); -// static -void* LLIMFloater::createPanelIMControl(void* userdata) + // Check whether we have all participants names in LLAvatarNameCache + for (std::vector<LLSD>::const_iterator it = participants_uuids.begin(); it != participants_uuids.end(); ++it) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelIMControlPanel(); - self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); - return self->mControlPanel; -} - + const LLUUID& id = it->asUUID(); + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) + { + all_names_resolved = false; -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; + // If a name is not found in cache, request it and continue the process recursively + // until all ids are resolved into names. + LLAvatarNameCache::get(id, + boost::bind(&LLIMFloater::onParticipantsListChanged, this, avatar_list)); + break; + } } -// static -void* LLIMFloater::createPanelAdHocControl(void* userdata) + if (all_names_resolved) + { + std::vector<LLAvatarName> avatar_names; + std::vector<LLSD>::const_iterator it = participants_uuids.begin(); + for (; it != participants_uuids.end(); ++it) + { + const LLUUID& id = it->asUUID(); + LLAvatarName av_name; + if (LLAvatarNameCache::get(id, &av_name)) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); - return self->mControlPanel; + avatar_names.push_back(av_name); + } } -void LLIMFloater::onSlide() + // We should check whether the vector is not empty to pass the assertion + // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. + if (!avatar_names.empty()) { - mControlPanel->getParent()->setVisible(!mControlPanel->getParent()->getVisible()); - - gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getParent()->getVisible()); - - getChild<LLButton>("slide_left_btn")->setVisible(mControlPanel->getParent()->getVisible()); - getChild<LLButton>("slide_right_btn")->setVisible(!mControlPanel->getParent()->getVisible()); + std::string ui_title; + LLAvatarActions::buildResidentsString(avatar_names, ui_title); + updateSessionName(ui_title, ui_title); + } +} } //static @@ -388,7 +609,8 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { closeHiddenIMToasts(); - if (!gIMMgr->hasSession(session_id)) return NULL; + if (!gIMMgr->hasSession(session_id)) + return NULL; if(!isChatMultiTab()) { @@ -405,22 +627,24 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) } } + // Test the existence of the floater before we try to create it bool exist = findInstance(session_id); + // Get the floater: this will create the instance if it didn't exist LLIMFloater* floater = getInstance(session_id); - if (!floater) return NULL; + if (!floater) + return NULL; if(isChatMultiTab()) { LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - // do not add existed floaters to avoid adding torn off instances + // Do not add again existing floaters if (!exist) { // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; - if (floater_container) { floater_container->addFloater(floater, TRUE, i_pt); @@ -458,6 +682,37 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) return floater; } +//static +LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +void LLIMFloater::onClose(bool app_quitting) +{ + setTyping(false); + + // The source of much argument and design thrashing + // Should the window hide or the session close when the X is clicked? + // + // Last change: + // EXT-3516 X Button should end IM session, _ button should hide + gIMMgr->leaveSession(mSessionID); + + // Clean up the conversation *after* the session has been ended + LLIMConversation::onClose(app_quitting); +} void LLIMFloater::setDocked(bool docked, bool pop_on_undock) { @@ -520,9 +775,12 @@ void LLIMFloater::setVisible(BOOL visible) BOOL LLIMFloater::getVisible() { + bool visible; + if(isChatMultiTab()) { - LLIMFloaterContainer* im_container = LLIMFloaterContainer::getInstance(); + LLIMFloaterContainer* im_container = + LLIMFloaterContainer::getInstance(); // Treat inactive floater as invisible. bool is_active = im_container->getActiveFloater() == this; @@ -530,16 +788,21 @@ BOOL LLIMFloater::getVisible() //torn off floater is always inactive if (!is_active && getHost() != im_container) { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } - + else + { // getVisible() returns TRUE when Tabbed IM window is minimized. - return is_active && !im_container->isMinimized() && im_container->getVisible(); + visible = is_active && !im_container->isMinimized() + && im_container->getVisible(); + } } else { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } + + return visible; } //static @@ -547,7 +810,8 @@ bool LLIMFloater::toggle(const LLUUID& session_id) { if(!isChatMultiTab()) { - LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>( + "impanel", session_id); if (floater && floater->getVisible() && floater->hasFocus()) { // clicking on chiclet to close floater just hides it to maintain existing @@ -568,17 +832,6 @@ bool LLIMFloater::toggle(const LLUUID& session_id) return true; } -//static -LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) -{ - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); -} - void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; @@ -586,53 +839,57 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) //will be different only for an ad-hoc im session if (mSessionID != im_session_id) { - mSessionID = im_session_id; - setKey(im_session_id); - mControlPanel->setSessionId(im_session_id); - } + initIMSession(im_session_id); - // updating "Call" button from group control panel here to enable it without placing into draw() (EXT-4796) - if(gAgent.isInGroup(im_session_id)) - { - mControlPanel->updateCallButton(); + buildParticipantList(); } + + initIMFloater(); //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) - //need to send delayed messaged collected while waiting for session initialization - if (!mQueuedMsgsForInit.size()) return; + if (mQueuedMsgsForInit.size()) + { LLSD::array_iterator iter; for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); - ++iter) + iter != mQueuedMsgsForInit.endArray(); ++iter) { LLIMModel::sendMessage(iter->asString(), mSessionID, mOtherParticipantUUID, mDialog); } } +} -void LLIMFloater::updateMessages() +void LLIMFloater::appendMessage(const LLChat& chat, const LLSD &args) { - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); + LLChat& tmp_chat = const_cast<LLChat&>(chat); + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = args; + chat_args["use_plain_text_chat_history"] = + gSavedSettings.getBOOL("PlainTextChatHistory"); + chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); + chat_args["show_names_for_p2p_conv"] = !mIsP2PChat + || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLIMFloater::updateMessages() +{ std::list<LLSD> messages; // we shouldn't reset unread message counters if IM floater doesn't have focus - if (hasFocus()) - { - LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex+1); - } - else - { - LLIMModel::instance().getMessagesSilently(mSessionID, messages, mLastMessageIndex+1); - } + LLIMModel::instance().getMessages( + mSessionID, messages, mLastMessageIndex + 1, hasFocus()); if (messages.size()) { - LLSD chat_args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - std::ostringstream message; std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); @@ -682,7 +939,7 @@ void LLIMFloater::updateMessages() chat.mText = message; } - mChatHistory->appendMessage(chat, chat_args); + appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); // if it is a notification - next message is a notification history log, so skip it @@ -706,6 +963,7 @@ void LLIMFloater::reloadMessages() mChatHistory->clear(); mLastMessageIndex = -1; updateMessages(); + mInputEditor->setFont(LLViewerChat::getChatFont()); } // static @@ -732,20 +990,14 @@ void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userd } // static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +void LLIMFloater::onInputEditorKeystroke(LLTextEditor* caller, void* userdata) { LLIMFloater* self = (LLIMFloater*)userdata; std::string text = self->mInputEditor->getText(); - if (!text.empty()) - { - self->setTyping(true); - } - else - { + // Deleting all text counts as stopping typing. - self->setTyping(false); + self->setTyping(!text.empty()); } -} void LLIMFloater::setTyping(bool typing) { @@ -769,27 +1021,25 @@ void LLIMFloater::setTyping(bool typing) // much network traffic. Only send in person-to-person IMs. if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL ) { - if ( mMeTyping ) + // Still typing, send 'start typing' notification or + // send 'stop typing' notification immediately + if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) { - if ( mTypingTimer.getElapsedTimeF32() > 1.f ) - { - // Still typing, send 'start typing' notification - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); + LLIMModel::instance().sendTypingState(mSessionID, + mOtherParticipantUUID, mMeTyping); mShouldSendTypingState = false; + } } - else - { - // Send 'stop typing' notification immediately - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE); - mShouldSendTypingState = false; - } - } + if (!mIsNearbyChat) + { LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); if (speaker_mgr) + { speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); - + } +} } void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) @@ -808,9 +1058,7 @@ void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) void LLIMFloater::processAgentListUpdates(const LLSD& body) { - if ( !body.isMap() ) return; - - if ( body.has("agent_updates") && body["agent_updates"].isMap() ) + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) { LLSD agent_data = body["agent_updates"].get(gAgentID.asString()); if (agent_data.isMap() && agent_data.has("info")) @@ -835,30 +1083,6 @@ void LLIMFloater::processAgentListUpdates(const LLSD& body) } } -void LLIMFloater::updateChatHistoryStyle() -{ - mChatHistory->clear(); - mLastMessageIndex = -1; - updateMessages(); -} - -void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFontGL* font = LLViewerChat::getChatFont(); - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater) - { - floater->updateChatHistoryStyle(); - floater->mInputEditor->setFont(font); - } - } - -} - void LLIMFloater::processSessionUpdate(const LLSD& session_update) { // *TODO : verify following code when moderated mode will be implemented @@ -870,7 +1094,8 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) if (voice_moderated) { - setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label")); + setTitle(session_label + std::string(" ") + + LLTrans::getString("IM_moderated_chat_label")); } else { @@ -883,98 +1108,56 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) } } -BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, +// virtual +BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, std::string& tooltip_msg) { - - if (mDialog == IM_NOTHING_SPECIAL) + if (cargo_type == DAD_PERSON) { - LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, - cargo_type, cargo_data, accept); + if (dropPerson(static_cast<LLUUID*>(cargo_data), drop)) + { + *accept = ACCEPT_YES_MULTI; } - - // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites - else if (isInviteAllowed()) + else { *accept = ACCEPT_NO; - - if (cargo_type == DAD_CALLINGCARD) - { - if (dropCallingCard((LLInventoryItem*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; } } - else if (cargo_type == DAD_CATEGORY) + else if (mDialog == IM_NOTHING_SPECIAL) { - if (dropCategory((LLInventoryCategory*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; + LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, + cargo_type, cargo_data, accept); } - } - } + return TRUE; } -BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop) +bool LLIMFloater::dropPerson(LLUUID* person_id, bool drop) { - BOOL rv = isInviteAllowed(); - if(rv && item && item->getCreatorUUID().notNull()) + bool res = person_id && person_id->notNull(); + if(res) { - if(drop) - { uuid_vec_t ids; - ids.push_back(item->getCreatorUUID()); - inviteToSession(ids); - } - } - else + ids.push_back(*person_id); + + res = canAddSelectedToChat(ids); + if(res && drop) { - // set to false if creator uuid is null. - rv = FALSE; + addSessionParticipants(ids); } - return rv; } -BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop) -{ - BOOL rv = isInviteAllowed(); - if(rv && category) - { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLUniqueBuddyCollector buddies; - gInventory.collectDescendentsIf(category->getUUID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - buddies); - S32 count = items.count(); - if(count == 0) - { - rv = FALSE; + return res; } - else if(drop) - { - uuid_vec_t ids; - ids.reserve(count); - for(S32 i = 0; i < count; ++i) - { - ids.push_back(items.get(i)->getCreatorUUID()); - } - inviteToSession(ids); - } - } - return rv; -} BOOL LLIMFloater::isInviteAllowed() const { - return ( (IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE == mDialog) ); + || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mIsP2PChat); } class LLSessionInviteResponder : public LLHTTPClient::Responder @@ -998,11 +1181,10 @@ private: BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); - if (!region) - { - return FALSE; - } + bool is_region_exist = region != NULL; + if (is_region_exist) + { S32 count = ids.size(); if( isInviteAllowed() && (count > 0) ) @@ -1024,8 +1206,7 @@ BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) LLHTTPClient::post( url, data, - new LLSessionInviteResponder( - mSessionID)); + new LLSessionInviteResponder(mSessionID)); } else { @@ -1035,8 +1216,9 @@ BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) // successful add, because everyone that needed to get added // was added. } + } - return TRUE; + return is_region_exist; } void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) @@ -1077,7 +1259,6 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); } } - } } @@ -1094,7 +1275,8 @@ void LLIMFloater::closeHiddenIMToasts() } }; - LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); + LLNotificationsUI::LLScreenChannel* channel = + LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); if (channel != NULL) { channel->closeHiddenToasts(IMToastMatcher()); @@ -1107,7 +1289,7 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"]; - LLFloater* im_floater = LLFloaterReg::findInstance("impanel", session_id); + LLFloater* im_floater = findInstance(session_id); if (option == 0 && im_floater != NULL) { im_floater->closeFloater(); @@ -1117,79 +1299,44 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& } // static -bool LLIMFloater::isChatMultiTab() -{ - // Restart is required in order to change chat window type. - static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; - return is_single_window; -} - -// static -void LLIMFloater::initIMFloater() -{ - // This is called on viewer start up - // init chat window type before user changed it in preferences - isChatMultiTab(); -} - -//static void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) { LLUUID session_id = data["session_id"]; - if (session_id.isNull()) return; + if (session_id.isNull()) + return; LLUUID from_id = data["from_id"]; - if (gAgentID == from_id || LLUUID::null == from_id) return; + if (gAgentID == from_id || LLUUID::null == from_id) + return; LLIMFloater* floater = LLIMFloater::findInstance(session_id); - if (!floater) return; + if (!floater) + return; - if (IM_NOTHING_SPECIAL != floater->mDialog) return; + if (IM_NOTHING_SPECIAL != floater->mDialog) + return; floater->removeTypingIndicator(); } void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { - - if (isChatMultiTab()) - { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); - if (!im_box) return; - - if (LLIMFloater::findInstance(session_id)) return; - - LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); - - im_box->addFloater(new_tab, FALSE, LLTabContainer::END); - } - + LLIMFloater::addToHost(session_id); } - -void LLIMFloater::onClickCloseBtn() +void LLIMFloater::addToHost(const LLUUID& session_id) + { + if (LLIMConversation::isChatMultiTab()) { - - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); - - if (session == NULL) + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (!im_box) { - llwarns << "Empty session." << llendl; - return; + im_box = LLIMFloaterContainer::getInstance(); } - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (is_call_with_chat && voice_channel != NULL && voice_channel->isActive()) + if (im_box && !LLIMFloater::findInstance(session_id)) { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); - return; + LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); + im_box->addFloater(new_tab, FALSE, LLTabContainer::END); + } } - - LLFloater::onClickCloseBtn(); } diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index f7cd35b5eb..2ac11ded20 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -27,13 +27,18 @@ #ifndef LL_IMFLOATER_H #define LL_IMFLOATER_H +#include "llimview.h" +#include "llimconversation.h" #include "llinstantmessage.h" #include "lllogchat.h" #include "lltooldraganddrop.h" -#include "lltransientdockablefloater.h" +#include "llvoicechannel.h" +#include "llvoiceclient.h" class LLAvatarName; -class LLLineEditor; +class LLButton; +class LLChatEntry; +class LLTextEditor; class LLPanelChatControlPanel; class LLChatHistory; class LLInventoryItem; @@ -43,25 +48,32 @@ class LLInventoryCategory; * Individual IM window that appears at the bottom of the screen, * optionally "docked" to the bottom tray. */ -class LLIMFloater : public LLTransientDockableFloater +class LLIMFloater + : public LLVoiceClientStatusObserver + , public LLIMConversation { LOG_CLASS(LLIMFloater); public: LLIMFloater(const LLUUID& session_id); virtual ~LLIMFloater(); - + + void initIMSession(const LLUUID& session_id); + void initIMFloater(); + // LLView overrides /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. - /*virtual*/ void draw(); + + static LLIMFloater* findInstance(const LLUUID& session_id); + static LLIMFloater* getInstance(const LLUUID& session_id); + static void addToHost(const LLUUID& session_id); // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); - // Make IM conversion visible and update the message history static LLIMFloater* show(const LLUUID& session_id); @@ -69,16 +81,12 @@ public: // Returns true iff panel became visible static bool toggle(const LLUUID& session_id); - static LLIMFloater* findInstance(const LLUUID& session_id); - - static LLIMFloater* getInstance(const LLUUID& session_id); - void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel void updateMessages(); void reloadMessages(); - static void onSendMsg( LLUICtrl*, void*); + static void onSendMsg(LLUICtrl*, void*); void sendMsg(); // callback for LLIMModel on new messages @@ -89,62 +97,72 @@ public: void setPositioned(bool b) { mPositioned = b; }; void onVisibilityChange(const LLSD& new_visibility); + + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + void onChange(EStatusType status, const std::string &channelURI, + bool proximal); + + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + virtual void onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, + const LLVoiceChannel::EState& new_state); + void processIMTyping(const LLIMInfo* im_info, BOOL typing); void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - void updateChatHistoryStyle(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg); - - /** - * Returns true if chat is displayed in multi tabbed floater - * false if chat is displayed in multiple windows - */ - static bool isChatMultiTab(); - - static void initIMFloater(); //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } - -protected: - /* virtual */ - void onClickCloseBtn(); + bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } private: // process focus events to set a currently active session /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); + /*virtual*/ void refresh(); + + /*virtual*/ void onClickCloseBtn(); + // Update the window title, input field help text, etc. void updateSessionName(const std::string& ui_title, const std::string& ui_label); - + // For display name lookups for IM window titles void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - - BOOL dropCallingCard(LLInventoryItem* item, BOOL drop); - BOOL dropCategory(LLInventoryCategory* category, BOOL drop); + + /// Updates the list of ad hoc conference participants + /// in an IM floater title. + void onParticipantsListChanged(LLUICtrl* ctrl); + + bool dropPerson(LLUUID* person_id, bool drop); BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); - - static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - void setTyping(bool typing); - void onSlide(); - static void* createPanelIMControl(void* userdata); - static void* createPanelGroupControl(void* userdata); - static void* createPanelAdHocControl(void* userdata); + void appendMessage(const LLChat& chat, const LLSD &args = 0); + static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); + void setTyping(bool typing); + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids); + + void onCallButtonClicked(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + void boundVoiceChannel(); // Add the "User is typing..." indicator. void addTypingIndicator(const LLIMInfo* im_info); @@ -156,14 +174,15 @@ private: static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; + + LLIMModel::LLIMSession* mSession; S32 mLastMessageIndex; + LLChatHistory* mChatHistory; + EInstantMessage mDialog; LLUUID mOtherParticipantUUID; - LLChatHistory* mChatHistory; - LLLineEditor* mInputEditor; + LLChatEntry* mInputEditor; bool mPositioned; std::string mSavedTitle; @@ -176,7 +195,13 @@ private: bool mSessionInitialized; LLSD mQueuedMsgsForInit; -}; + bool mStartConferenceInSameFloater; + + // connection to voice channel state change signal + boost::signals2::connection mVoiceChannelStateChangeConnection; + + boost::signals2::connection mParticipantsListRefreshConnection; +}; #endif // LL_IMFLOATER_H diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 0f0ae896a2..386afab77f 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -27,20 +27,34 @@ #include "llviewerprecompiledheaders.h" +#include "llimfloater.h" #include "llimfloatercontainer.h" + #include "llfloaterreg.h" -#include "llimview.h" +#include "lllayoutstack.h" +#include "llnearbychat.h" + +#include "llagent.h" +#include "llavataractions.h" #include "llavatariconctrl.h" +#include "llavatarnamecache.h" #include "llgroupiconctrl.h" -#include "llagent.h" +#include "llfloateravatarpicker.h" +#include "llimview.h" #include "lltransientfloatermgr.h" +#include "llviewercontrol.h" // // LLIMFloaterContainer // LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -: LLMultiFloater(seed) +: LLMultiFloater(seed), + mExpandCollapseBtn(NULL), + mConversationsRoot(NULL) { + // Firstly add our self to IMSession observers, so we catch session events + LLIMMgr::getInstance()->addSessionObserver(this); + mAutoResize = FALSE; LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); } @@ -49,6 +63,26 @@ LLIMFloaterContainer::~LLIMFloaterContainer() { mNewMessageConnection.disconnect(); LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); + + gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); + gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); + + if (!LLSingleton<LLIMMgr>::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } +} + +void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) +{ + LLIMFloater::show(session_id); +} + +void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) +{ + LLIMFloater* floaterp = LLIMFloater::findInstance(session_id); + LLFloater::onClickClose(floaterp); + removeConversationListItem(floaterp); } BOOL LLIMFloaterContainer::postBuild() @@ -56,24 +90,46 @@ BOOL LLIMFloaterContainer::postBuild() mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLIMFloaterContainer::onNewMessageReceived, this, _1)); // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button // mTabContainer will be initialized in LLMultiFloater::addChild() + + setTabContainer(getChild<LLTabContainer>("im_box_tab_container")); + + mConversationsStack = getChild<LLLayoutStack>("conversations_stack"); + mConversationsPane = getChild<LLLayoutPanel>("conversations_layout_panel"); + mMessagesPane = getChild<LLLayoutPanel>("messages_layout_panel"); + + mConversationsListPanel = getChild<LLPanel>("conversations_list_panel"); + + // CHUI-98 : View Model for conversations + LLConversationItem* base_item = new LLConversationItem(); + LLFolderView::Params p; + p.view_model = &mConversationViewModel; + p.parent_panel = mConversationsListPanel; + p.rect = mConversationsListPanel->getLocalRect(); + p.follows.flags = FOLLOWS_ALL; + p.listener = base_item; + + mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); + mConversationsListPanel->addChild(mConversationsRoot); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMFloaterContainer::onExpandCollapseButtonClicked, this)); + + childSetAction("add_btn", boost::bind(&LLIMFloaterContainer::onAddButtonClicked, this)); + + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); + LLAvatarNameCache::addUseDisplayNamesCallback( + boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); + return TRUE; } void LLIMFloaterContainer::onOpen(const LLSD& key) { LLMultiFloater::onOpen(key); -/* - if (key.isDefined()) - { - LLIMFloater* im_floater = LLIMFloater::findInstance(key.asUUID()); - if (im_floater) - { - im_floater->openFloater(); - } - } -*/ } +// virtual void LLIMFloaterContainer::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point) @@ -86,11 +142,15 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, openFloater(floaterp->getKey()); return; } + + // Make sure the message panel is open when adding a floater or it stays mysteriously hidden + collapseMessagesPane(false); + // Add the floater LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); LLUUID session_id = floaterp->getKey(); - + LLIconCtrl* icon = 0; if(gAgent.isInGroup(session_id, TRUE)) @@ -116,12 +176,44 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, mTabContainer->setTabImage(floaterp, icon); } + void LLIMFloaterContainer::onCloseFloater(LLUUID& id) { mSessions.erase(id); setFocus(TRUE); } +// virtual +void LLIMFloaterContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + S32 conversations_pane_min_dim = mConversationsPane->getMinDim(); + + if (is_right_pane_expanded) + { + S32 conversations_pane_width = + (is_left_pane_expanded ? gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") : conversations_pane_min_dim); + + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getPanelByIndex(tab_idx)); + if (floaterp) + { + new_min_width = llmax(new_min_width, + floaterp->getMinWidth() + conversations_pane_width + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight()); + } + } + } + else + { + new_min_width = conversations_pane_min_dim; + } +} + void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) { LLUUID session_id = data["session_id"].asUUID(); @@ -136,6 +228,21 @@ void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) } } +void LLIMFloaterContainer::onExpandCollapseButtonClicked() +{ + if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() + && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) + { + // Expand the messages pane from ultra minimized state + // if it was collapsed last in order. + collapseMessagesPane(false); + } + else + { + collapseConversationsPane(!mConversationsPane->isCollapsed()); + } +} + LLIMFloaterContainer* LLIMFloaterContainer::findInstance() { return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container"); @@ -151,8 +258,6 @@ void LLIMFloaterContainer::setMinimized(BOOL b) if (isMinimized() == b) return; LLMultiFloater::setMinimized(b); - // Hide minimized floater (see EXT-5315) - setVisible(!b); if (isMinimized()) return; @@ -162,4 +267,337 @@ void LLIMFloaterContainer::setMinimized(BOOL b) } } +void LLIMFloaterContainer::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } + LLFloater::draw(); + + repositioningWidgets(); +} + +void LLIMFloaterContainer::tabClose() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } +} + +void LLIMFloaterContainer::setVisible(BOOL visible) +{ + if (visible) + { + // Make sure we have the Nearby Chat present when showing the conversation container + LLFloater* nearby_chat = LLFloaterReg::findInstance("chat_bar"); + if (nearby_chat == NULL) + { + // If not found, force the creation of the nearby chat conversation panel + // *TODO: find a way to move this to XML as a default panel or something like that + LLSD name("chat_bar"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } + } + + // We need to show/hide all the associated conversations that have been torn off + // (and therefore, are not longer managed by the multifloater), + // so that they show/hide with the conversations manager. + conversations_items_map::iterator item_it = mConversationsItems.begin(); + for (;item_it != mConversationsItems.end(); ++item_it) + { + LLConversationItem* item = item_it->second; + item->setVisibleIfDetached(visible); + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); + +} + +void LLIMFloaterContainer::collapseMessagesPane(bool collapse) +{ + if (mMessagesPane->isCollapsed() == collapse) + { + return; + } + + if (collapse) + { + // Save the messages pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", mMessagesPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); + } + + // Show/hide the messages pane. + mConversationsStack->collapsePanel(mMessagesPane, collapse); + + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); +} + +void LLIMFloaterContainer::collapseConversationsPane(bool collapse) +{ + if (mConversationsPane->isCollapsed() == collapse) + { + return; + } + + LLView* button_panel = getChild<LLView>("conversations_pane_buttons_expanded"); + button_panel->setVisible(!collapse); + mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); + + if (collapse) + { + // Save the conversations pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); + } + + mConversationsStack->collapsePanel(mConversationsPane, collapse); + + S32 collapsed_width = mConversationsPane->getMinDim(); + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); +} + +void LLIMFloaterContainer::updateState(bool collapse, S32 delta_width) +{ + LLRect floater_rect = getRect(); + floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); + + // Set by_user = true so that reshaped rect is saved in user_settings. + setShape(floater_rect, true); + + updateResizeLimits(); + + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + setCanResize(is_left_pane_expanded || is_right_pane_expanded); + setCanMinimize(is_left_pane_expanded || is_right_pane_expanded); +} + +void LLIMFloaterContainer::onAddButtonClicked() +{ + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloaterContainer::onAvatarPicked, this, _1), TRUE, TRUE); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (picker && root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +void LLIMFloaterContainer::onAvatarPicked(const uuid_vec_t& ids) +{ + if (ids.size() == 1) + { + LLAvatarActions::startIM(ids.back()); + } + else + { + LLAvatarActions::startConference(ids); + } +} + +void LLIMFloaterContainer::repositioningWidgets() +{ + LLRect panel_rect = mConversationsListPanel->getRect(); + S32 item_height = 16; + int index = 0; + for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + widget_it != mConversationsWidgets.end(); + widget_it++, ++index) + { + LLFolderViewItem* widget = widget_it->second; + widget->setRect(LLRect(0, + panel_rect.getHeight() - item_height*index, + panel_rect.getWidth(), + panel_rect.getHeight() - item_height*(index+1))); + } +} + +// CHUI-137 : Temporary implementation of conversations list +void LLIMFloaterContainer::addConversationListItem(std::string name, const LLUUID& uuid, LLFloater* floaterp) +{ + // Check if the item is not already in the list, exit if it is and has the same name and uuid (nothing to do) + // Note: this happens often, when reattaching a torn off conversation for instance + conversations_items_map::iterator item_it = mConversationsItems.find(floaterp); + if (item_it != mConversationsItems.end()) + { + LLConversationItem* item = item_it->second; + // Check if the item has changed + if (item->hasSameValues(name,uuid)) + { + // If it hasn't changed, nothing to do -> exit + return; + } + } + + // Remove the conversation item that might exist already: it'll be recreated anew further down anyway + // and nothing wrong will happen removing it if it doesn't exist + removeConversationListItem(floaterp,false); + + // Create a conversation item + LLConversationItem* item = new LLConversationItem(name, uuid, floaterp, this); + mConversationsItems[floaterp] = item; + + // Create a widget from it + LLFolderViewItem* widget = createConversationItemWidget(item); + mConversationsWidgets[floaterp] = widget; + + // Add a new conversation widget to the root folder of a folder view. + mConversationsRoot->addItem(widget); + + // Add it to the UI + widget->setVisible(TRUE); + + repositioningWidgets(); + + mConversationsListPanel->addChild(widget); + + return; +} + +void LLIMFloaterContainer::removeConversationListItem(LLFloater* floaterp, bool change_focus) +{ + // Delete the widget and the associated conversation item + // Note : since the mConversationsItems is also the listener to the widget, deleting + // the widget will also delete its listener + conversations_widgets_map::iterator widget_it = mConversationsWidgets.find(floaterp); + if (widget_it != mConversationsWidgets.end()) + { + LLFolderViewItem* widget = widget_it->second; + widget->destroyView(); + } + + // Suppress the conversation items and widgets from their respective maps + mConversationsItems.erase(floaterp); + mConversationsWidgets.erase(floaterp); + + repositioningWidgets(); + + // Don't let the focus fall IW, select and refocus on the first conversation in the list + if (change_focus) + { + setFocus(TRUE); + conversations_items_map::iterator item_it = mConversationsItems.begin(); + if (item_it != mConversationsItems.end()) + { + LLConversationItem* item = item_it->second; + item->selectItem(); + } + } + return; +} + +LLFloater* LLIMFloaterContainer::findConversationItem(LLUUID& uuid) +{ + LLFloater* floaterp = NULL; + for (conversations_items_map::iterator item_it = mConversationsItems.begin(); item_it != mConversationsItems.end(); ++item_it) + { + LLConversationItem* item = item_it->second; + if (item->hasSameValue(uuid)) + { + floaterp = item_it->first; + break; + } + } + return floaterp; +} + +LLFolderViewItem* LLIMFloaterContainer::createConversationItemWidget(LLConversationItem* item) +{ + LLFolderViewItem::Params params; + + params.name = item->getDisplayName(); + //params.icon = bridge->getIcon(); + //params.icon_open = bridge->getOpenIcon(); + //params.creation_date = bridge->getCreationDate(); + params.root = mConversationsRoot; + params.listener = item; + params.rect = LLRect (0, 0, 0, 0); + params.tool_tip = params.name; + + return LLUICtrlFactory::create<LLFolderViewItem>(params); +} + +// Conversation items +LLConversationItem::LLConversationItem(std::string name, const LLUUID& uuid, LLFloater* floaterp, LLIMFloaterContainer* containerp) : + mName(name), + mUUID(uuid), + mFloater(floaterp), + mContainer(containerp) +{ +} + +LLConversationItem::LLConversationItem() : + mName(""), + mUUID(), + mFloater(NULL), + mContainer(NULL) +{ +} + + +// Virtual action callbacks +void LLConversationItem::selectItem(void) +{ + LLMultiFloater* host_floater = mFloater->getHost(); + if (host_floater == mContainer) + { + // Always expand the message pane if the panel is hosted by the container + mContainer->collapseMessagesPane(false); + // Switch to the conversation floater that is being selected + mContainer->selectFloater(mFloater); + } + // Set the focus on the selected floater + mFloater->setFocus(TRUE); +} + +void LLConversationItem::setVisibleIfDetached(BOOL visible) +{ + // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized + // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here + if (!mFloater->getHost() && !mFloater->isMinimized()) + { + mFloater->setVisible(visible); + } +} + +void LLConversationItem::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLConversationItem::openItem( void ) +{ +} + +void LLConversationItem::closeItem( void ) +{ +} + +void LLConversationItem::previewItem( void ) +{ +} + +void LLConversationItem::showProperties(void) +{ +} + +bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const +{ + // We compare only by name for the moment + // *TODO : Implement the sorting by date + S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + return (compare < 0); +} + // EOF diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 892ecef48d..7005ab7b6a 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -30,14 +30,182 @@ #include <map> #include <vector> +#include "llimview.h" #include "llfloater.h" #include "llmultifloater.h" #include "llavatarpropertiesprocessor.h" #include "llgroupmgr.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" + +class LLButton; +class LLLayoutPanel; +class LLLayoutStack; class LLTabContainer; +class LLIMFloaterContainer; + +// CHUI-137 : Temporary implementation of conversations list +class LLConversationItem; -class LLIMFloaterContainer : public LLMultiFloater +typedef std::map<LLFloater*, LLConversationItem*> conversations_items_map; +typedef std::map<LLFloater*, LLFolderViewItem*> conversations_widgets_map; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each +// that we tuck into the mConversationsListPanel. +class LLConversationItem : public LLFolderViewModelItemCommon +{ +public: + LLConversationItem(std::string name, const LLUUID& uuid, LLFloater* floaterp, LLIMFloaterContainer* containerp); + LLConversationItem(); + virtual ~LLConversationItem() {} + + // Stub those things we won't really be using in this conversation context + virtual const std::string& getName() const { return mName; } + virtual const std::string& getDisplayName() const { return mName; } + virtual const std::string& getSearchableName() const { return mName; } + virtual const LLUUID& getUUID() const { return mUUID; } + virtual time_t getCreationDate() const { return 0; } + virtual LLPointer<LLUIImage> getIcon() const { return NULL; } + virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } + virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } + virtual std::string getLabelSuffix() const { return LLStringUtil::null; } + virtual BOOL isItemRenameable() const { return FALSE; } + virtual BOOL renameItem(const std::string& new_name) { return FALSE; } + virtual BOOL isItemMovable( void ) const { return FALSE; } + virtual BOOL isItemRemovable( void ) const { return FALSE; } + virtual BOOL isItemInTrash( void) const { return FALSE; } + virtual BOOL removeItem() { return FALSE; } + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } + virtual void move( LLFolderViewModelItem* parent_listener ) { } + virtual BOOL isItemCopyable() const { return FALSE; } + virtual BOOL copyToClipboard() const { return FALSE; } + virtual BOOL cutToClipboard() const { return FALSE; } + virtual BOOL isClipboardPasteable() const { return FALSE; } + virtual void pasteFromClipboard() { } + virtual void pasteLinkFromClipboard() { } + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } + virtual BOOL isUpToDate() const { return TRUE; } + virtual bool hasChildren() const { return FALSE; } + + virtual bool potentiallyVisible() { return true; } + virtual void filter( LLFolderViewFilter& filter) { } + virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } + virtual void setPassedFilter(bool passed, bool passed_folder, S32 filter_generation) { } + virtual bool passedFilter(S32 filter_generation = -1) { return true; } + + // The action callbacks + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void openItem( void ); + virtual void closeItem( void ); + virtual void previewItem( void ); + virtual void selectItem(void); + virtual void showProperties(void); + + void setVisibleIfDetached(BOOL visible); + + // This method will be called to determine if a drop can be + // performed, and will set drop to TRUE if a drop is + // requested. + // Returns TRUE if a drop is possible/happened, FALSE otherwise. + virtual BOOL dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) { return FALSE; } + + bool hasSameValues(std::string name, const LLUUID& uuid) { return ((name == mName) && (uuid == mUUID)); } + bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } +private: + std::string mName; + const LLUUID mUUID; + LLFloater* mFloater; + LLIMFloaterContainer* mContainer; +}; + +// We don't want to ever filter conversations but we need to declare that class to create a conversation view model. +// We just stubb everything for the moment. +class LLConversationFilter : public LLFolderViewFilter +{ +public: + + enum ESortOrderType + { + SO_NAME = 0, // Sort inventory by name + SO_DATE = 0x1, // Sort inventory by date + }; + + LLConversationFilter() { mEmpty = ""; } + ~LLConversationFilter() {} + + bool check(const LLFolderViewModelItem* item) { return true; } + bool checkFolder(const LLFolderViewModelItem* folder) const { return true; } + void setEmptyLookupMessage(const std::string& message) { } + std::string getEmptyLookupMessage() const { return mEmpty; } + bool showAllResults() const { return true; } + + bool isActive() const { return false; } + bool isModified() const { return false; } + void clearModified() { } + const std::string& getName() const { return mEmpty; } + const std::string& getFilterText() { return mEmpty; } + void setModified(EFilterModified behavior = FILTER_RESTART) { } + + void setFilterCount(S32 count) { } + S32 getFilterCount() const { return 0; } + void decrementFilterCount() { } + + bool isDefault() const { return true; } + bool isNotDefault() const { return false; } + void markDefault() { } + void resetDefault() { } + + S32 getCurrentGeneration() const { return 0; } + S32 getFirstSuccessGeneration() const { return 0; } + S32 getFirstRequiredGeneration() const { return 0; } +private: + std::string mEmpty; +}; + +class LLConversationSort +{ +public: + LLConversationSort(U32 order = 0) + : mSortOrder(order), + mByDate(false), + mByName(false) + { + mByDate = (order & LLConversationFilter::SO_DATE); + mByName = (order & LLConversationFilter::SO_NAME); + } + + bool isByDate() const { return mByDate; } + U32 getSortOrder() const { return mSortOrder; } + + bool operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const; +private: + U32 mSortOrder; + bool mByDate; + bool mByName; +}; + +class LLConversationViewModel +: public LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> +{ +public: + typedef LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> base_t; + + void sort(LLFolderViewFolder* folder) { } // *TODO : implement conversation sort + bool contentsReady() { return true; } // *TODO : we need to check that participants names are available somewhat + bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items + +private: +}; + +// CHUI-137 : End + +class LLIMFloaterContainer + : public LLMultiFloater + , public LLIMSessionObserver { public: LLIMFloaterContainer(const LLSD& seed); @@ -45,12 +213,16 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); void onCloseFloater(LLUUID& id); /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + /*virtual*/ void tabClose(); + static LLFloater* getCurrentVoiceFloater(); static LLIMFloaterContainer* findInstance(); @@ -59,12 +231,53 @@ public: virtual void setMinimized(BOOL b); + void collapseMessagesPane(bool collapse); + + + // LLIMSessionObserver observe triggers + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {}; + private: typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t; avatarID_panel_map_t mSessions; boost::signals2::connection mNewMessageConnection; + /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height); + void onNewMessageReceived(const LLSD& data); + + void onExpandCollapseButtonClicked(); + + void collapseConversationsPane(bool collapse); + + void updateState(bool collapse, S32 delta_width); + void repositioningWidgets(); + + void onAddButtonClicked(); + void onAvatarPicked(const uuid_vec_t& ids); + + LLButton* mExpandCollapseBtn; + LLLayoutPanel* mMessagesPane; + LLLayoutPanel* mConversationsPane; + LLLayoutStack* mConversationsStack; + + // Conversation list implementation +public: + void removeConversationListItem(LLFloater* floaterp, bool change_focus = true); + void addConversationListItem(std::string name, const LLUUID& uuid, LLFloater* floaterp); + LLFloater* findConversationItem(LLUUID& uuid); +private: + LLFolderViewItem* createConversationItemWidget(LLConversationItem* item); + + // Conversation list data + LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to + conversations_items_map mConversationsItems; + conversations_widgets_map mConversationsWidgets; + LLConversationViewModel mConversationViewModel; + LLFolderView* mConversationsRoot; }; #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index 07d73c8c66..047472a282 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -37,10 +37,9 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) +LLIMHandler::LLIMHandler() +: LLSysHandler("IM Notifications", "notifytoast") { - mType = type; - // Getting a Channel for our notifications mChannel = LLChannelManager::getInstance()->createNotificationChannel()->getHandle(); } @@ -59,72 +58,49 @@ void LLIMHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLIMHandler::processNotification(const LLSD& notify) +bool LLIMHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLSD substitutions = notification->getSubstitutions(); - - // According to comments in LLIMMgr::addMessage(), if we get message - // from ourselves, the sender id is set to null. This fixes EXT-875. - LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); - if (avatar_id.isNull()) - avatar_id = gAgentID; - - LLToastIMPanel::Params im_p; - im_p.notification = notification; - im_p.avatar_id = avatar_id; - im_p.from = substitutions["FROM"].asString(); - im_p.time = substitutions["TIME"].asString(); - im_p.message = substitutions["MESSAGE"].asString(); - im_p.session_id = substitutions["SESSION_ID"].asUUID(); - - LLToastIMPanel* im_box = new LLToastIMPanel(im_p); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.session_id = im_p.session_id; - p.notification = notification; - p.panel = im_box; - p.can_be_stored = false; - p.on_delete_toast = boost::bind(&LLIMHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // send a signal to the counter manager; - mNewNotificationSignal(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } - return false; -} + LLSD substitutions = notification->getSubstitutions(); + + // According to comments in LLIMMgr::addMessage(), if we get message + // from ourselves, the sender id is set to null. This fixes EXT-875. + LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); + if (avatar_id.isNull()) + avatar_id = gAgentID; + + LLToastIMPanel::Params im_p; + im_p.notification = notification; + im_p.avatar_id = avatar_id; + im_p.from = substitutions["FROM"].asString(); + im_p.time = substitutions["TIME"].asString(); + im_p.message = substitutions["MESSAGE"].asString(); + im_p.session_id = substitutions["SESSION_ID"].asUUID(); + + LLToastIMPanel* im_box = new LLToastIMPanel(im_p); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.session_id = im_p.session_id; + p.notification = notification; + p.panel = im_box; + p.can_be_stored = false; + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); -//-------------------------------------------------------------------------- -void LLIMHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); + return false; } -//-------------------------------------------------------------------------- diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4000570872..ae08b7f8b9 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,7 +41,7 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" - +#include "llimconversation.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" @@ -716,6 +716,16 @@ bool LLIMModel::clearSession(const LLUUID& session_id) return true; } +void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index, const bool sendNoUnreadMsgs) +{ + getMessagesSilently(session_id, messages, start_index); + + if (sendNoUnreadMsgs) + { + sendNoUnreadMessages(session_id); + } +} + void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) { LLIMSession* session = findIMSession(session_id); @@ -757,13 +767,6 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) -{ - getMessagesSilently(session_id, messages, start_index); - - sendNoUnreadMessages(session_id); -} - bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { LLIMSession* session = findIMSession(session_id); @@ -904,7 +907,7 @@ const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const LLIMSession* session = findIMSession(session_id); if (!session) { - llwarns << "session " << session_id << "does not exist " << llendl; + llwarns << "session " << session_id << " does not exist " << llendl; return LLUUID::null; } @@ -2480,8 +2483,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); if(nearby_chat) { @@ -2497,6 +2499,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); } // log message to file + else { std::string session_name; @@ -2579,7 +2582,8 @@ LLUUID LLIMMgr::addSession( { LLDynamicArray<LLUUID> ids; ids.put(other_participant_id); - return addSession(name, dialog, other_participant_id, ids, voice); + LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voice); + return session_id; } // Adds a session using the given session_id. If the session already exists @@ -2588,7 +2592,8 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice) + const LLDynamicArray<LLUUID>& ids, bool voice, + const LLUUID& floater_id) { if (0 == ids.getLength()) { @@ -2603,6 +2608,19 @@ LLUUID LLIMMgr::addSession( LLUUID session_id = computeSessionID(dialog,other_participant_id); + if (floater_id.notNull()) + { + LLIMFloater* im_floater = LLIMFloater::findInstance(floater_id); + + if (im_floater && im_floater->getStartConferenceInSameFloater()) + { + // The IM floater should be initialized with a new session_id + // so that it is found by that id when creating a chiclet in LLIMFloater::onIMChicletCreated, + // and a new floater is not created. + im_floater->initIMSession(session_id); + } + } + bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); //works only for outgoing ad-hoc sessions @@ -2634,6 +2652,8 @@ LLUUID LLIMMgr::addSession( noteMutedUsers(session_id, ids); } + notifyObserverSessionVoiceOrIMStarted(session_id); + return session_id; } @@ -2920,6 +2940,14 @@ void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::st } } +void LLIMMgr::notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionVoiceOrIMStarted(session_id); + } +} + void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id) { for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 7c2cd03d97..80bf315aa8 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -192,12 +192,6 @@ public: bool clearSession(const LLUUID& session_id); /** - * Populate supplied std::list with messages starting from index specified by start_index without - * emitting no unread messages signal. - */ - void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); - - /** * Sends no unread messages signal. */ void sendNoUnreadMessages(const LLUUID& session_id); @@ -205,7 +199,7 @@ public: /** * Populate supplied std::list with messages starting from index specified by start_index */ - void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0, const bool sendNoUnreadMsgs = true); /** * Add a message to an IM Model - the message is saved in a message store associated with a session specified by session_id @@ -288,6 +282,12 @@ public: private: /** + * Populate supplied std::list with messages starting from index specified by start_index without + * emitting no unread messages signal. + */ + void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + + /** * Add message to a list of message associated with session specified by session_id */ bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); @@ -298,6 +298,7 @@ class LLIMSessionObserver public: virtual ~LLIMSessionObserver() {} virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) = 0; virtual void sessionRemoved(const LLUUID& session_id) = 0; virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) = 0; }; @@ -347,10 +348,12 @@ public: // Adds a session using a specific group of starting agents // the dialog type is assumed correct. Returns the uuid of the session. + // A session can be added to a floater specified by floater_id. LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice = false); + const LLDynamicArray<LLUUID>& ids, bool voice = false, + const LLUUID& floater_id = LLUUID::null); /** * Creates a P2P session with the requisite handle for responding to voice calls. @@ -460,6 +463,7 @@ private: static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group); void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + void notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id); void notifyObserverSessionRemoved(const LLUUID& session_id); void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 17d0b0ffbb..8a15cd279f 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -28,38 +28,21 @@ #include "llinspectavatar.h" // viewer files -#include "llagent.h" -#include "llagentdata.h" -#include "llavataractions.h" +#include "llavatariconctrl.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" -#include "llcallingcard.h" #include "lldateutil.h" -#include "llfloaterreporter.h" -#include "llfloaterworldmap.h" -#include "llimview.h" #include "llinspect.h" -#include "llmutelist.h" -#include "llpanelblockedlist.h" +#include "llslurl.h" #include "llstartup.h" -#include "llspeakers.h" -#include "llviewermenu.h" -#include "llvoiceclient.h" -#include "llviewerobjectlist.h" #include "lltransientfloatermgr.h" -#include "llnotificationsutil.h" // Linden libraries #include "llfloater.h" #include "llfloaterreg.h" -#include "llmenubutton.h" #include "lltextbox.h" -#include "lltoggleablemenu.h" #include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" -#include "lluictrl.h" - -#include "llavatariconctrl.h" class LLFetchAvatarData; @@ -81,22 +64,13 @@ public: LLInspectAvatar(const LLSD& avatar_id); virtual ~LLInspectAvatar(); - /*virtual*/ BOOL postBuild(void); - // Because floater is single instance, need to re-parse data on each spawn // (for example, inspector about same avatar but in different position) /*virtual*/ void onOpen(const LLSD& avatar_id); - // When closing they should close their gear menu - /*virtual*/ void onClose(bool app_quitting); - // Update view based on information from avatar properties processor void processAvatarData(LLAvatarData* data); - // override the inspector mouse leave so timer is only paused if - // gear menu is not open - /* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } private: @@ -104,47 +78,6 @@ private: // Used on construction and if avatar id changes. void requestUpdate(); - // Set the volume slider to this user's current client-side volume setting, - // hiding/disabling if the user is not nearby. - void updateVolumeSlider(); - - // Shows/hides moderator panel depending on voice state - void updateModeratorPanel(); - - // Moderator ability to enable/disable voice chat for avatar - void toggleSelectedVoice(bool enabled); - - // Button callbacks - void onClickAddFriend(); - void onClickViewProfile(); - void onClickIM(); - void onClickCall(); - void onClickTeleport(); - void onClickInviteToGroup(); - void onClickPay(); - void onClickShare(); - void onToggleMute(); - void onClickReport(); - void onClickFreeze(); - void onClickEject(); - void onClickKick(); - void onClickCSR(); - void onClickZoomIn(); - void onClickFindOnMap(); - bool onVisibleFindOnMap(); - bool onVisibleEject(); - bool onVisibleFreeze(); - bool onVisibleZoomIn(); - void onClickMuteVolume(); - void onVolumeChange(const LLSD& data); - bool enableMute(); - bool enableUnmute(); - bool enableTeleportOffer(); - bool godModeEnabled(); - - // Is used to determine if "Add friend" option should be enabled in gear menu - bool isNotFriend(); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); @@ -209,39 +142,8 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mAvatarName(), mPropertiesRequest(NULL) { - mCommitCallbackRegistrar.add("InspectAvatar.ViewProfile", boost::bind(&LLInspectAvatar::onClickViewProfile, this)); - mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this)); - mCommitCallbackRegistrar.add("InspectAvatar.IM", - boost::bind(&LLInspectAvatar::onClickIM, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Call", boost::bind(&LLInspectAvatar::onClickCall, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Share", boost::bind(&LLInspectAvatar::onClickShare, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ToggleMute", boost::bind(&LLInspectAvatar::onToggleMute, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Freeze", boost::bind(&LLInspectAvatar::onClickFreeze, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Eject", boost::bind(&LLInspectAvatar::onClickEject, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Kick", boost::bind(&LLInspectAvatar::onClickKick, this)); - mCommitCallbackRegistrar.add("InspectAvatar.CSR", boost::bind(&LLInspectAvatar::onClickCSR, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onClickFindOnMap, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ZoomIn", boost::bind(&LLInspectAvatar::onClickZoomIn, this)); - mCommitCallbackRegistrar.add("InspectAvatar.DisableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, false)); - mCommitCallbackRegistrar.add("InspectAvatar.EnableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, true)); - - mEnableCallbackRegistrar.add("InspectAvatar.EnableGod", boost::bind(&LLInspectAvatar::godModeEnabled, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleEject", boost::bind(&LLInspectAvatar::onVisibleEject, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreeze", boost::bind(&LLInspectAvatar::onVisibleFreeze, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.Enable", boost::bind(&LLInspectAvatar::isNotFriend, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableCall", boost::bind(&LLAvatarActions::canCall)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableTeleportOffer", boost::bind(&LLInspectAvatar::enableTeleportOffer, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableMute", boost::bind(&LLInspectAvatar::enableMute, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableUnmute", boost::bind(&LLInspectAvatar::enableUnmute, this)); - // can't make the properties request until the widgets are constructed - // as it might return immediately, so do it in postBuild. + // as it might return immediately, so do it in onOpen. LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); LLTransientFloater::init(this); @@ -257,25 +159,6 @@ LLInspectAvatar::~LLInspectAvatar() LLTransientFloaterMgr::getInstance()->removeControlView(this); } -/*virtual*/ -BOOL LLInspectAvatar::postBuild(void) -{ - getChild<LLUICtrl>("add_friend_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickAddFriend, this) ); - - getChild<LLUICtrl>("view_profile_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickViewProfile, this) ); - - getChild<LLUICtrl>("mute_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickMuteVolume, this) ); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback( - boost::bind(&LLInspectAvatar::onVolumeChange, this, _2)); - - return TRUE; -} - - // Multiple calls to showInstance("inspect_avatar", foo) will provide different // LLSD for foo, which we will catch here. //virtual @@ -287,11 +170,6 @@ void LLInspectAvatar::onOpen(const LLSD& data) // Extract appropriate avatar id mAvatarID = data["avatar_id"]; - BOOL self = mAvatarID == gAgent.getID(); - - getChild<LLUICtrl>("gear_self_btn")->setVisible(self); - getChild<LLUICtrl>("gear_btn")->setVisible(!self); - // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned // See LLToolTipMgr::createToolTip @@ -304,20 +182,13 @@ void LLInspectAvatar::onOpen(const LLSD& data) LLUI::positionViewNearMouse(this); } + // Generate link to avatar profile. + getChild<LLUICtrl>("avatar_profile_link")->setTextArg("[LINK]", LLSLURL("agent", mAvatarID, "about").getSLURLString()); + // can't call from constructor as widgets are not built yet requestUpdate(); - - updateVolumeSlider(); - - updateModeratorPanel(); } -// virtual -void LLInspectAvatar::onClose(bool app_quitting) -{ - getChild<LLMenuButton>("gear_btn")->hideMenu(); -} - void LLInspectAvatar::requestUpdate() { // Don't make network requests when spawning from the debug menu at the @@ -344,25 +215,6 @@ void LLInspectAvatar::requestUpdate() delete mPropertiesRequest; mPropertiesRequest = new LLFetchAvatarData(mAvatarID, this); - // You can't re-add someone as a friend if they are already your friend - bool is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL; - bool is_self = (mAvatarID == gAgentID); - if (is_self) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - else if (is_friend) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(true); - } - else - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(true); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - // Use an avatar_icon even though the image id will come down with the // avatar properties because the avatar_icon code maintains a cache of icons // and this may result in the image being visible sooner. @@ -405,214 +257,6 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) mPropertiesRequest = NULL; } -// For the avatar inspector, we only want to unpause the fade timer -// if neither the gear menu or self gear menu are open -void LLInspectAvatar::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); - LLToggleableMenu* gear_menu_self = getChild<LLMenuButton>("gear_self_btn")->getMenu(); - if ( gear_menu && gear_menu->getVisible() && - gear_menu_self && gear_menu_self->getVisible() ) - { - return; - } - - if(childHasVisiblePopupMenu()) - { - return; - } - - mOpenTimer.unpause(); -} - -void LLInspectAvatar::updateModeratorPanel() -{ - bool enable_moderator_panel = false; - - if (LLVoiceChannel::getCurrentVoiceChannel() && - mAvatarID != gAgent.getID()) - { - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - - if (session_id != LLUUID::null) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - LLPointer<LLSpeaker> self_speakerp = speaker_mgr->findSpeaker(gAgent.getID()); - LLPointer<LLSpeaker> selected_speakerp = speaker_mgr->findSpeaker(mAvatarID); - - if(speaker_mgr->isVoiceActive() && selected_speakerp && - selected_speakerp->isInVoiceChannel() && - ((self_speakerp && self_speakerp->mIsModerator) || gAgent.isGodlike())) - { - getChild<LLUICtrl>("enable_voice")->setVisible(selected_speakerp->mModeratorMutedVoice); - getChild<LLUICtrl>("disable_voice")->setVisible(!selected_speakerp->mModeratorMutedVoice); - - enable_moderator_panel = true; - } - } - } - } - - if (enable_moderator_panel) - { - if (!getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(true); - // stretch the floater so it can accommodate the moderator panel - reshape(getRect().getWidth(), getRect().getHeight() + getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } - } - else if (getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(false); - // shrink the inspector floater back to original size - reshape(getRect().getWidth(), getRect().getHeight() - getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } -} - -void LLInspectAvatar::toggleSelectedVoice(bool enabled) -{ - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - if (!gAgent.getRegion()) - return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = session_id; - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = mAvatarID; - data["params"]["mute_info"] = LLSD::emptyMap(); - // ctrl value represents ability to type, so invert - data["params"]["mute_info"]["voice"] = !enabled; - - class MuteVoiceResponder : public LLHTTPClient::Responder - { - public: - MuteVoiceResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - virtual void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_moderator", - mSessionID); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic", - mSessionID); - } - } - } - - private: - LLUUID mSessionID; - }; - - LLHTTPClient::post( - url, - data, - new MuteVoiceResponder(speaker_mgr->getSessionID())); - } - - closeFloater(); - -} - -void LLInspectAvatar::updateVolumeSlider() -{ - bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); - - // Do not display volume slider and mute button if it - // is ourself or we are not in a voice channel together - if (!voice_enabled || (mAvatarID == gAgent.getID())) - { - getChild<LLUICtrl>("mute_btn")->setVisible(false); - getChild<LLUICtrl>("volume_slider")->setVisible(false); - } - - else - { - getChild<LLUICtrl>("mute_btn")->setVisible(true); - getChild<LLUICtrl>("volume_slider")->setVisible(true); - - // By convention, we only display and toggle voice mutes, not all mutes - bool is_muted = LLMuteList::getInstance()-> - isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - - mute_btn->setEnabled( !is_linden); - mute_btn->setValue( is_muted ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted ); - - F32 volume; - - if (is_muted) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - -} - -void LLInspectAvatar::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } - - updateVolumeSlider(); -} - -void LLInspectAvatar::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - void LLInspectAvatar::onAvatarNameCache( const LLUUID& agent_id, const LLAvatarName& av_name) @@ -640,215 +284,6 @@ void LLInspectAvatar::onAvatarNameCache( } } -void LLInspectAvatar::onClickAddFriend() -{ - LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName.getLegacyName()); - closeFloater(); -} - -void LLInspectAvatar::onClickViewProfile() -{ - LLAvatarActions::showProfile(mAvatarID); - closeFloater(); -} - -bool LLInspectAvatar::isNotFriend() -{ - return !LLAvatarActions::isFriend(mAvatarID); -} - -bool LLInspectAvatar::onVisibleFindOnMap() -{ - return gAgent.isGodlike() || is_agent_mappable(mAvatarID); -} - -bool LLInspectAvatar::onVisibleEject() -{ - return enable_freeze_eject( LLSD(mAvatarID) ); -} - -bool LLInspectAvatar::onVisibleFreeze() -{ - // either user is a god and can do long distance freeze - // or check for target proximity and permissions - return gAgent.isGodlike() || enable_freeze_eject(LLSD(mAvatarID)); -} - -bool LLInspectAvatar::onVisibleZoomIn() -{ - return gObjectList.findObject(mAvatarID); -} - -void LLInspectAvatar::onClickIM() -{ - LLAvatarActions::startIM(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCall() -{ - LLAvatarActions::startCall(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickTeleport() -{ - LLAvatarActions::offerTeleport(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickInviteToGroup() -{ - LLAvatarActions::inviteToGroup(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickPay() -{ - LLAvatarActions::pay(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickShare() -{ - LLAvatarActions::share(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onToggleMute() -{ - LLMute mute(mAvatarID, mAvatarName.mDisplayName, LLMute::AGENT); - - if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) - { - LLMuteList::getInstance()->remove(mute); - } - else - { - LLMuteList::getInstance()->add(mute); - } - - LLPanelBlockedList::showPanelAndSelect(mute.mID); - closeFloater(); -} - -void LLInspectAvatar::onClickReport() -{ - LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName.getCompleteName()); - closeFloater(); -} - -bool godlike_freeze(const LLSD& notification, const LLSD& response) -{ - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - switch (option) - { - case 0: - LLAvatarActions::freeze(avatar_id); - break; - case 1: - LLAvatarActions::unfreeze(avatar_id); - break; - default: - break; - } - - return false; -} - -void LLInspectAvatar::onClickFreeze() -{ - if (gAgent.isGodlike()) - { - // use godlike freeze-at-a-distance, with confirmation - LLNotificationsUtil::add("FreezeAvatar", - LLSD(), - LLSD().with("avatar_id", mAvatarID), - godlike_freeze); - } - else - { - // use default "local" version of freezing that requires avatar to be in range - handle_avatar_freeze( LLSD(mAvatarID) ); - } - closeFloater(); -} - -void LLInspectAvatar::onClickEject() -{ - handle_avatar_eject( LLSD(mAvatarID) ); - closeFloater(); -} - -void LLInspectAvatar::onClickKick() -{ - LLAvatarActions::kick(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCSR() -{ - std::string name; - gCacheName->getFullName(mAvatarID, name); - LLAvatarActions::csr(mAvatarID, name); - closeFloater(); -} - -void LLInspectAvatar::onClickZoomIn() -{ - handle_zoom_to_object(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickFindOnMap() -{ - gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName.mDisplayName); - LLFloaterReg::showInstance("world_map"); -} - - -bool LLInspectAvatar::enableMute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && !LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableUnmute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableTeleportOffer() -{ - return LLAvatarActions::canOfferTeleport(mAvatarID); -} - -bool LLInspectAvatar::godModeEnabled() -{ - return gAgent.isGodlike(); -} - ////////////////////////////////////////////////////////////////////////////// // LLInspectAvatarUtil ////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b86c453d61..9f1d4bdec9 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -37,6 +37,7 @@ #include "llappearancemgr.h" #include "llattachmentsmgr.h" #include "llavataractions.h" +#include "llfavoritesbar.h" // management of favorites folder #include "llfloateropenobject.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" @@ -115,10 +116,10 @@ void teleport_via_landmark(const LLUUID& asset_id); static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); // Helper functions @@ -189,7 +190,8 @@ LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, mUUID(uuid), mRoot(root), mInvType(LLInventoryType::IT_NONE), - mIsLink(FALSE) + mIsLink(FALSE), + LLFolderViewModelItemInventory(inventory->getRootViewModel()) { mInventoryPanel = inventory->getInventoryPanelHandle(); const LLInventoryObject* obj = getInventoryObject(); @@ -208,7 +210,11 @@ const std::string& LLInvFVBridge::getName() const const std::string& LLInvFVBridge::getDisplayName() const { - return getName(); + if(mDisplayName.empty()) + { + buildDisplayName(); + } + return mDisplayName; } // Folders have full perms @@ -227,9 +233,24 @@ LLFolderType::EType LLInvFVBridge::getPreferredType() const // Folders don't have creation dates. time_t LLInvFVBridge::getCreationDate() const { - return 0; + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + return objectp->getCreationDate(); + } + return (time_t)0; +} + +void LLInvFVBridge::setCreationDate(time_t creation_date_utc) +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + objectp->setCreationDate(creation_date_utc); + } } + // Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() const { @@ -283,7 +304,7 @@ void LLInvFVBridge::showProperties() */ } -void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { // Deactivate gestures when moving them into Trash LLInvFVBridge* bridge; @@ -292,11 +313,11 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc LLViewerInventoryCategory* cat = NULL; LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; - S32 count = batch.count(); + S32 count = batch.size(); S32 i,j; for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if (item) @@ -309,7 +330,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc } for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if (cat) @@ -327,7 +348,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc removeBatchNoCheck(batch); } -void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch) { // this method moves a bunch of items and folders to the trash. As // per design guidelines for the inventory model, the message is @@ -343,14 +364,14 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* uuid_vec_t move_ids; LLInventoryModel::update_map_t update; bool start_new_message = true; - S32 count = batch.count(); + S32 count = batch.size(); S32 i; // first, hide any 'preview' floaters that correspond to the items // being deleted. for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -363,7 +384,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -404,7 +425,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -876,6 +897,12 @@ LLInventoryModel* LLInvFVBridge::getInventoryModel() const return panel ? panel->getModel() : NULL; } +LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + return panel ? panel->getFilter() : NULL; +} + BOOL LLInvFVBridge::isItemInTrash() const { LLInventoryModel* model = getInventoryModel(); @@ -1000,6 +1027,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags) @@ -1125,7 +1153,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, default: llinfos << "Unhandled asset type (llassetstorage.h): " << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << llendl; - break; + break; } if (new_listener) @@ -1250,10 +1278,10 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const if (can_list) { - LLFolderViewFolder * object_folderp = mRoot->getFolderByID(object_id); + LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL; if (object_folderp) { - can_list = !object_folderp->isLoading(); + can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading(); } } @@ -1261,7 +1289,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const { // Get outbox id const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(outbox_id); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get() ? mInventoryPanel.get()->getItemByID(outbox_id) : NULL; if (outbox_itemp) { @@ -1271,7 +1299,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const void * cargo_data = (void *) obj; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } } @@ -1283,6 +1311,21 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const #endif } +LLToolDragAndDrop::ESource LLInvFVBridge::getDragSource() const +{ + if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_AGENT; + } + else if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_LIBRARY; + } + + return LLToolDragAndDrop::SOURCE_VIEWER; +} + + // +=================================================+ // | InventoryFVBridgeBuilder | @@ -1291,6 +1334,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /* = 0x00 */) const @@ -1299,6 +1343,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -1355,7 +1400,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -1368,10 +1413,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteFromClipboard(); return; } else if ("paste_link" == action) @@ -1380,10 +1425,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteLinkFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard(); return; } else if (isMarketplaceCopyAction(action)) @@ -1494,25 +1539,20 @@ PermissionMask LLItemBridge::getPermissionMask() const return perm_mask; } -const std::string& LLItemBridge::getDisplayName() const -{ - if(mDisplayName.empty()) - { - buildDisplayName(getItem(), mDisplayName); - } - return mDisplayName; -} - -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) +void LLItemBridge::buildDisplayName() const { - if(item) + if(getItem()) { - name.assign(item->getName()); + mDisplayName.assign(getItem()->getName()); } else { - name.assign(LLStringUtil::null); + mDisplayName.assign(LLStringUtil::null); } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); } LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const @@ -1629,25 +1669,23 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); model->notifyObservers(); + buildDisplayName(); } // return FALSE because we either notified observers (& therefore // rebuilt) or we didn't update. return FALSE; } - BOOL LLItemBridge::removeItem() { if(!isItemRemovable()) { return FALSE; } - // move it to the trash LLPreview::hide(mUUID, TRUE); @@ -1786,6 +1824,93 @@ void LLFolderBridge::selectItem() LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); } +void LLFolderBridge::buildDisplayName() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + // *TODO: to be removed when database supports multi language. This is a + // temporary attempt to display the inventory folder in the user locale. + // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID + // it uses the same way to find localized string + + // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(getName() == "Accessories") + { + //To ensure that Accessories folder is in Library we have to check its parent folder. + //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model + LLInventoryCategory* cat = gInventory.getCategory(getUUID()); + if(cat) + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + mDisplayName.assign(getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(mDisplayName, std::string("InvFolder ") + getName(), LLSD()); + } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); +} + + +void LLFolderBridge::update() +{ + bool possibly_has_children = false; + bool up_to_date = isUpToDate(); + if(!up_to_date && hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) + { + possibly_has_children = true; + } + + bool loading = (possibly_has_children + && !up_to_date ); + + if (loading != mIsLoading) + { + if ( loading && !mIsLoading ) + { + // Measure how long we've been in the loading state + mTimeSinceRequestStart.reset(); + } + + const BOOL in_inventory = gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID()); + const BOOL in_library = gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID()); + + bool root_is_loading = false; + if (in_inventory) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); + } + if (in_library) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); + } + if ((mIsLoading + && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) + || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() + && root_is_loading)) + { + mDisplayName = LLInvFVBridge::getDisplayName() + " ( " + LLTrans::getString("LoadingData") + " ) "; + mIsLoading = true; + } + else + { + mDisplayName = LLInvFVBridge::getDisplayName(); + mIsLoading = false; + } + } +} + + // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -1794,11 +1919,11 @@ public: LLIsItemRemovable() : mPassed(TRUE) {} virtual void doFolder(LLFolderViewFolder* folder) { - mPassed &= folder->getListener()->isItemRemovable(); + mPassed &= folder->getViewModelItem()->isItemRemovable(); } virtual void doItem(LLFolderViewItem* item) { - mPassed &= item->getListener()->isItemRemovable(); + mPassed &= item->getViewModelItem()->isItemRemovable(); } BOOL mPassed; }; @@ -1812,7 +1937,7 @@ BOOL LLFolderBridge::isItemRemovable() const } LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); + LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getItemByID(mUUID) : NULL); if (folderp) { LLIsItemRemovable folder_test; @@ -2051,7 +2176,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID &cat_id = inv_cat->getUUID(); @@ -2270,7 +2395,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { // Check whether the folder being dragged from active inventory panel // passes the filter of the destination panel. - is_movable = check_category(model, cat_id, active_folder_view, filter); + is_movable = check_category(model, cat_id, active_panel, filter); } } } @@ -2696,7 +2821,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if ("open" == action) { - LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mRoot->getItemByID(mUUID)); + LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mInventoryPanel.get()->getItemByID(mUUID)); if (f) { f->setOpen(TRUE); @@ -2743,7 +2868,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -2880,17 +3005,24 @@ LLUIImagePtr LLFolderBridge::getIcon() const LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); - /*case LLAssetType::AT_MESH: - control = "inv_folder_mesh.tga"; - break;*/ } -LLUIImagePtr LLFolderBridge::getOpenIcon() const +LLUIImagePtr LLFolderBridge::getIconOpen() const { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE)); } +LLUIImagePtr LLFolderBridge::getIconOverlay() const +{ + if (getInventoryObject() && getInventoryObject()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + + BOOL LLFolderBridge::renameItem(const std::string& new_name) { rename_category(getInventoryModel(), mUUID, new_name); @@ -2971,7 +3103,7 @@ void LLFolderBridge::pasteFromClipboard() if (move_is_into_outbox) { - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get()->getItemByID(mUUID); if (outbox_itemp) { @@ -2994,7 +3126,7 @@ void LLFolderBridge::pasteFromClipboard() void * cargo_data = (void *) item; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } @@ -3166,7 +3298,7 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.count() > 0) ? TRUE : FALSE ); } -void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) +void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); @@ -3177,30 +3309,30 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) if (lost_and_found_id == mUUID) { // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); + items.push_back(std::string("Empty Lost And Found")); - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("New Script")); - mDisabledItems.push_back(std::string("New Note")); - mDisabledItems.push_back(std::string("New Gesture")); - mDisabledItems.push_back(std::string("New Clothes")); - mDisabledItems.push_back(std::string("New Body Parts")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); } if(trash_id == mUUID) { // This is the trash. - mItems.push_back(std::string("Empty Trash")); + items.push_back(std::string("Empty Trash")); } else if(isItemInTrash()) { // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - addTrashContextMenuOptions(mItems, mDisabledItems); + items.clear(); // clear any items that used to exist + addTrashContextMenuOptions(items, disabled_items); } else if(isOutboxFolder()) { - addOutboxContextMenuOptions(flags, mItems, mDisabledItems); + addOutboxContextMenuOptions(flags, items, disabled_items); } else if(isAgentInventory()) // do not allow creating in library { @@ -3214,40 +3346,40 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) { - mItems.push_back(std::string("New Folder")); + items.push_back(std::string("New Folder")); } - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); } #if SUPPORT_ENSEMBLES // Changing folder types is an unfinished unsupported feature // and can lead to unexpected behavior if enabled. - mItems.push_back(std::string("Change Type")); + items.push_back(std::string("Change Type")); const LLViewerInventoryCategory *cat = getCategory(); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { - mDisabledItems.push_back(std::string("Change Type")); + disabled_items.push_back(std::string("Change Type")); } #endif - getClipboardEntries(false, mItems, mDisabledItems, flags); + getClipboardEntries(false, items, disabled_items, flags); } else { // Want some but not all of the items from getClipboardEntries for outfits. if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { - mItems.push_back(std::string("Rename")); + items.push_back(std::string("Rename")); - addDeleteContextMenuOptions(mItems, mDisabledItems); + addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } } } @@ -3276,20 +3408,44 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Preemptively disable system folder removal if more than one item selected. if ((flags & FIRST_SELECTED_ITEM) == 0) { - mDisabledItems.push_back(std::string("Delete System Folder")); + disabled_items.push_back(std::string("Delete System Folder")); } if (!isOutboxFolder()) { - mItems.push_back(std::string("Share")); + items.push_back(std::string("Share")); if (!canShare()) { - mDisabledItems.push_back(std::string("Share")); + disabled_items.push_back(std::string("Share")); + } + } + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category) + { + uuid_vec_t folders; + folders.push_back(category->getUUID()); + + sSelf = getHandle(); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); + fetch->startFetch(); + if (fetch->isFinished()) + { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; + buildContextMenuFolderOptions(flags, items, disabled_items); + } + else + { + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(fetch); } } } -void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { // Build folder specific options back up LLInventoryModel* model = getInventoryModel(); @@ -3316,21 +3472,21 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); + items.push_back(std::string("Calling Card Separator")); + items.push_back(std::string("Conference Chat Folder")); + items.push_back(std::string("IM All Contacts In Folder")); } } if (!isItemRemovable()) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type)) { - mItems.push_back(std::string("Delete System Folder")); + items.push_back(std::string("Delete System Folder")); } #endif @@ -3345,7 +3501,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) checkFolderForContentsOfType(model, is_object) || checkFolderForContentsOfType(model, is_gesture) ) { - mItems.push_back(std::string("Folder Wearables Separator")); + items.push_back(std::string("Folder Wearables Separator")); // Only enable add/replace outfit for non-system folders. if (!is_system_folder) @@ -3353,25 +3509,25 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) // Adding an outfit onto another (versus replacing) doesn't make sense. if (type != LLFolderType::FT_OUTFIT) { - mItems.push_back(std::string("Add To Outfit")); + items.push_back(std::string("Add To Outfit")); } - mItems.push_back(std::string("Replace Outfit")); + items.push_back(std::string("Replace Outfit")); } if (is_ensemble) { - mItems.push_back(std::string("Wear As Ensemble")); + items.push_back(std::string("Wear As Ensemble")); } - mItems.push_back(std::string("Remove From Outfit")); + items.push_back(std::string("Remove From Outfit")); if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) { - mDisabledItems.push_back(std::string("Remove From Outfit")); + disabled_items.push_back(std::string("Remove From Outfit")); } if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) { - mDisabledItems.push_back(std::string("Replace Outfit")); + disabled_items.push_back(std::string("Replace Outfit")); } - mItems.push_back(std::string("Outfit Separator")); + items.push_back(std::string("Outfit Separator")); } } @@ -3380,49 +3536,28 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { sSelf.markDead(); - mItems.clear(); - mDisabledItems.clear(); + // fetch contents of this folder, as context menu can depend on contents + // still, user would have to open context menu again to see the changes + gInventory.fetchDescendentsOf(getUUID()); + + + menuentry_vec_t items; + menuentry_vec_t disabled_items; lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; LLInventoryModel* model = getInventoryModel(); if(!model) return; - buildContextMenuBaseOptions(flags); - - // Add menu items that are dependent on the contents of the folder. - LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); - if (category) - { - uuid_vec_t folders; - folders.push_back(category->getUUID()); - - sSelf = getHandle(); - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); - fetch->startFetch(); - if (fetch->isFinished()) - { - // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down - // This saves lots of time as buildContextMenu() is called a lot - delete fetch; - buildContextMenuFolderOptions(flags); - } - else - { - // it's all on its way - add an observer, and the inventory will call done for us when everything is here. - inc_busy_count(); - gInventory.addObserver(fetch); - } - } - - hide_context_entries(menu, mItems, mDisabledItems); + buildContextMenuOptions(flags, 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(); menu.arrangeAndClear(); } -BOOL LLFolderBridge::hasChildren() const +bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; @@ -3512,25 +3647,6 @@ void LLFolderBridge::pasteClipboard(void* user_data) if(self) self->pasteFromClipboard(); } -void LLFolderBridge::createNewCategory(void* user_data) -{ - LLFolderBridge* bridge = (LLFolderBridge*)user_data; - if(!bridge) return; - LLInventoryPanel* panel = bridge->mInventoryPanel.get(); - if (!panel) return; - LLInventoryModel* model = panel->getModel(); - if(!model) return; - LLUUID id; - id = model->createNewCategory(bridge->getUUID(), - LLFolderType::FT_NONE, - LLStringUtil::null); - model->notifyObservers(); - - // At this point, the bridge has probably been deleted, but the - // view is still there. - panel->setSelection(id, TAKE_FOCUS_YES); -} - void LLFolderBridge::createNewShirt(void* user_data) { LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHIRT); @@ -3596,6 +3712,24 @@ void LLFolderBridge::createNewEyes(void* user_data) LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_EYES); } +EInventorySortGroup LLFolderBridge::getSortGroup() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + if (preferred_type == LLFolderType::FT_TRASH) + { + return SG_TRASH_FOLDER; + } + + if(LLFolderType::lookupIsProtectedType(preferred_type)) + { + return SG_SYSTEM_FOLDER; + } + + return SG_NORMAL_FOLDER; +} + + // static void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::EType type) { @@ -3698,9 +3832,10 @@ void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback(); LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; - if (drag_over_item && drag_over_item->getListener()) + LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL; + if (view_model) { - cb.get()->setTargetLandmarkId(drag_over_item->getListener()->getUUID()); + cb.get()->setTargetLandmarkId(view_model->getUUID()); } copy_inventory_item( @@ -3749,7 +3884,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); @@ -3866,13 +4001,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -3884,6 +4016,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // If an item is being dragged between windows, unselect everything in the active window // so that we don't follow the selection to its new location (which is very annoying). + // RN: a better solution would be to deselect automatically when an item is moved + // and then select any item that is dropped only in the panel that it is dropped in if (active_panel && (destination_panel != active_panel)) { active_panel->unSelectAll(); @@ -3901,8 +4035,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (itemp) { LLUUID srcItemId = inv_item->getUUID(); - LLUUID destItemId = itemp->getListener()->getUUID(); - gInventory.rearrangeFavoriteLandmarks(srcItemId, destItemId); + LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID(); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); } } @@ -4089,13 +4223,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -4135,10 +4266,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!model || !active_folder_view || !filter) + if (!model || !active_panel || !filter) return false; if (!filter->checkFolder(cat_id)) @@ -4158,13 +4289,13 @@ bool check_category(LLInventoryModel* model, // Empty folder should be checked as any other folder view item. // If we are filtering by date the folder should not pass because // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate(). - return check_item(cat_id, active_folder_view, filter); + return check_item(cat_id, active_panel, filter); } for (S32 i = 0; i < num_descendent_categories; ++i) { LLInventoryCategory* category = descendent_categories[i]; - if(!check_category(model, category->getUUID(), active_folder_view, filter)) + if(!check_category(model, category->getUUID(), active_panel, filter)) { return false; } @@ -4173,7 +4304,7 @@ bool check_category(LLInventoryModel* model, for (S32 i = 0; i < num_descendent_items; ++i) { LLViewerInventoryItem* item = descendent_items[i]; - if(!check_item(item->getUUID(), active_folder_view, filter)) + if(!check_item(item->getUUID(), active_panel, filter)) { return false; } @@ -4184,15 +4315,15 @@ bool check_category(LLInventoryModel* model, // static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!active_folder_view || !filter) return false; + if (!active_panel || !filter) return false; - LLFolderViewItem* fv_item = active_folder_view->getItemByID(item_id); + LLFolderViewItem* fv_item = active_panel->getItemByID(item_id); if (!fv_item) return false; - return filter->check(fv_item); + return filter->check(fv_item->getViewModelItem()); } // +=================================================+ @@ -4294,15 +4425,6 @@ void LLSoundBridge::openItem() } } -void LLSoundBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - send_sound_trigger(item->getAssetUUID(), 1.0); - } -} - void LLSoundBridge::openSoundPreview(void* which) { LLSoundBridge *me = (LLSoundBridge *)which; @@ -4518,7 +4640,7 @@ LLCallingCardBridge::~LLCallingCardBridge() void LLCallingCardBridge::refreshFolderViewItem() { LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; + LLFolderViewItem* itemp = panel ? panel->getItemByID(mUUID) : NULL; if (itemp) { itemp->refresh(); @@ -5309,11 +5431,10 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); - model->notifyObservers(); + buildDisplayName(); if (isAgentAvatarValid()) { @@ -5913,16 +6034,6 @@ void LLMeshBridge::openItem() } } -void LLMeshBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // preview mesh - } -} - - void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { lldebugs << "LLMeshBridge::buildContextMenu()" << llendl; @@ -6011,14 +6122,15 @@ void LLLinkFolderBridge::gotoItem() const LLUUID &cat_uuid = getFolderID(); if (!cat_uuid.isNull()) { - if (LLFolderViewItem *base_folder = mRoot->getItemByID(cat_uuid)) + LLFolderViewItem *base_folder = mInventoryPanel.get()->getItemByID(cat_uuid); + if (base_folder) { if (LLInventoryModel* model = getInventoryModel()) { model->fetchDescendentsOf(cat_uuid); } base_folder->setOpen(TRUE); - mRoot->setSelectionFromRoot(base_folder,TRUE); + mRoot->setSelection(base_folder,TRUE); mRoot->scrollToShowSelection(); } } @@ -6349,9 +6461,8 @@ LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_ /************************************************************************/ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - LLFolderBridge::buildContextMenu(menu, flags); - - menuentry_vec_t disabled_items, items = getMenuItems(); + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); @@ -6363,42 +6474,29 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /*= 0x00*/ ) const { LLInvFVBridge* new_listener = NULL; - switch(asset_type) + if (asset_type == LLAssetType::AT_CATEGORY + && actual_asset_type != LLAssetType::AT_LINK_FOLDER) { - case LLAssetType::AT_CATEGORY: - if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) - { - // *TODO: Create a link folder handler instead if it is necessary - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, - actual_asset_type, - inv_type, - inventory, - root, - uuid, - flags); - break; - } new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); - break; - default: - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, - actual_asset_type, - inv_type, - inventory, - root, - uuid, - flags); + } + else + { + new_listener = LLInventoryFVBridgeBuilder::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + view_model, + root, + uuid, + flags); } return new_listener; - } - // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index dc9e88d54d..9997d1720f 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -29,11 +29,13 @@ #include "llcallingcard.h" #include "llfloaterproperties.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" +#include "llinventorypanel.h" #include "llviewercontrol.h" #include "llwearable.h" +#include "lltooldraganddrop.h" class LLInventoryFilter; class LLInventoryPanel; @@ -41,7 +43,7 @@ class LLInventoryModel; class LLMenuGL; class LLCallingCardObserver; class LLViewerJointAttachment; - +class LLFolderView; typedef std::vector<std::string> menuentry_vec_t; @@ -56,7 +58,7 @@ typedef std::vector<std::string> menuentry_vec_t; // functionality a bit. (except for folders, you can create those // manually...) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInvFVBridge : public LLFolderViewEventListener +class LLInvFVBridge : public LLFolderViewModelItemInventory { public: // This method is a convenience function which creates the correct @@ -65,6 +67,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00); @@ -78,23 +81,25 @@ public: // LLInvFVBridge functionality //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } - virtual void clearDisplayName() {} + virtual void clearDisplayName() { mDisplayName.clear(); } virtual void restoreItem() {} virtual void restoreToWorld() {} //-------------------------------------------------------------------- - // Inherited LLFolderViewEventListener functions + // Inherited LLFolderViewModelItemInventory functions //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + const std::string& getSearchableName() const { return mSearchableName; } + virtual PermissionMask getPermissionMask() const; virtual LLFolderType::EType getPreferredType() const; virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} virtual void closeItem() {} - virtual void previewItem() {openItem();} virtual void showProperties(); virtual BOOL isItemRenameable() const { return TRUE; } //virtual BOOL renameItem(const std::string& new_name) {} @@ -103,8 +108,8 @@ public: virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; //virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* new_parent_bridge) {} + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -115,6 +120,7 @@ public: void getClipboardEntries(bool show_asset_id, menuentry_vec_t &items, menuentry_vec_t &disabled_items, U32 flags); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual LLToolDragAndDrop::ESource getDragSource() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -122,6 +128,9 @@ public: std::string& tooltip_msg) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const; + //-------------------------------------------------------------------- // Convenience functions for adding various common menu options. @@ -138,16 +147,16 @@ protected: protected: LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); - LLInventoryObject* getInventoryObject() const; LLInventoryModel* getInventoryModel() const; + LLInventoryFilter* getInventoryFilter() const; BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? BOOL isAgentInventory() const; // false if lost or in the inventory library - BOOL isCOFFolder() const; // true if COF or descendent of - BOOL isInboxFolder() const; // true if COF or descendent of marketplace inbox - BOOL isOutboxFolder() const; // true if COF or descendent of marketplace outbox + BOOL isCOFFolder() const; // true if COF or descendant of + BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox + BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox BOOL isOutboxFolderDirectParent() const; const LLUUID getOutboxFolder() const; @@ -160,14 +169,19 @@ protected: LLViewerInventoryCategory* item, const LLUUID& new_parent, BOOL restamp); - void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch); + void removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch); protected: - LLHandle<LLInventoryPanel> mInventoryPanel; - LLFolderView* mRoot; - const LLUUID mUUID; // item id - LLInventoryType::EType mInvType; - BOOL mIsLink; + LLHandle<LLInventoryPanel> mInventoryPanel; + LLFolderView* mRoot; + const LLUUID mUUID; // item id + LLInventoryType::EType mInvType; + bool mIsLink; + LLTimer mTimeSinceRequestStart; + mutable std::string mDisplayName; + mutable std::string mSearchableName; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); + virtual void buildDisplayName() const {} }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -184,6 +198,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; @@ -203,7 +218,6 @@ public: virtual void restoreToWorld(); virtual void gotoItem(); virtual LLUIImagePtr getIcon() const; - virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; virtual PermissionMask getPermissionMask() const; @@ -212,19 +226,16 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); virtual BOOL isItemCopyable() const; - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } - LLViewerInventoryItem* getItem() const; protected: BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; - static void buildDisplayName(LLInventoryItem* item, std::string& name); + virtual void buildDisplayName() const; - mutable std::string mDisplayName; }; class LLFolderBridge : public LLInvFVBridge @@ -232,15 +243,18 @@ class LLFolderBridge : public LLInvFVBridge public: LLFolderBridge(LLInventoryPanel* inventory, LLFolderView* root, - const LLUUID& uuid) : - LLInvFVBridge(inventory, root, uuid), + const LLUUID& uuid) + : LLInvFVBridge(inventory, root, uuid), mCallingCards(FALSE), - mWearables(FALSE) + mWearables(FALSE), + mIsLoading(false) {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg); + virtual void buildDisplayName() const; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -250,7 +264,9 @@ public: virtual LLFolderType::EType getPreferredType() const; virtual LLUIImagePtr getIcon() const; - virtual LLUIImagePtr getOpenIcon() const; + virtual LLUIImagePtr getIconOpen() const; + virtual LLUIImagePtr getIconOverlay() const; + static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual BOOL renameItem(const std::string& new_name); @@ -262,7 +278,7 @@ public: virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -275,20 +291,24 @@ public: virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; + EInventorySortGroup getSortGroup() const; + virtual void update(); + static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); LLViewerInventoryCategory* getCategory() const; LLHandle<LLFolderBridge> getHandle() { mHandle.bind(this); return mHandle; } + bool isLoading() { return mIsLoading; } + protected: - void buildContextMenuBaseOptions(U32 flags); - void buildContextMenuFolderOptions(U32 flags); + void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); + void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); //-------------------------------------------------------------------- // Menu callbacks //-------------------------------------------------------------------- static void pasteClipboard(void* user_data); - static void createNewCategory(void* user_data); static void createNewShirt(void* user_data); static void createNewPants(void* user_data); static void createNewShoes(void* user_data); @@ -308,8 +328,6 @@ protected: void modifyOutfit(BOOL append); void determineFolderType(); - menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items - void dropToFavorites(LLInventoryItem* inv_item); void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); @@ -321,11 +339,12 @@ public: static void staticFolderOptionsMenu(); private: - BOOL mCallingCards; - BOOL mWearables; - menuentry_vec_t mItems; - menuentry_vec_t mDisabledItems; - LLRootHandle<LLFolderBridge> mHandle; + + bool mCallingCards; + bool mWearables; + bool mIsLoading; + LLTimer mTimeSinceRequestStart; + LLRootHandle<LLFolderBridge> mHandle; }; class LLTextureBridge : public LLItemBridge @@ -354,7 +373,6 @@ public: const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void*); }; @@ -544,7 +562,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -629,6 +646,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 4573074c73..b4be927b09 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -29,7 +29,7 @@ #include "llinventoryfilter.h" // viewer includes -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llfolderviewitem.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" @@ -42,109 +42,91 @@ #include "llclipboard.h" #include "lltrans.h" +//TODO RN: fix use of static cast as much as possible + LLFastTimer::DeclareTimer FT_FILTER_CLIPBOARD("Filter Clipboard"); -LLInventoryFilter::FilterOps::FilterOps() : - mFilterObjectTypes(0xffffffffffffffffULL), - mFilterCategoryTypes(0xffffffffffffffffULL), - mFilterWearableTypes(0xffffffffffffffffULL), - mMinDate(time_min()), - mMaxDate(time_max()), - mHoursAgo(0), - mShowFolderState(SHOW_NON_EMPTY_FOLDERS), - mPermissions(PERM_NONE), - mFilterTypes(FILTERTYPE_OBJECT), - mFilterUUID(LLUUID::null), - mFilterLinks(FILTERLINK_INCLUDE_LINKS) +LLInventoryFilter::FilterOps::FilterOps(const Params& p) +: mFilterObjectTypes(p.object_types), + mFilterCategoryTypes(p.category_types), + mFilterWearableTypes(p.wearable_types), + mMinDate(p.date_range.min_date), + mMaxDate(p.date_range.max_date), + mHoursAgo(p.hours_ago), + mShowFolderState(p.show_folder_state), + mPermissions(p.permissions), + mFilterTypes(p.types), + mFilterUUID(p.uuid), + mFilterLinks(p.links) { } ///---------------------------------------------------------------------------- /// Class LLInventoryFilter ///---------------------------------------------------------------------------- -LLInventoryFilter::LLInventoryFilter(const std::string& name) -: mName(name), - mModified(FALSE), - mNeedTextRebuild(TRUE), - mEmptyLookupMessage("InventoryNoMatchingItems") +LLInventoryFilter::LLInventoryFilter(const Params& p) +: mName(p.name), + mFilterModified(FILTER_NONE), + mEmptyLookupMessage("InventoryNoMatchingItems"), + mFilterOps(p.filter_ops), + mOrder(p.sort_order), + mFilterSubString(p.substring), + mCurrentGeneration(0), + mFirstRequiredGeneration(0), + mFirstSuccessGeneration(0), + mFilterCount(0) { - mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately - - mSubStringMatchOffset = 0; - mFilterSubString.clear(); - mFilterGeneration = 0; - mMustPassGeneration = S32_MAX; - mMinRequiredGeneration = 0; - mFilterCount = 0; - mNextFilterGeneration = mFilterGeneration + 1; - - mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); - mFilterBehavior = FILTER_NONE; + mNextFilterGeneration = mCurrentGeneration + 1; // copy mFilterOps into mDefaultFilterOps markDefault(); } -LLInventoryFilter::~LLInventoryFilter() -{ -} - -BOOL LLInventoryFilter::check(const LLFolderViewItem* item) +bool LLInventoryFilter::check(const LLFolderViewModelItem* item) { + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); // Clipboard cut items are *always* filtered so we need this value upfront - const LLFolderViewEventListener* listener = item->getListener(); const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); // If it's a folder and we're showing all folders, return automatically. - const BOOL is_folder = (dynamic_cast<const LLFolderViewFolder*>(item) != NULL); + const BOOL is_folder = listener->getInventoryType() == LLInventoryType::IT_CATEGORY; if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { return passed_clipboard; } - mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : 0; - const BOOL passed_filtertype = checkAgainstFilterType(item); - const BOOL passed_permissions = checkAgainstPermissions(item); - const BOOL passed_filterlink = checkAgainstFilterLinks(item); - const BOOL passed = (passed_filtertype && - passed_permissions && - passed_filterlink && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + BOOL passed = string_offset != std::string::npos; + passed = passed && checkAgainstFilterType(listener); + passed = passed && checkAgainstPermissions(listener); + passed = passed && checkAgainstFilterLinks(listener); + passed = passed && passed_clipboard; return passed; } bool LLInventoryFilter::check(const LLInventoryItem* item) { - mSubStringMatchOffset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); - const bool passed = (passed_filtertype && - passed_permissions && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + const bool passed = (passed_filtertype + && passed_permissions + && passed_clipboard + && (mFilterSubString.size() == 0 || string_offset != std::string::npos)); return passed; } -bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const +bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const { - if (!folder) - { - llwarns << "The filter can not be checked on an invalid folder." << llendl; - llassert(false); // crash in development builds - return false; - } - - const LLFolderViewEventListener* listener = folder->getListener(); + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); if (!listener) { - llwarns << "Folder view event listener not found." << llendl; - llassert(false); // crash in development builds + llerrs << "Folder view event listener not found." << llendl; return false; } @@ -155,6 +137,13 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { + // when applying a filter, matching folders get their contents downloaded first + if (isNotDefault() + && !gInventory.isCategoryComplete(folder_id)) + { + LLInventoryModelBackgroundFetch::instance().start(folder_id); + } + // Always check against the clipboard const BOOL passed_clipboard = checkAgainstClipboard(folder_id); @@ -163,14 +152,14 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { return passed_clipboard; } - + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) { // Can only filter categories for items in your inventory // (e.g. versus in-world object contents). const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); if (!cat) - return false; + return folder_id.isNull(); LLFolderType::EType cat_type = cat->getPreferredType(); if (cat_type != LLFolderType::FT_NONE && (1LL << cat_type & mFilterOps.mFilterCategoryTypes) == U64(0)) return false; @@ -179,9 +168,8 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return passed_clipboard; } -BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; LLInventoryType::EType object_type = listener->getInventoryType(); @@ -268,7 +256,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } } - + return TRUE; } @@ -347,13 +335,12 @@ bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const return true; } -BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstPermissions(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; PermissionMask perm = listener->getPermissionMask(); - const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(item->getListener()); + const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(listener); if (bridge && bridge->isLink()) { const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); @@ -375,9 +362,8 @@ bool LLInventoryFilter::checkAgainstPermissions(const LLInventoryItem* item) con return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; } -BOOL LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return TRUE; const LLUUID object_id = listener->getUUID(); @@ -397,20 +383,20 @@ const std::string& LLInventoryFilter::getFilterSubString(BOOL trim) const return mFilterSubString; } -std::string::size_type LLInventoryFilter::getStringMatchOffset() const +std::string::size_type LLInventoryFilter::getStringMatchOffset(LLFolderViewItem* item) const { - return mSubStringMatchOffset; + return mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; } -BOOL LLInventoryFilter::isDefault() const +bool LLInventoryFilter::isDefault() const { return !isNotDefault(); } // has user modified default filter params? -BOOL LLInventoryFilter::isNotDefault() const +bool LLInventoryFilter::isNotDefault() const { - BOOL not_default = FALSE; + S32 not_default = 0; not_default |= (mFilterOps.mFilterObjectTypes != mDefaultFilterOps.mFilterObjectTypes); not_default |= (mFilterOps.mFilterCategoryTypes != mDefaultFilterOps.mFilterCategoryTypes); @@ -422,11 +408,11 @@ BOOL LLInventoryFilter::isNotDefault() const not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); - - return not_default; + + return not_default != 0; } -BOOL LLInventoryFilter::isActive() const +bool LLInventoryFilter::isActive() const { return mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL || mFilterOps.mFilterCategoryTypes != 0xffffffffffffffffULL @@ -440,16 +426,9 @@ BOOL LLInventoryFilter::isActive() const || mFilterOps.mHoursAgo != 0; } -BOOL LLInventoryFilter::isModified() const +bool LLInventoryFilter::isModified() const { - return mModified; -} - -BOOL LLInventoryFilter::isModifiedAndClear() -{ - BOOL ret = mModified; - mModified = FALSE; - return ret; + return mFilterModified != FILTER_NONE; } void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) @@ -613,9 +592,10 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) { + static LLCachedControl<U32> s_last_logoff(gSavedPerAccountSettings, "LastLogoff", 0); if (sl && !isSinceLogoff()) { - setDateRange(mLastLogoff, time_max()); + setDateRange(s_last_logoff(), time_max()); setModified(); } if (!sl && isSinceLogoff()) @@ -634,17 +614,18 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) } } -BOOL LLInventoryFilter::isSinceLogoff() const +bool LLInventoryFilter::isSinceLogoff() const { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && + static LLCachedControl<U32> s_last_logoff(gSavedSettings, "LastLogoff", 0); + + return (mFilterOps.mMinDate == (time_t)s_last_logoff()) && (mFilterOps.mMaxDate == time_max()) && (mFilterOps.mFilterTypes & FILTERTYPE_DATE); } void LLInventoryFilter::clearModified() { - mModified = FALSE; - mFilterBehavior = FILTER_NONE; + mFilterModified = FILTER_NONE; } void LLInventoryFilter::setHoursAgo(U32 hours) @@ -742,83 +723,68 @@ void LLInventoryFilter::resetDefault() setModified(); } -void LLInventoryFilter::setModified(EFilterBehavior behavior) +void LLInventoryFilter::setModified(EFilterModified behavior) { - mModified = TRUE; - mNeedTextRebuild = TRUE; - mFilterGeneration = mNextFilterGeneration++; + mFilterText.clear(); + mCurrentGeneration = mNextFilterGeneration++; - if (mFilterBehavior == FILTER_NONE) + if (mFilterModified == FILTER_NONE) { - mFilterBehavior = behavior; + mFilterModified = behavior; } - else if (mFilterBehavior != behavior) + else if (mFilterModified != behavior) { // trying to do both less restrictive and more restrictive filter // basically means restart from scratch - mFilterBehavior = FILTER_RESTART; + mFilterModified = FILTER_RESTART; } - if (isNotDefault()) - { - // if not keeping current filter results, update last valid as well - switch(mFilterBehavior) - { - case FILTER_RESTART: - mMustPassGeneration = mFilterGeneration; - mMinRequiredGeneration = mFilterGeneration; - break; - case FILTER_LESS_RESTRICTIVE: - mMustPassGeneration = mFilterGeneration; - break; - case FILTER_MORE_RESTRICTIVE: - mMinRequiredGeneration = mFilterGeneration; - // must have passed either current filter generation (meaningless, as it hasn't been run yet) - // or some older generation, so keep the value - mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); - break; - default: - llerrs << "Bad filter behavior specified" << llendl; - } - } - else + // if not keeping current filter results, update last valid as well + switch(mFilterModified) { - // shortcut disabled filters to show everything immediately - mMinRequiredGeneration = 0; - mMustPassGeneration = S32_MAX; + case FILTER_RESTART: + mFirstRequiredGeneration = mCurrentGeneration; + mFirstSuccessGeneration = mCurrentGeneration; + break; + case FILTER_LESS_RESTRICTIVE: + mFirstRequiredGeneration = mCurrentGeneration; + break; + case FILTER_MORE_RESTRICTIVE: + mFirstSuccessGeneration = mCurrentGeneration; + break; + default: + llerrs << "Bad filter behavior specified" << llendl; } } -BOOL LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const +bool LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const { return mFilterOps.mFilterObjectTypes & (1LL << t); } const std::string& LLInventoryFilter::getFilterText() { - if (!mNeedTextRebuild) + if (!mFilterText.empty()) { return mFilterText; } - mNeedTextRebuild = FALSE; std::string filtered_types; std::string not_filtered_types; BOOL filtered_by_type = FALSE; BOOL filtered_by_all_types = TRUE; S32 num_filter_types = 0; + mFilterText.clear(); if (isFilterObjectTypesWith(LLInventoryType::IT_ANIMATION)) { - //filtered_types += " Animations,"; filtered_types += LLTrans::getString("Animations"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Animations,"; not_filtered_types += LLTrans::getString("Animations"); filtered_by_all_types = FALSE; @@ -826,140 +792,120 @@ const std::string& LLInventoryFilter::getFilterText() if (isFilterObjectTypesWith(LLInventoryType::IT_CALLINGCARD)) { - //filtered_types += " Calling Cards,"; filtered_types += LLTrans::getString("Calling Cards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Calling Cards,"; not_filtered_types += LLTrans::getString("Calling Cards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_WEARABLE)) { - //filtered_types += " Clothing,"; filtered_types += LLTrans::getString("Clothing"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Clothing,"; not_filtered_types += LLTrans::getString("Clothing"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_GESTURE)) { - //filtered_types += " Gestures,"; filtered_types += LLTrans::getString("Gestures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Gestures,"; not_filtered_types += LLTrans::getString("Gestures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LANDMARK)) { - //filtered_types += " Landmarks,"; filtered_types += LLTrans::getString("Landmarks"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Landmarks,"; not_filtered_types += LLTrans::getString("Landmarks"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_NOTECARD)) { - //filtered_types += " Notecards,"; filtered_types += LLTrans::getString("Notecards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Notecards,"; not_filtered_types += LLTrans::getString("Notecards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_OBJECT) && isFilterObjectTypesWith(LLInventoryType::IT_ATTACHMENT)) { - //filtered_types += " Objects,"; filtered_types += LLTrans::getString("Objects"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Objects,"; not_filtered_types += LLTrans::getString("Objects"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LSL)) { - //filtered_types += " Scripts,"; filtered_types += LLTrans::getString("Scripts"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Scripts,"; not_filtered_types += LLTrans::getString("Scripts"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SOUND)) { - //filtered_types += " Sounds,"; filtered_types += LLTrans::getString("Sounds"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Sounds,"; not_filtered_types += LLTrans::getString("Sounds"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_TEXTURE)) { - //filtered_types += " Textures,"; filtered_types += LLTrans::getString("Textures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Textures,"; not_filtered_types += LLTrans::getString("Textures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SNAPSHOT)) { - //filtered_types += " Snapshots,"; filtered_types += LLTrans::getString("Snapshots"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Snapshots,"; not_filtered_types += LLTrans::getString("Snapshots"); filtered_by_all_types = FALSE; } @@ -975,7 +921,6 @@ const std::string& LLInventoryFilter::getFilterText() } else { - //mFilterText += "No "; mFilterText += LLTrans::getString("No Filters"); mFilterText += not_filtered_types; } @@ -985,66 +930,58 @@ const std::string& LLInventoryFilter::getFilterText() if (isSinceLogoff()) { - //mFilterText += " - Since Logoff"; mFilterText += LLTrans::getString("Since Logoff"); } return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) const -{ - data["filter_types"] = (LLSD::Integer)getFilterObjectTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); -} - -void LLInventoryFilter::fromLLSD(LLSD& data) -{ - if(data.has("filter_types")) - { - setFilterObjectTypes((U64)data["filter_types"].asInteger()); - } - - if(data.has("min_date") && data.has("max_date")) - { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); - } - - if(data.has("hours_ago")) - { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } - if(data.has("show_folder_state")) - { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); - } +LLInventoryFilter& LLInventoryFilter::operator=( const LLInventoryFilter& other ) +{ + setFilterObjectTypes(other.getFilterObjectTypes()); + setDateRange(other.getMinDate(), other.getMaxDate()); + setHoursAgo(other.getHoursAgo()); + setShowFolderState(other.getShowFolderState()); + setFilterPermissions(other.getFilterPermissions()); + setFilterSubString(other.getFilterSubString()); + setSortOrder(other.getSortOrder()); + setDateRangeLastLogoff(other.isSinceLogoff()); + return *this; +} - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } +void LLInventoryFilter::toParams(Params& params) const +{ + params.filter_ops.types = getFilterObjectTypes(); + params.filter_ops.category_types = getFilterCategoryTypes(); + params.filter_ops.wearable_types = getFilterWearableTypes(); + params.filter_ops.date_range.min_date = getMinDate(); + params.filter_ops.date_range.max_date = getMaxDate(); + params.filter_ops.hours_ago = getHoursAgo(); + params.filter_ops.show_folder_state = getShowFolderState(); + params.filter_ops.permissions = getFilterPermissions(); + params.substring = getFilterSubString(); + params.sort_order = getSortOrder(); + params.since_logoff = isSinceLogoff(); +} - if(data.has("sort_order")) +void LLInventoryFilter::fromParams(const Params& params) +{ + if (!params.validateBlock()) { - setSortOrder((U32)data["sort_order"].asInteger()); + return; } - if(data.has("since_logoff")) - { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); - } + setFilterObjectTypes(params.filter_ops.types); + setFilterCategoryTypes(params.filter_ops.category_types); + setFilterWearableTypes(params.filter_ops.wearable_types); + setDateRange(params.filter_ops.date_range.min_date, params.filter_ops.date_range.max_date); + setHoursAgo(params.filter_ops.hours_ago); + setShowFolderState(params.filter_ops.show_folder_state); + setFilterPermissions(params.filter_ops.permissions); + setFilterSubString(params.substring); + setSortOrder(params.sort_order); + setDateRangeLastLogoff(params.since_logoff); } U64 LLInventoryFilter::getFilterObjectTypes() const @@ -1057,7 +994,12 @@ U64 LLInventoryFilter::getFilterCategoryTypes() const return mFilterOps.mFilterCategoryTypes; } -BOOL LLInventoryFilter::hasFilterString() const +U64 LLInventoryFilter::getFilterWearableTypes() const +{ + return mFilterOps.mFilterWearableTypes; +} + +bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; } @@ -1092,10 +1034,6 @@ U32 LLInventoryFilter::getSortOrder() const { return mOrder; } -const std::string& LLInventoryFilter::getName() const -{ - return mName; -} void LLInventoryFilter::setFilterCount(S32 count) { @@ -1113,15 +1051,15 @@ void LLInventoryFilter::decrementFilterCount() S32 LLInventoryFilter::getCurrentGeneration() const { - return mFilterGeneration; + return mCurrentGeneration; } -S32 LLInventoryFilter::getMinRequiredGeneration() const +S32 LLInventoryFilter::getFirstSuccessGeneration() const { - return mMinRequiredGeneration; + return mFirstSuccessGeneration; } -S32 LLInventoryFilter::getMustPassGeneration() const +S32 LLInventoryFilter::getFirstRequiredGeneration() const { - return mMustPassGeneration; + return mFirstRequiredGeneration; } void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) @@ -1129,9 +1067,12 @@ void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) mEmptyLookupMessage = message; } -const std::string& LLInventoryFilter::getEmptyLookupMessage() const +std::string LLInventoryFilter::getEmptyLookupMessage() const { - return mEmptyLookupMessage; + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); + + return LLTrans::getString(mEmptyLookupMessage, args); } @@ -1141,3 +1082,27 @@ bool LLInventoryFilter::areDateLimitsSet() || mFilterOps.mMaxDate != time_max() || mFilterOps.mHoursAgo != 0; } + +bool LLInventoryFilter::showAllResults() const +{ + return hasFilterString(); +} + + + +bool LLInventoryFilter::FilterOps::DateRange::validateBlock( bool emit_errors /*= true*/ ) const +{ + bool valid = LLInitParam::Block<DateRange>::validateBlock(emit_errors); + if (valid) + { + if (max_date() < min_date()) + { + if (emit_errors) + { + llwarns << "max_date should be greater or equal to min_date" << llendl; + } + valid = false; + } + } + return valid; +} diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 9e600c036f..af245a9c3b 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -29,12 +29,13 @@ #include "llinventorytype.h" #include "llpermissionsflags.h" +#include "llfolderviewmodel.h" class LLFolderViewItem; class LLFolderViewFolder; class LLInventoryItem; -class LLInventoryFilter +class LLInventoryFilter : public LLFolderViewFilter { public: enum EFolderShow @@ -44,14 +45,6 @@ public: SHOW_NO_FOLDERS }; - enum EFilterBehavior - { - FILTER_NONE, // nothing to do, already filtered - FILTER_RESTART, // restart filtering from scratch - FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter - FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one - }; - enum EFilterType { FILTERTYPE_NONE = 0, FILTERTYPE_OBJECT = 0x1 << 0, // normal default search-by-object-type @@ -59,7 +52,7 @@ public: FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it FILTERTYPE_DATE = 0x1 << 3, // search by date range FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type - FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if }; enum EFilterLink @@ -77,16 +70,94 @@ public: SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top }; - LLInventoryFilter(const std::string& name); - virtual ~LLInventoryFilter(); + struct FilterOps + { + struct DateRange : public LLInitParam::Block<DateRange> + { + Optional<time_t> min_date; + Optional<time_t> max_date; + + DateRange() + : min_date("min_date", time_min()), + max_date("max_date", time_max()) + {} + + bool validateBlock(bool emit_errors = true) const; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<U32> types; + Optional<U64> object_types, + wearable_types, + category_types; + Optional<EFilterLink> links; + Optional<LLUUID> uuid; + Optional<DateRange> date_range; + Optional<S32> hours_ago; + Optional<EFolderShow> show_folder_state; + Optional<PermissionMask> permissions; + + Params() + : types("filter_types", FILTERTYPE_OBJECT), + object_types("object_types", 0xffffFFFFffffFFFFULL), + wearable_types("wearable_types", 0xffffFFFFffffFFFFULL), + category_types("category_types", 0xffffFFFFffffFFFFULL), + links("links", FILTERLINK_INCLUDE_LINKS), + uuid("uuid"), + date_range("date_range"), + hours_ago("hours_ago", 0), + show_folder_state("show_folder_state", SHOW_NON_EMPTY_FOLDERS), + permissions("permissions", PERM_NONE) + {} + }; + + FilterOps(const Params& = Params()); + + U32 mFilterTypes; + + U64 mFilterObjectTypes; // For _OBJECT + U64 mFilterWearableTypes; + U64 mFilterCategoryTypes; // For _CATEGORY + LLUUID mFilterUUID; // for UUID + + time_t mMinDate; + time_t mMaxDate; + U32 mHoursAgo; + EFolderShow mShowFolderState; + PermissionMask mPermissions; + U64 mFilterLinks; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<std::string> name; + Optional<FilterOps::Params> filter_ops; + Optional<std::string> substring; + Optional<U32> sort_order; + Optional<bool> since_logoff; + + Params() + : name("name"), + filter_ops(""), + substring("substring"), + sort_order("sort_order"), + since_logoff("since_logoff") + {} + }; + + LLInventoryFilter(const Params& p = Params()); + LLInventoryFilter(const LLInventoryFilter& other) { *this = other; } + virtual ~LLInventoryFilter() {} // +-------------------------------------------------------------------+ // + Parameters // +-------------------------------------------------------------------+ - void setFilterObjectTypes(U64 types); U64 getFilterObjectTypes() const; U64 getFilterCategoryTypes() const; - BOOL isFilterObjectTypesWith(LLInventoryType::EType t) const; + U64 getFilterWearableTypes() const; + bool isFilterObjectTypesWith(LLInventoryType::EType t) const; + void setFilterObjectTypes(U64 types); void setFilterCategoryTypes(U64 types); void setFilterUUID(const LLUUID &object_id); void setFilterWearableTypes(U64 types); @@ -96,7 +167,7 @@ public: void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } - BOOL hasFilterString() const; + bool hasFilterString() const; void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -115,19 +186,17 @@ public: // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ - BOOL check(const LLFolderViewItem* item); + bool check(const LLFolderViewModelItem* listener); bool check(const LLInventoryItem* item); - bool checkFolder(const LLFolderViewFolder* folder) const; + bool checkFolder(const LLFolderViewModelItem* listener) const; bool checkFolder(const LLUUID& folder_id) const; - BOOL checkAgainstFilterType(const LLFolderViewItem* item) const; - bool checkAgainstFilterType(const LLInventoryItem* item) const; - BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; - bool checkAgainstPermissions(const LLInventoryItem* item) const; - BOOL checkAgainstFilterLinks(const LLFolderViewItem* item) const; - bool checkAgainstClipboard(const LLUUID& object_id) const; + + bool showAllResults() const; + std::string::size_type getStringMatchOffset() const; + std::string::size_type getStringMatchOffset(LLFolderViewItem* item) const; // +-------------------------------------------------------------------+ // + Presentation // +-------------------------------------------------------------------+ @@ -138,20 +207,19 @@ public: U32 getSortOrder() const; void setEmptyLookupMessage(const std::string& message); - const std::string& getEmptyLookupMessage() const; + std::string getEmptyLookupMessage() const; // +-------------------------------------------------------------------+ // + Status // +-------------------------------------------------------------------+ - BOOL isActive() const; - BOOL isModified() const; - BOOL isModifiedAndClear(); - BOOL isSinceLogoff() const; + bool isActive() const; + bool isModified() const; + bool isSinceLogoff() const; void clearModified(); - const std::string& getName() const; + const std::string& getName() const { return mName; } const std::string& getFilterText(); //RN: this is public to allow system to externally force a global refilter - void setModified(EFilterBehavior behavior = FILTER_RESTART); + void setModified(EFilterModified behavior = FILTER_RESTART); // +-------------------------------------------------------------------+ // + Count @@ -163,8 +231,8 @@ public: // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ - BOOL isDefault() const; - BOOL isNotDefault() const; + bool isDefault() const; + bool isNotDefault() const; void markDefault(); void resetDefault(); @@ -172,57 +240,44 @@ public: // + Generation // +-------------------------------------------------------------------+ S32 getCurrentGeneration() const; - S32 getMinRequiredGeneration() const; - S32 getMustPassGeneration() const; + S32 getFirstSuccessGeneration() const; + S32 getFirstRequiredGeneration() const; + // +-------------------------------------------------------------------+ // + Conversion // +-------------------------------------------------------------------+ - void toLLSD(LLSD& data) const; - void fromLLSD(LLSD& data); + void toParams(Params& params) const; + void fromParams(const Params& p); + + LLInventoryFilter& operator =(const LLInventoryFilter& other); private: bool areDateLimitsSet(); - - struct FilterOps - { - FilterOps(); - U32 mFilterTypes; - - U64 mFilterObjectTypes; // For _OBJECT - U64 mFilterWearableTypes; - U64 mFilterCategoryTypes; // For _CATEGORY - LLUUID mFilterUUID; // for UUID - - time_t mMinDate; - time_t mMaxDate; - U32 mHoursAgo; - EFolderShow mShowFolderState; - PermissionMask mPermissions; - U64 mFilterLinks; - }; + bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstFilterType(const LLInventoryItem* item) const; + bool checkAgainstPermissions(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstPermissions(const LLInventoryItem* item) const; + bool checkAgainstFilterLinks(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstClipboard(const LLUUID& object_id) const; U32 mOrder; - U32 mLastLogoff; FilterOps mFilterOps; FilterOps mDefaultFilterOps; - std::string::size_type mSubStringMatchOffset; std::string mFilterSubString; std::string mFilterSubStringOrig; const std::string mName; - S32 mFilterGeneration; - S32 mMustPassGeneration; - S32 mMinRequiredGeneration; + S32 mCurrentGeneration; + S32 mFirstRequiredGeneration; + S32 mFirstSuccessGeneration; S32 mNextFilterGeneration; S32 mFilterCount; - EFilterBehavior mFilterBehavior; + EFilterModified mFilterModified; - BOOL mModified; - BOOL mNeedTextRebuild; std::string mFilterText; std::string mEmptyLookupMessage; }; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ab5b082915..07f3dd8ffb 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -45,7 +45,7 @@ // newview includes #include "llappearancemgr.h" #include "llappviewer.h" -//#include "llfirstuse.h" +#include "llclipboard.h" #include "llfloaterinventory.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" @@ -74,8 +74,10 @@ #include "llsidepanelinventory.h" #include "lltabcontainer.h" #include "lltooldraganddrop.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llviewermessage.h" +#include "llviewerfoldertype.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" @@ -948,7 +950,7 @@ void LLSaveFolderState::setApply(BOOL apply) void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) { LLMemType mt(LLMemType::MTYPE_INVENTORY_DO_FOLDER); - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); if(!bridge) return; if(mApply) @@ -983,7 +985,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { - if (item->getFiltered()) + if (item->passedFilter()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } @@ -991,12 +993,12 @@ void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && folder->getParentFolder()) + if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) { folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } // if this folder didn't pass the filter, and none of its descendants did - else if (!folder->getFiltered() && !folder->hasFilteredDescendants()) + else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) { folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_NO); } @@ -1004,7 +1006,7 @@ void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) { - if (item->getFiltered() && !mItemSelected) + if (item->passedFilter() && !mItemSelected) { item->getRoot()->setSelection(item, FALSE, FALSE); if (item->getParentFolder()) @@ -1017,7 +1019,7 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && !mItemSelected) + if (folder->LLFolderViewItem::passedFilter() && !mItemSelected) { folder->getRoot()->setSelection(folder, FALSE, FALSE); if (folder->getParentFolder()) @@ -1044,3 +1046,87 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) } } +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action) +{ + if ("rename" == action) + { + root->startRenamingSelectedItem(); + return; + } + if ("delete" == action) + { + LLSD args; + args["QUESTION"] = LLTrans::getString(root->getNumSelectedItems() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root)); + return; + } + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); + } + + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + LLFolderViewModelItemInventory* inventory_item = static_cast<LLFolderViewModelItemInventory*>(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + return; + } + + + std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); + + LLMultiPreview* multi_previewp = NULL; + LLMultiProperties* multi_propertiesp = NULL; + + if (("task_open" == action || "open" == action) && selected_items.size() > 1) + { + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + + } + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) + { + multi_propertiesp = new LLMultiProperties(); + gFloaterView->addChild(multi_propertiesp); + + 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); + } + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } + else if (multi_propertiesp) + { + multi_propertiesp->openFloater(LLSD()); + } +} + +void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, const LLSD& response, LLFolderView* root ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + root->removeSelectedItems(); + } +} diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 5cf9c528b0..d8d3d9bbbb 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -418,21 +418,6 @@ public: class LLFolderViewItem; class LLFolderViewFolder; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFunctor -// -// Simple abstract base class for applying a functor to folders and -// items in a folder view hierarchy. This is suboptimal for algorithms -// that only work folders or only work on items, but I'll worry about -// that later when it's determined to be too slow. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLFolderViewFunctor -{ -public: - virtual ~LLFolderViewFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder) = 0; - virtual void doItem(LLFolderViewItem* item) = 0; -}; class LLInventoryState { @@ -442,49 +427,13 @@ public: static LLUUID sWearNewClothingTransactionID; // wear all clothing in this transaction }; -class LLSelectFirstFilteredItem : public LLFolderViewFunctor +struct LLInventoryAction { -public: - LLSelectFirstFilteredItem() : mItemSelected(FALSE) {} - virtual ~LLSelectFirstFilteredItem() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - BOOL wasItemSelected() { return mItemSelected; } -protected: - BOOL mItemSelected; -}; + static void doToSelected(class LLInventoryModel* model, class LLFolderView* root, const std::string& action); -class LLOpenFilteredFolders : public LLFolderViewFunctor -{ -public: - LLOpenFilteredFolders() {} - virtual ~LLOpenFilteredFolders() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); + static void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLFolderView* root); }; -class LLSaveFolderState : public LLFolderViewFunctor -{ -public: - LLSaveFolderState() : mApply(FALSE) {} - virtual ~LLSaveFolderState() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item) {} - void setApply(BOOL apply); - void clearOpenFolders() { mOpenFolders.clear(); } -protected: - std::set<LLUUID> mOpenFolders; - BOOL mApply; -}; - -class LLOpenFoldersWithSelection : public LLFolderViewFunctor -{ -public: - LLOpenFoldersWithSelection() {} - virtual ~LLOpenFoldersWithSelection() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; #endif // LL_LLINVENTORYFUNCTIONS_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 6e23d7c701..5cb7f53bfa 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1245,14 +1245,33 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) items, INCLUDE_TRASH); S32 count = items.count(); + + item_map_t::iterator item_map_end = mItemMap.end(); + cat_map_t::iterator cat_map_end = mCategoryMap.end(); + LLUUID uu_id; + for(S32 i = 0; i < count; ++i) { - deleteObject(items.get(i)->getUUID()); + uu_id = items.get(i)->getUUID(); + + // This check prevents the deletion of a previously deleted item. + // This is necessary because deletion is not done in a hierarchical + // order. The current item may have been already deleted as a child + // of its deleted parent. + if (mItemMap.find(uu_id) != item_map_end) + { + deleteObject(uu_id); + } } + count = categories.count(); for(S32 i = 0; i < count; ++i) { - deleteObject(categories.get(i)->getUUID()); + uu_id = categories.get(i)->getUUID(); + if (mCategoryMap.find(uu_id) != cat_map_end) + { + deleteObject(uu_id); + } } } } @@ -3239,68 +3258,7 @@ void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, c } } -//* @param[in] items vector of items in order to be saved. -void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - updateItem(item); - - // Tell the parent folder to refresh its sort order. - addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); - } - - notifyObservers(); -} - -// See also LLInventorySort where landmarks in the Favorites folder are sorted. -class LLViewerInventoryItemSort -{ -public: - bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) - { - return a->getSortField() < b->getSortField(); - } -}; -/** - * Sorts passed items by LLViewerInventoryItem sort field. - * - * @param[in, out] items - array of items, not sorted. - */ -static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -} - -// * @param source_item_id - LLUUID of the source item to be moved into new position -// * @param target_item_id - LLUUID of the target item before which source item should be placed. -void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) -{ - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLIsType is_type(LLAssetType::AT_LANDMARK); - LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - - // ensure items are sorted properly before changing order. EXT-3498 - rearrange_item_order_by_sort_field(items); - - // update order - updateItemsOrder(items, source_item_id, target_item_id); - - saveItemsOrder(items); -} //---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 8382e875b4..3613bc4917 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -362,15 +362,6 @@ public: // Returns end() of the vector if not found. static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - // Saves current order of the passed items using inventory item sort field. - // Resets 'items' sort fields and saves them on server. - // Is used to save order for Favorites folder. - void saveItemsOrder(const LLInventoryModel::item_array_t& items); - - // Rearranges Landmarks inside Favorites folder. - // Moves source landmark before target one. - void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); - //-------------------------------------------------------------------- // Creation //-------------------------------------------------------------------- diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 71dd963f28..6a7ee3b6a0 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -38,11 +38,13 @@ #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llfolderview.h" +#include "llfolderviewitem.h" #include "llimfloater.h" #include "llimview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" +#include "llpreview.h" #include "llsidepanelinventory.h" #include "llviewerattachmenu.h" #include "llviewerfoldertype.h" @@ -55,7 +57,6 @@ const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("Recent const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryPanelObserver // @@ -134,7 +135,6 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mAllowMultiSelect(p.allow_multi_select), mShowItemLinkOverlays(p.show_item_link_overlays), mShowEmptyMessage(p.show_empty_message), - mShowLoadStatus(p.show_load_status), mViewsInitialized(false), mInvFVBridgeBuilder(NULL) { @@ -183,14 +183,25 @@ void LLInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) LLAssetType::AT_CATEGORY, LLInventoryType::IT_CATEGORY, this, + &mInventoryViewModel, NULL, root_id); mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); + addItemID(root_id, mFolderRoot); } void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { + // Clear up the root view + // Note: This needs to be done *before* we build the new folder view + LLFolderViewItem* root_view = getItemByID(gInventory.getRootFolderID()); + if (root_view) + { + removeItemID(static_cast<LLFolderViewModelItemInventory*>(root_view->getViewModelItem())->getUUID()); + root_view->destroyView(); + } + LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD); mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves @@ -202,18 +213,16 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) mFolderRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); // Scroller - { - LLRect scroller_view_rect = getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(params.scroll()); - scroller_params.rect(scroller_view_rect); - mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); - addChild(mScroller); - mScroller->addChild(mFolderRoot); - mFolderRoot->setScrollContainer(mScroller); - mFolderRoot->setFollowsAll(); - mFolderRoot->addChild(mFolderRoot->mStatusTextBox); - } + LLRect scroller_view_rect = getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(params.scroll()); + scroller_params.rect(scroller_view_rect); + mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); + addChild(mScroller); + mScroller->addChild(mFolderRoot); + mFolderRoot->setScrollContainer(mScroller); + mFolderRoot->setFollowsAll(); + mFolderRoot->addChild(mFolderRoot->mStatusTextBox); // Set up the callbacks from the inventory we're viewing, and then build everything. mInventoryObserver = new LLInventoryPanelObserver(this); @@ -228,6 +237,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -258,13 +268,12 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) LLInventoryPanel::~LLInventoryPanel() { - if (mFolderRoot) + gIdleCallbacks.deleteFunction(idle, this); + + U32 sort_order = getFolderViewModel()->getSorter().getSortOrder(); + if (mSortOrderSetting != INHERIT_SORT_ORDER) { - U32 sort_order = mFolderRoot->getSortOrder(); - if (mSortOrderSetting != INHERIT_SORT_ORDER) - { - gSavedSettings.setU32(mSortOrderSetting, sort_order); - } + gSavedSettings.setU32(mSortOrderSetting, sort_order); } gIdleCallbacks.deleteFunction(onIdle, this); @@ -281,7 +290,7 @@ LLInventoryPanel::~LLInventoryPanel() void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) - mFolderRoot->updateSelection(); + updateSelection(); // Nudge the filter if the clipboard state changed if (mClipboardState != LLClipboard::instance().getGeneration()) @@ -293,22 +302,14 @@ void LLInventoryPanel::draw() LLPanel::draw(); } -LLInventoryFilter* LLInventoryPanel::getFilter() +const LLInventoryFilter* LLInventoryPanel::getFilter() const { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); - } - return NULL; + return getFolderViewModel()->getFilter(); } -const LLInventoryFilter* LLInventoryPanel::getFilter() const +LLInventoryFilter* LLInventoryPanel::getFilter() { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); - } - return NULL; + return getFolderViewModel()->getFilter(); } void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) @@ -321,12 +322,12 @@ void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType U32 LLInventoryPanel::getFilterObjectTypes() const { - return mFolderRoot->getFilterObjectTypes(); + return getFilter()->getFilterObjectTypes(); } U32 LLInventoryPanel::getFilterPermMask() const { - return mFolderRoot->getFilterPermissions(); + return getFilter()->getFilterPermissions(); } @@ -347,16 +348,17 @@ void LLInventoryPanel::setFilterSubString(const std::string& string) const std::string LLInventoryPanel::getFilterSubString() { - return mFolderRoot->getFilterSubString(); + return getFilter()->getFilterSubString(); } void LLInventoryPanel::setSortOrder(U32 order) { + LLInventorySort sorter(order); getFilter()->setSortOrder(order); - if (getFilter()->isModified()) + if (order != getFolderViewModel()->getSorter().getSortOrder()) { - mFolderRoot->setSortOrder(order); + getFolderViewModel()->setSorter(LLInventorySort(order)); // try to keep selection onscreen, even if it wasn't to start with mFolderRoot->scrollToShowSelection(); } @@ -364,12 +366,7 @@ void LLInventoryPanel::setSortOrder(U32 order) U32 LLInventoryPanel::getSortOrder() const { - return mFolderRoot->getSortOrder(); -} - -void LLInventoryPanel::requestSort() -{ - mFolderRoot->requestSort(); + return getFolderViewModel()->getSorter().getSortOrder(); } void LLInventoryPanel::setSinceLogoff(BOOL sl) @@ -418,11 +415,20 @@ void LLInventoryPanel::modelChanged(U32 mask) { const LLUUID& item_id = (*items_iter); const LLInventoryObject* model_item = model->getObject(item_id); - LLFolderViewItem* view_item = mFolderRoot->getItemByID(item_id); + LLFolderViewItem* view_item = getItemByID(item_id); + LLFolderViewModelItemInventory* viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); // LLFolderViewFolder is derived from LLFolderViewItem so dynamic_cast from item // to folder is the fast way to get a folder without searching through folders tree. - LLFolderViewFolder* view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + LLFolderViewFolder* view_folder = NULL; + + // Check requires as this item might have already been deleted + // as a child of its deleted parent. + if (model_item && view_item) + { + view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + } ////////////////////////////// // LABEL Operation @@ -433,7 +439,7 @@ void LLInventoryPanel::modelChanged(U32 mask) if (view_item) { // Request refresh on this item (also flags for filtering) - LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getViewModelItem(); if(bridge) { // Clear the display name first, so it gets properly re-built during refresh() bridge->clearDisplayName(); @@ -449,11 +455,14 @@ void LLInventoryPanel::modelChanged(U32 mask) if (mask & LLInventoryObserver::REBUILD) { handled = true; - if (model_item && view_item) + if (model_item && view_item && viewmodel_item) { view_item->destroyView(); + removeItemID(viewmodel_item->getUUID()); } view_item = buildNewViews(item_id); + viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); view_folder = dynamic_cast<LLFolderViewFolder *>(view_item); } @@ -510,23 +519,24 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (model_item && view_item) { // Don't process the item if it is the root - if (view_item->getRoot() != view_item) + if (view_item->getParentFolder()) { - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolderRoot->getItemByID(model_item->getParentUUID()); + LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); // Item has been moved. if (view_item->getParentFolder() != new_parent) { if (new_parent != NULL) { // Item is to be moved and we found its new parent in the panel's directory, so move the item's UI. - view_item->getParentFolder()->extractItem(view_item); - view_item->addToFolder(new_parent, mFolderRoot); + view_item->addToFolder(new_parent); + addItemID(viewmodel_item->getUUID(), view_item); } else { // Item is to be moved outside the panel's directory (e.g. moved to trash for a panel that // doesn't include trash). Just remove the item's UI. view_item->destroyView(); + removeItemID(viewmodel_item->getUUID()); } } } @@ -535,9 +545,10 @@ void LLInventoryPanel::modelChanged(U32 mask) ////////////////////////////// // REMOVE Operation // This item has been removed from memory, but its associated UI element still exists. - else if (!model_item && view_item) + else if (!model_item && view_item && viewmodel_item) { // Remove the item's UI. + removeItemID(viewmodel_item->getUUID()); view_item->destroyView(); } } @@ -549,6 +560,11 @@ LLFolderView* LLInventoryPanel::getRootFolder() return mFolderRoot; } +LLUUID LLInventoryPanel::getRootFolderID() +{ + return static_cast<LLFolderViewModelItemInventory*>(mFolderRoot->getViewModelItem())->getUUID(); +} + // static void LLInventoryPanel::onIdle(void *userdata) @@ -568,16 +584,37 @@ void LLInventoryPanel::onIdle(void *userdata) } } -const LLUUID& LLInventoryPanel::getRootFolderID() const +void LLInventoryPanel::idle(void* user_data) { - return mFolderRoot->getListener()->getUUID(); + LLInventoryPanel* panel = (LLInventoryPanel*)user_data; + panel->mFolderRoot->update(); + // while dragging, update selection rendering to reflect single/multi drag status + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); + if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) + { + panel->mFolderRoot->setShowSingleSelection(TRUE); + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } } + void LLInventoryPanel::initializeViews() { if (!gInventory.isInventoryUsable()) return; - rebuildViewsFor(getRootFolderID()); + buildNewViews(gInventory.getRootFolderID()); + + gIdleCallbacks.addFunction(idle, this); mViewsInitialized = true; @@ -587,14 +624,14 @@ void LLInventoryPanel::initializeViews() if (gAgent.isFirstLogin()) { // Auto open the user's library - LLFolderViewFolder* lib_folder = mFolderRoot->getFolderByID(gInventory.getLibraryRootFolderID()); + LLFolderViewFolder* lib_folder = getFolderByID(gInventory.getLibraryRootFolderID()); if (lib_folder) { lib_folder->setOpen(TRUE); } // Auto close the user's my inventory folder - LLFolderViewFolder* my_inv_folder = mFolderRoot->getFolderByID(gInventory.getRootFolderID()); + LLFolderViewFolder* my_inv_folder = getFolderByID(gInventory.getRootFolderID()); if (my_inv_folder) { my_inv_folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); @@ -602,18 +639,6 @@ void LLInventoryPanel::initializeViews() } } -LLFolderViewItem* LLInventoryPanel::rebuildViewsFor(const LLUUID& id) -{ - // Destroy the old view for this ID so we can rebuild it. - LLFolderViewItem* old_view = mFolderRoot->getItemByID(id); - if (old_view) - { - old_view->destroyView(); - } - - return buildNewViews(id); -} - LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix) { LLRect folder_rect(0, @@ -628,11 +653,12 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u p.rect = folder_rect; p.parent_panel = this; p.tool_tip = p.name; - p.listener = bridge; + p.listener = bridge; + p.view_model = &mInventoryViewModel; p.use_label_suffix = useLabelSuffix; p.allow_multiselect = mAllowMultiSelect; p.show_empty_message = mShowEmptyMessage; - p.show_load_status = mShowLoadStatus; + p.show_item_link_overlays = mShowItemLinkOverlays; return LLUICtrlFactory::create<LLFolderView>(p); } @@ -642,14 +668,6 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br LLFolderViewFolder::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -662,14 +680,6 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -682,79 +692,69 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { LLInventoryObject const* objectp = gInventory.getObject(id); - LLUUID root_id = mFolderRoot->getListener()->getUUID(); - LLFolderViewFolder* parent_folder = NULL; - LLFolderViewItem* itemp = NULL; - if (id == root_id) - { - parent_folder = mFolderRoot; - } - else if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - parent_folder = (LLFolderViewFolder*)mFolderRoot->getItemByID(parent_id); - - if (parent_folder) + if (!objectp) return NULL; + + LLFolderViewItem* folder_view_item = getItemByID(id); + + const LLUUID &parent_id = objectp->getParentUUID(); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); + + if (!folder_view_item && parent_folder) + { + if (objectp->getType() <= LLAssetType::AT_NONE || + objectp->getType() >= LLAssetType::AT_COUNT) { - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { - llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " - << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() - << llendl; - return NULL; - } + llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << llendl; + return NULL; + } - if ((objectp->getType() == LLAssetType::AT_CATEGORY) && - (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), + objectp->getType(), + LLInventoryType::IT_CATEGORY, + this, + &mInventoryViewModel, + mFolderRoot, + objectp->getUUID()); + if (new_listener) { - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - mFolderRoot, - objectp->getUUID()); - if (new_listener) - { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot->getSortOrder()); - } - itemp = folderp; - } + folder_view_item = createFolderViewFolder(new_listener); } - else - { - // Build new view for item. - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - mFolderRoot, - item->getUUID(), - item->getFlags()); + } + else + { + // Build new view for item. + LLInventoryItem* item = (LLInventoryItem*)objectp; + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), + item->getActualType(), + item->getInventoryType(), + this, + &mInventoryViewModel, + mFolderRoot, + item->getUUID(), + item->getFlags()); - if (new_listener) - { - itemp = createFolderViewItem(new_listener); - } + if (new_listener) + { + folder_view_item = createFolderViewItem(new_listener); } + } - if (itemp) - { - itemp->addToFolder(parent_folder, mFolderRoot); - } - } + if (folder_view_item) + { + folder_view_item->addToFolder(parent_folder); + addItemID(id, folder_view_item); + } } // If this is a folder, add the children of the folder and recursively add any // child folders. - if (id.isNull() - || (objectp - && objectp->getType() == LLAssetType::AT_CATEGORY)) + if (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY) { LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; @@ -771,7 +771,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) } } - if(items && parent_folder) + if(items) { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); @@ -784,7 +784,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) mInventory->unlockDirectDescendentArrays(id); } - return itemp; + return folder_view_item; } // bit of a hack to make sure the inventory is open. @@ -795,8 +795,8 @@ void LLInventoryPanel::openStartFolderOrMyInventory() { LLFolderViewFolder *fchild = dynamic_cast<LLFolderViewFolder*>(child); if (fchild - && fchild->getListener() - && fchild->getListener()->getUUID() == gInventory.getRootFolderID()) + && fchild->getViewModelItem() + && fchild->getViewModelItem()->getName() == "My Inventory") { fchild->setOpen(TRUE); break; @@ -813,7 +813,7 @@ void LLInventoryPanel::openSelected() { LLFolderViewItem* folder_item = mFolderRoot->getCurSelectedItem(); if(!folder_item) return; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); if(!bridge) return; bridge->openItem(); } @@ -917,7 +917,7 @@ void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_foc { return; } - mFolderRoot->setSelectionByID(obj_id, take_keyboard_focus); + setSelectionByID(obj_id, take_keyboard_focus); } void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb) @@ -930,7 +930,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std:: void LLInventoryPanel::clearSelection() { - mFolderRoot->clearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action) @@ -939,7 +939,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it mCompletionObserver->reset(); for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(); it != items.end(); ++it) { - LLUUID id = (*it)->getListener()->getUUID(); + LLUUID id = static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID(); LLViewerInventoryItem* inv_item = mInventory->getItem(id); if (inv_item && !inv_item->isFinished()) @@ -959,19 +959,14 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it } } -void LLInventoryPanel::doToSelected(const LLSD& userdata) -{ - mFolderRoot->doToSelected(&gInventory, userdata); -} - void LLInventoryPanel::doCreate(const LLSD& userdata) { - menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata); + menu_create_inventory_item(this, LLFolderBridge::sSelf.get(), userdata); } bool LLInventoryPanel::beginIMSession() { - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); std::string name; static int session_num = 1; @@ -979,20 +974,19 @@ bool LLInventoryPanel::beginIMSession() LLDynamicArray<LLUUID> members; EInstantMessage type = IM_SESSION_CONFERENCE_START; - std::set<LLUUID>::const_iterator iter; + std::set<LLFolderViewItem*>::const_iterator iter; for (iter = selected_items.begin(); iter != selected_items.end(); iter++) { - LLUUID item = *iter; - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); + LLFolderViewItem* folder_item = (*iter); if(folder_item) { - LLFolderViewEventListener* fve_listener = folder_item->getListener(); + LLFolderViewModelItemInventory* fve_listener = static_cast<LLFolderViewModelItemInventory*>(folder_item->getViewModelItem()); if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY)) { - LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener(); + LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getViewModelItem(); if(!bridge) return true; LLViewerInventoryCategory* cat = bridge->getCategory(); if(!cat) return true; @@ -1026,9 +1020,7 @@ bool LLInventoryPanel::beginIMSession() } else { - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); - if(!folder_item) return true; - LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getViewModelItem(); if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD) { @@ -1069,13 +1061,13 @@ bool LLInventoryPanel::beginIMSession() bool LLInventoryPanel::attachObject(const LLSD& userdata) { // Copy selected item UUIDs to a vector. - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); uuid_vec_t items; - for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin(); + for (std::set<LLFolderViewItem*>::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - items.push_back(*set_iter); + items.push_back(static_cast<LLFolderViewModelItemInventory*>((*set_iter)->getViewModelItem())->getUUID()); } // Attach selected items. @@ -1222,6 +1214,87 @@ BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) co return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); } +void LLInventoryPanel::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLInventoryPanel::removeItemID(const LLUUID& id) +{ + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(id, categories, items, TRUE); + + mItemMap.erase(id); + + for (LLInventoryModel::cat_array_t::iterator it = categories.begin(), end_it = categories.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } + + for (LLInventoryModel::item_array_t::iterator it = items.begin(), end_it = items.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } +} + +LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); +LLFolderViewItem* LLInventoryPanel::getItemByID(const LLUUID& id) +{ + LLFastTimer _(FTM_GET_ITEM_BY_ID); + + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) +{ + LLFolderViewItem* item = getItemByID(id); + return dynamic_cast<LLFolderViewFolder*>(item); +} + + +void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) +{ + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getViewModelItem()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + } +} + +void LLInventoryPanel::updateSelection() +{ + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +} + +void LLInventoryPanel::doToSelected(const LLSD& userdata) +{ + LLInventoryAction::doToSelected(mInventory, mFolderRoot, userdata.asString()); + + return; +} + /************************************************************************/ /* Recent Inventory Panel related class */ diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 6db59afb9b..910325cdbc 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -30,6 +30,8 @@ #include "llassetstorage.h" #include "lldarray.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodelinventory.h" #include "llfloater.h" #include "llinventory.h" #include "llinventoryfilter.h" @@ -38,22 +40,10 @@ #include "lluictrlfactory.h" #include <set> -class LLFolderView; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryFilter; -class LLInventoryModel; class LLInvFVBridge; class LLInventoryFVBridgeBuilder; -class LLMenuBarGL; -class LLCheckBoxCtrl; -class LLSpinCtrl; -class LLTextBox; -class LLIconCtrl; -class LLSaveFolderState; -class LLFilterEditor; -class LLTabContainer; class LLInvPanelComplObserver; +class LLFolderViewModelInventory; class LLInventoryPanel : public LLPanel { @@ -85,7 +75,6 @@ public: Optional<std::string> start_folder; Optional<bool> use_label_suffix; Optional<bool> show_empty_message; - Optional<bool> show_load_status; Optional<LLScrollContainer::Params> scroll; Optional<bool> accepts_drag_and_drop; @@ -98,7 +87,6 @@ public: start_folder("start_folder"), use_label_suffix("use_label_suffix", true), show_empty_message("show_empty_message", true), - show_load_status("show_load_status"), scroll("scroll"), accepts_drag_and_drop("accepts_drag_and_drop") {} @@ -116,6 +104,7 @@ public: public: LLInventoryModel* getModel() { return mInventory; } + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } // LLView methods void draw(); @@ -156,6 +145,7 @@ public: // This method is called when something has changed about the inventory. void modelChanged(U32 mask); LLFolderView* getRootFolder(); + LLUUID getRootFolderID(); LLScrollContainer* getScrollableContainer() { return mScroller; } void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); @@ -167,7 +157,8 @@ public: void doCreate(const LLSD& userdata); bool beginIMSession(); bool attachObject(const LLSD& userdata); - + static void idle(void* user_data); + // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); @@ -182,10 +173,21 @@ public: static void openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id); + void addItemID(const LLUUID& id, LLFolderViewItem* itemp); + void removeItemID(const LLUUID& id); + LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + void updateSelection(); + + LLFolderViewModelInventory* getFolderViewModel(); + const LLFolderViewModelInventory* getFolderViewModel() const; + protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete + LLUUID mSelectThisID; LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; LLInvPanelComplObserver* mCompletionObserver; @@ -193,11 +195,13 @@ protected: BOOL mAllowMultiSelect; BOOL mShowItemLinkOverlays; // Shows link graphic over inventory item icons BOOL mShowEmptyMessage; - BOOL mShowLoadStatus; LLFolderView* mFolderRoot; LLScrollContainer* mScroller; + LLFolderViewModelInventory mInventoryViewModel; + + std::map<LLUUID, LLFolderViewItem*> mItemMap; /** * Pointer to LLInventoryFVBridgeBuilder. * @@ -218,7 +222,6 @@ public: void setSortOrder(U32 order); U32 getSortOrder() const; - void requestSort(); private: std::string mSortOrderSetting; @@ -231,12 +234,10 @@ public: void addHideFolderType(LLFolderType::EType folder_type); public: - BOOL getIsViewsInitialized() const { return mViewsInitialized; } - const LLUUID& getRootFolderID() const; + BOOL getIsViewsInitialized() const { return mViewsInitialized; } protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); - LLFolderViewItem* rebuildViewsFor(const LLUUID& id); // Given the id and the parent, build all of the folder views. virtual void buildFolderView(const LLInventoryPanel::Params& params); LLFolderViewItem* buildNewViews(const LLUUID& id); diff --git a/indra/newview/lllistcontextmenu.h b/indra/newview/lllistcontextmenu.h index fabd68ee20..04d3314829 100644 --- a/indra/newview/lllistcontextmenu.h +++ b/indra/newview/lllistcontextmenu.h @@ -37,7 +37,7 @@ class LLContextMenu; /** * Context menu for single or multiple list items. * - * Derived classes must implement contextMenu(). + * Derived classes must implement createMenu(). * * Typical usage: * <code> diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a7303ad035..4e53082c05 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -1,8 +1,8 @@ /** * @file LLNearbyChat.cpp - * @brief Nearby chat history scrolling panel implementation + * @brief LLNearbyChat class implementation * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -26,68 +26,67 @@ #include "llviewerprecompiledheaders.h" -#include "llnearbychat.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llrootview.h" -//#include "llchatitemscontainerctrl.h" +#include "message.h" + #include "lliconctrl.h" +#include "llappviewer.h" +#include "llchatentry.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llimfloatercontainer.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" #include "llresizebar.h" #include "llresizehandle.h" +#include "lldraghandle.h" #include "llmenugl.h" -#include "llviewermenu.h"//for gMenuHolder - +#include "llviewermenu.h" // for gMenuHolder #include "llnearbychathandler.h" #include "llchannelmanager.h" - -#include "llagent.h" // gAgent #include "llchathistory.h" #include "llstylemap.h" - #include "llavatarnamecache.h" - -#include "lldraghandle.h" - -#include "llnearbychatbar.h" #include "llfloaterreg.h" #include "lltrans.h" -static const S32 RESIZE_BAR_THICKNESS = 3; - - -static LLRegisterPanelClassWrapper<LLNearbyChat> t_panel_nearby_chat("panel_nearby_chat"); +#include "llfirstuse.h" +#include "llnearbychat.h" +#include "llagent.h" // gAgent +#include "llgesturemgr.h" +#include "llmultigesture.h" +#include "llkeyboard.h" +#include "llanimationstates.h" +#include "llviewerstats.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" +#include "llviewerchat.h" +#include "lltranslate.h" -LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) -: LLPanel(p), - mChatHistory(NULL) -{ -} +S32 LLNearbyChat::sLastSpecialChatChannel = 0; -BOOL LLNearbyChat::postBuild() +// --- 2 functions in the global namespace :( --- +bool isWordsName(const std::string& name) { - //menu - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); - registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); - - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(menu) - mPopupMenuHandle = menu->getHandle(); - - gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true); - - mChatHistory = getChild<LLChatHistory>("chat_history"); + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); - if(!LLPanel::postBuild()) - return false; - - return true; + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } } std::string appendTime() @@ -106,52 +105,106 @@ std::string appendTime() } -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +const S32 EXPANDED_HEIGHT = 266; +const S32 COLLAPSED_HEIGHT = 60; +const S32 EXPANDED_MIN_HEIGHT = 150; + +// legacy callback glue +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); + +struct LLChatTypeTrigger { + std::string name; + EChatType type; +}; + +static LLChatTypeTrigger sChatTypeTriggers[] = { + { "/whisper" , CHAT_TYPE_WHISPER}, + { "/shout" , CHAT_TYPE_SHOUT} +}; + + +LLNearbyChat::LLNearbyChat(const LLSD& key) +: LLIMConversation(key), + mChatBox(NULL), + mChatHistory(NULL), + //mOutputMonitor(NULL), + mSpeakerMgr(NULL), + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) { - LLChat& tmp_chat = const_cast<LLChat&>(chat); + setIsChrome(TRUE); + mKey = LLSD(); + mIsNearbyChat = true; + mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); +} - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); +//virtual +BOOL LLNearbyChat::postBuild() +{ + mChatBox = getChild<LLChatEntry>("chat_editor"); - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); - - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args = args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - mChatHistory->appendMessage(chat, chat_args); - } + mChatBox->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); + mChatBox->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); + mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); + mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); + mChatBox->setCommitOnFocusLost( FALSE ); + mChatBox->setPassDelete(TRUE); + mChatBox->setFont(LLViewerChat::getChatFont()); - if(archive) +// mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); +// mOutputMonitor->setVisible(FALSE); + + // Register for font change notifications + LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChat::onChatFontChange, this, _1)); + + enableResizeCtrls(true, true, false); + + // title must be defined BEFORE call addConversationListItem() because + // it is used for show the item's name in the conversations list + setTitle(getString("NearbyChatTitle")); + + addToHost(); + + //for menu + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); + registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(menu) { - mMessageArchive.push_back(chat); - if(mMessageArchive.size()>200) - mMessageArchive.erase(mMessageArchive.begin()); + mPopupMenuHandle = menu->getHandle(); } - if (args["do_not_log"].asBoolean()) + // obsolete, but may be needed for backward compatibility? + gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); + + mChatHistory = getChild<LLChatHistory>("chat_history"); + if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) { - return; + loadHistory(); } - if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) - { - std::string from_name = chat.mFromName; + // added row in the conversations list when nearby chat is tear-off + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + im_box->addConversationListItem(getTitle(), LLSD(), this); - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // if the chat is coming from an agent, log the complete name - LLAvatarName av_name; - LLAvatarNameCache::get(chat.mFromID, &av_name); + return LLIMConversation::postBuild(); +} - if (!av_name.mIsDisplayNameDefault) - { - from_name = av_name.getCompleteName(); - } - } +// virtual +void LLNearbyChat::refresh() +{ + displaySpeakingIndicator(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); - LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + // *HACK: Update transparency type depending on whether our children have focus. + // This is needed because this floater is chrome and thus cannot accept focus, so + // the transparency type setting code from LLFloater::setFocus() isn't reached. + if (getTransparencyType() != TT_DEFAULT) + { + setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); } } @@ -162,44 +215,51 @@ void LLNearbyChat::onNearbySpeakers() LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); } - void LLNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) { } + bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) { std::string str = userdata.asString(); if(str == "nearby_people") - onNearbySpeakers(); + onNearbySpeakers(); return false; } -void LLNearbyChat::removeScreenChat() +void LLNearbyChat::getAllowedRect(LLRect& rect) { - LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); - if(chat_channel) - { - chat_channel->removeToastsFromChannel(); - } + rect = gViewerWindow->getWorldViewRectScaled(); } - -void LLNearbyChat::setVisible(BOOL visible) +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusReceived() { - if(visible) - { - removeScreenChat(); - } - - LLPanel::setVisible(visible); + setBackgroundOpaque(true); + LLIMConversation::onFocusReceived(); } +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusLost() +{ + setBackgroundOpaque(false); + LLIMConversation::onFocusLost(); +} -void LLNearbyChat::getAllowedRect(LLRect& rect) +BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) { - rect = gViewerWindow->getWorldViewRectScaled(); + //fix for EXT-6625 + //highlight NearbyChat history whenever mouseclick happen in NearbyChat + //setting focus to eidtor will force onFocusLost() call that in its turn will change + //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + + if(mChatHistory) + mChatHistory->setFocus(TRUE); + return LLPanel::handleMouseDown(x, y, mask); } -void LLNearbyChat::updateChatHistoryStyle() +void LLNearbyChat::reloadMessages() { mChatHistory->clear(); @@ -212,34 +272,6 @@ void LLNearbyChat::updateChatHistoryStyle() } } -//static -void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); - if(nearby_chat) - nearby_chat->updateChatHistoryStyle(); -} - -bool isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - void LLNearbyChat::loadHistory() { LLSD do_not_log; @@ -274,9 +306,9 @@ void LLNearbyChat::loadHistory() chat.mSourceType = CHAT_SOURCE_AGENT; if (from_id.isNull() && SYSTEM_FROM == from) - { + { chat.mSourceType = CHAT_SOURCE_SYSTEM; - + } else if (from_id.isNull()) { @@ -289,50 +321,623 @@ void LLNearbyChat::loadHistory() } } +void LLNearbyChat::removeScreenChat() +{ + LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + if(chat_channel) + { + chat_channel->removeToastsFromChannel(); + } +} + +void LLNearbyChat::setVisible(BOOL visible) +{ + if(visible) + { + removeScreenChat(); + } + + LLIMConversation::setVisible(visible); +} + + +void LLNearbyChat::enableDisableCallBtn() +{ + // bool btn_enabled = LLAgent::isActionAllowed("speak"); + + getChildView("voice_call_btn")->setEnabled(false /*btn_enabled*/); +} + +void LLNearbyChat::onTearOffClicked() +{ + LLIMConversation::onTearOffClicked(); + + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + + // see CHUI-170: Save torn-off state of the nearby chat between sessions + BOOL in_the_multifloater = (getHost() == im_box); + gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); +} + +void LLNearbyChat::addToHost() +{ + if ( LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + if (im_box) + { + if (gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) + { + im_box->addFloater(this, TRUE, LLTabContainer::END); + } + else + { + // setting of the "potential" host: this sequence sets + // LLFloater::mHostHandle = NULL (a current host), but + // LLFloater::mLastHostHandle = im_box (a "future" host) + setHost(im_box); + setHost(NULL); + } + } + } +} + +// virtual +void LLNearbyChat::onOpen(const LLSD& key) +{ + LLIMConversation::onOpen(key); + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); +} + +bool LLNearbyChat::applyRectControl() +{ + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); + return LLFloater::applyRectControl(); +} + +void LLNearbyChat::onChatFontChange(LLFontGL* fontp) +{ + // Update things with the new font whohoo + if (mChatBox) + { + mChatBox->setFont(fontp); + } +} + //static LLNearbyChat* LLNearbyChat::getInstance() { - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - return chat_bar->findChild<LLNearbyChat>("nearby_chat"); + return LLFloaterReg::getTypedInstance<LLNearbyChat>("chat_bar"); } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusReceived() +void LLNearbyChat::show() { - setBackgroundOpaque(true); - LLPanel::onFocusReceived(); + if (isChatMultiTab()) + { + openFloater(getKey()); + } + setVisible(TRUE); } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusLost() +void LLNearbyChat::showHistory() { - setBackgroundOpaque(false); - LLPanel::onFocusLost(); + openFloater(); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); + + bool is_torn_off = getHost() == NULL; + + // Reshape and enable resize controls only if it's a torn off floater. + // Otherwise all the size changes should be handled by LLIMFloaterContainer. + if (is_torn_off) + { + reshape(getRect().getWidth(), mExpandedHeight); + enableResizeCtrls(true); + storeRectControl(); + } } -BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) +std::string LLNearbyChat::getCurrentChat() { - //fix for EXT-6625 - //highlight NearbyChat history whenever mouseclick happen in NearbyChat - //setting focus to eidtor will force onFocusLost() call that in its turn will change - //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + return mChatBox ? mChatBox->getText() : LLStringUtil::null; +} + +// virtual +BOOL LLNearbyChat::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + + if( KEY_RETURN == key && mask == MASK_CONTROL) + { + // shout + sendChat(CHAT_TYPE_SHOUT); + handled = TRUE; + } + + return handled; +} + +BOOL LLNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - if(mChatHistory) - mChatHistory->setFocus(TRUE); - return LLPanel::handleMouseDown(x, y, mask); + bool string_was_found = false; + + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) + { + std::string trigger_trunc = sChatTypeTriggers[n].name; + LLStringUtil::truncate(trigger_trunc, in_len); + + if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + { + *out_str = sChatTypeTriggers[n].name; + string_was_found = true; + } + } + } + + return string_was_found; } -void LLNearbyChat::draw() +void LLNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata) { - // *HACK: Update transparency type depending on whether our children have focus. - // This is needed because this floater is chrome and thus cannot accept focus, so - // the transparency type setting code from LLFloater::setFocus() isn't reached. - if (getTransparencyType() != TT_DEFAULT) + LLFirstUse::otherAvatarChatFirst(false); + + LLNearbyChat* self = (LLNearbyChat *)userdata; + + LLWString raw_text = self->mChatBox->getWText(); + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + + S32 length = raw_text.length(); + + if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences { - setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); + gAgent.startTyping(); + } + else + { + gAgent.stopTyping(); + } + + /* Doesn't work -- can't tell the difference between a backspace + that killed the selection vs. backspace at the end of line. + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + self->mInputEditor->setText( new_text ); + self->mInputEditor->setCursorToEnd(); + length = length - 1; + } + */ + + KEY key = gKeyboard->currentKey(); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + self->mChatBox->selectNext(rest_of_match, false); + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + self->mChatBox->endOfDoc(); + } + + //llinfos << "GESTUREDEBUG " << trigger + // << " len " << length + // << " outlen " << out_str.getLength() + // << llendl; + } +} + +// static +void LLNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) +{ + // stop typing animation + gAgent.stopTyping(); +} + +void LLNearbyChat::onChatBoxFocusReceived() +{ + mChatBox->setEnabled(!gDisconnected); +} + +EChatType LLNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) +{ + U32 length = str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (length >= sChatTypeTriggers[n].name.length()) + { + std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); + + if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) + { + U32 trigger_length = sChatTypeTriggers[n].name.length(); + + // It's to remove space after trigger name + if (length > trigger_length && str[trigger_length] == ' ') + trigger_length++; + + str = str.substr(trigger_length, length); + + if (CHAT_TYPE_NORMAL == type) + return sChatTypeTriggers[n].type; + else + break; + } + } + } + + return type; +} + +void LLNearbyChat::sendChat( EChatType type ) +{ + if (mChatBox) + { + LLWString text = mChatBox->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if (!text.empty()) + { + // Check if this is destined for another channel + S32 channel = 0; + stripChannelNumber(text, &channel); + + std::string utf8text = wstring_to_utf8str(text); + // Try to trigger a gesture, if not chat to a script. + std::string utf8_revised_text; + if (0 == channel) + { + // discard returned "found" boolean + LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + } + else + { + utf8_revised_text = utf8text; + } + + utf8_revised_text = utf8str_trim(utf8_revised_text); + + type = processChatTypeTriggers(type, utf8_revised_text); + + if (!utf8_revised_text.empty()) + { + // Chat with animation + sendChatFromViewer(utf8_revised_text, type, TRUE); + } + } + + mChatBox->setText(LLStringExplicit("")); + } + + gAgent.stopTyping(); + + // If the user wants to stop chatting on hitting return, lose focus + // and go out of chat mode. + if (gSavedSettings.getBOOL("CloseChatOnReturn")) + { + stopChat(); + } +} + + +void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) +{ + LLChat& tmp_chat = const_cast<LLChat&>(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = args; + chat_args["use_plain_text_chat_history"] = + gSavedSettings.getBOOL("PlainTextChatHistory"); + chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); + chat_args["show_names_for_p2p_conv"] = true; + + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); + + if(archive) + { + mMessageArchive.push_back(chat); + if(mMessageArchive.size()>200) + mMessageArchive.erase(mMessageArchive.begin()); + } + + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + { + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + } +} + + +void LLNearbyChat::onChatBoxCommit() +{ + if (mChatBox->getText().length() > 0) + { + sendChat(CHAT_TYPE_NORMAL); + } + + gAgent.stopTyping(); +} + +void LLNearbyChat::displaySpeakingIndicator() +{ + LLSpeakerMgr::speaker_list_t speaker_list; + LLUUID id; + + id.setNull(); + mSpeakerMgr->update(TRUE); + mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); + + for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) + { + LLPointer<LLSpeaker> s = *i; + if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) + { + id = s->mID; + break; + } + } + + if (!id.isNull()) + { + //mOutputMonitor->setVisible(TRUE); + //mOutputMonitor->setSpeakerId(id); + } + else + { + //mOutputMonitor->setVisible(FALSE); + } +} + +void LLNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) +{ + sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); +} + +void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) +{ + // Look for "/20 foo" channel chats. + S32 channel = 0; + LLWString out_text = stripChannelNumber(wtext, &channel); + std::string utf8_out_text = wstring_to_utf8str(out_text); + std::string utf8_text = wstring_to_utf8str(wtext); + + utf8_text = utf8str_trim(utf8_text); + if (!utf8_text.empty()) + { + utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); + } + + // Don't animate for chats people can't hear (chat to scripts) + if (animate && (channel == 0)) + { + if (type == CHAT_TYPE_WHISPER) + { + lldebugs << "You whisper " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_NORMAL) + { + lldebugs << "You say " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_SHOUT) + { + lldebugs << "You shout " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); + } + else + { + llinfos << "send_chat_from_viewer() - invalid volume" << llendl; + return; + } + } + else + { + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) + { + lldebugs << "Channel chat: " << utf8_text << llendl; + } + } + + send_chat_from_viewer(utf8_out_text, type, channel); +} + +// static +void LLNearbyChat::startChat(const char* line) +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); + + if (cb ) + { + cb->show(); + cb->setVisible(TRUE); + cb->setFocus(TRUE); + cb->mChatBox->setFocus(TRUE); + + if (line) + { + std::string line_string(line); + cb->mChatBox->setText(line_string); + } + + cb->mChatBox->endOfDoc(); } +} + +// Exit "chat mode" and do the appropriate focus changes +// static +void LLNearbyChat::stopChat() +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); - LLPanel::draw(); + if (cb) + { + cb->mChatBox->setFocus(FALSE); + + // stop typing animation + gAgent.stopTyping(); + } } + +// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. +// Otherwise returns input and channel 0. +LLWString LLNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) +{ + if (mesg[0] == '/' + && mesg[1] == '/') + { + // This is a "repeat channel send" + *channel = sLastSpecialChatChannel; + return mesg.substr(2, mesg.length() - 2); + } + else if (mesg[0] == '/' + && mesg[1] + && LLStringOps::isDigit(mesg[1])) + { + // This a special "/20" speak on a channel + S32 pos = 0; + + // Copy the channel number into a string + LLWString channel_string; + llwchar c; + do + { + c = mesg[pos+1]; + channel_string.push_back(c); + pos++; + } + while(c && pos < 64 && LLStringOps::isDigit(c)); + + // Move the pointer forward to the first non-whitespace char + // Check isspace before looping, so we can handle "/33foo" + // as well as "/33 foo" + while(c && iswspace(c)) + { + c = mesg[pos+1]; + pos++; + } + + sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); + *channel = sLastSpecialChatChannel; + return mesg.substr(pos, mesg.length() - pos); + } + else + { + // This is normal chat. + *channel = 0; + return mesg; + } +} + +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + gAgent.sendReliableMessage(); + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); +} + +class LLChatCommandHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } + + // Your code here + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + bool retval = false; + // Need at least 2 tokens to have a valid message. + if (tokens.size() < 2) + { + retval = false; + } + else + { + S32 channel = tokens[0].asInteger(); + // VWR-19499 Restrict function to chat channels greater than 0. + if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) + { + retval = true; + // Send unescaped message, see EXT-6353. + std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); + send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); + } + else + { + retval = false; + // Tell us this is an unsupported SLurl. + } + } + return retval; + } +}; + +// Creating the object registers with the dispatcher. +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7c5975cbc5..90feb71488 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -1,8 +1,8 @@ - /** +/** * @file llnearbychat.h - * @brief nearby chat history scrolling panel implementation + * @brief LLNearbyChat class definition * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -24,60 +24,115 @@ * $/LicenseInfo$ */ -#ifndef LL_LLNEARBYCHAT_H_ -#define LL_LLNEARBYCHAT_H_ +#ifndef LL_LLNEARBYCHAT_H +#define LL_LLNEARBYCHAT_H +#include "llimconversation.h" +#include "llcombobox.h" +#include "llgesturemgr.h" +#include "llchat.h" +#include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" +#include "llspeakers.h" #include "llscrollbar.h" #include "llviewerchat.h" -#include "llfloater.h" +#include "llpanel.h" class LLResizeBar; class LLChatHistory; -class LLNearbyChat: public LLPanel +class LLNearbyChat + : public LLIMConversation { public: - LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); + // constructor for inline chat-bars (e.g. hosted in chat history window) + LLNearbyChat(const LLSD& key); + ~LLNearbyChat() {} - BOOL postBuild (); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + + // focus overrides + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + /*virtual*/ void setVisible(BOOL visible); + + void loadHistory(); + void reloadMessages(); + void removeScreenChat(); + + static LLNearbyChat* getInstance(); + + void addToHost(); + void show(); /** @param archive true - to save a message to the chat history log */ - void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); + void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); void onNearbyChatContextMenuItemClicked(const LLSD& userdata); bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); + LLChatEntry* getChatBox() { return mChatBox; } + + std::string getCurrentChat(); + + virtual BOOL handleKeyHere( KEY key, MASK mask ); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual void draw(); - // focus overrides - /*virtual*/ void onFocusLost(); - /*virtual*/ void onFocusReceived(); - - /*virtual*/ void setVisible(BOOL visible); - - virtual void updateChatHistoryStyle(); + static void startChat(const char* line); + static void stopChat(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); + static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - void loadHistory(); + void showHistory(); - static LLNearbyChat* getInstance(); - void removeScreenChat(); +protected: + static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata); + static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); + void onChatBoxFocusReceived(); + + void sendChat( EChatType type ); + void onChatBoxCommit(); + void onChatFontChange(LLFontGL* fontp); + + /* virtual */ bool applyRectControl(); + + void onToggleNearbyChatPanel(); + + /*virtual*/ void onTearOffClicked(); + + static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); + EChatType processChatTypeTriggers(EChatType type, std::string &str); + + void displaySpeakingIndicator(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + // Which non-zero channel did we last chat on? + static S32 sLastSpecialChatChannel; + + LLChatEntry* mChatBox; + LLOutputMonitorCtrl* mOutputMonitor; + LLLocalSpeakerMgr* mSpeakerMgr; + + S32 mExpandedHeight; private: void getAllowedRect (LLRect& rect); - + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); void onNearbySpeakers (); + /*virtual*/ void refresh(); -private: LLHandle<LLView> mPopupMenuHandle; + std::vector<LLChat> mMessageArchive; LLChatHistory* mChatHistory; - std::vector<LLChat> mMessageArchive; }; #endif - - diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp deleted file mode 100644 index f8f0f7d243..0000000000 --- a/indra/newview/llnearbychatbar.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/** - * @file llnearbychatbar.cpp - * @brief LLNearbyChatBar class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "message.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "lltrans.h" - -#include "llfirstuse.h" -#include "llnearbychatbar.h" -#include "llnearbychatbarlistener.h" -#include "llagent.h" -#include "llgesturemgr.h" -#include "llmultigesture.h" -#include "llkeyboard.h" -#include "llanimationstates.h" -#include "llviewerstats.h" -#include "llcommandhandler.h" -#include "llviewercontrol.h" -#include "llnavigationbar.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llrootview.h" -#include "llviewerchat.h" -#include "llnearbychat.h" -#include "lltranslate.h" - -#include "llresizehandle.h" -#include "llautoreplace.h" - -S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; - -const S32 EXPANDED_HEIGHT = 300; -const S32 COLLAPSED_HEIGHT = 60; -const S32 EXPANDED_MIN_HEIGHT = 150; - -// legacy callback glue -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); - -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - -LLNearbyChatBar::LLNearbyChatBar(const LLSD& key) -: LLFloater(key), - mChatBox(NULL), - mNearbyChat(NULL), - mOutputMonitor(NULL), - mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) -{ - mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); - mListener.reset(new LLNearbyChatBarListener(*this)); -} - -//virtual -BOOL LLNearbyChatBar::postBuild() -{ - mChatBox = getChild<LLLineEditor>("chat_box"); - - mChatBox->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); - mChatBox->setCommitCallback(boost::bind(&LLNearbyChatBar::onChatBoxCommit, this)); - mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this); - mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); - mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChatBar::onChatBoxFocusReceived, this)); - - mChatBox->setIgnoreArrowKeys( FALSE ); - mChatBox->setCommitOnFocusLost( FALSE ); - mChatBox->setRevertOnEsc( FALSE ); - mChatBox->setIgnoreTab(TRUE); - mChatBox->setPassDelete(TRUE); - mChatBox->setReplaceNewlinesWithSpaces(FALSE); - mChatBox->setEnableLineHistory(TRUE); - mChatBox->setFont(LLViewerChat::getChatFont()); - - mNearbyChat = getChildView("nearby_chat"); - - gSavedSettings.declareBOOL("nearbychat_history_visibility", mNearbyChat->getVisible(), "Visibility state of nearby chat history", TRUE); - BOOL show_nearby_chat = gSavedSettings.getBOOL("nearbychat_history_visibility"); - - LLButton* show_btn = getChild<LLButton>("show_nearby_chat"); - show_btn->setCommitCallback(boost::bind(&LLNearbyChatBar::onToggleNearbyChatPanel, this)); - show_btn->setToggleState(show_nearby_chat); - - mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); - mOutputMonitor->setVisible(FALSE); - - showNearbyChatPanel(show_nearby_chat); - - // Register for font change notifications - LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChatBar::onChatFontChange, this, _1)); - - enableResizeCtrls(true, true, false); - - return TRUE; -} - -// virtual -void LLNearbyChatBar::onOpen(const LLSD& key) -{ - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); -} - -bool LLNearbyChatBar::applyRectControl() -{ - bool rect_controlled = LLFloater::applyRectControl(); - - if (!mNearbyChat->getVisible()) - { - reshape(getRect().getWidth(), getMinHeight()); - enableResizeCtrls(true, true, false); - } - else - { - enableResizeCtrls(true); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - } - - return rect_controlled; -} - -void LLNearbyChatBar::onChatFontChange(LLFontGL* fontp) -{ - // Update things with the new font whohoo - if (mChatBox) - { - mChatBox->setFont(fontp); - } -} - -//static -LLNearbyChatBar* LLNearbyChatBar::getInstance() -{ - return LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar"); -} - -void LLNearbyChatBar::showHistory() -{ - openFloater(); - - if (!getChildView("nearby_chat")->getVisible()) - { - onToggleNearbyChatPanel(); - } -} - -void LLNearbyChatBar::showTranslationCheckbox(BOOL show) -{ - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(show); -} - -void LLNearbyChatBar::draw() -{ - displaySpeakingIndicator(); - LLFloater::draw(); -} - -std::string LLNearbyChatBar::getCurrentChat() -{ - return mChatBox ? mChatBox->getText() : LLStringUtil::null; -} - -// virtual -BOOL LLNearbyChatBar::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - if( KEY_RETURN == key && mask == MASK_CONTROL) - { - // shout - sendChat(CHAT_TYPE_SHOUT); - handled = TRUE; - } - - return handled; -} - -BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (in_len > sChatTypeTriggers[n].name.length()) - continue; - - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); - - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) - { - *out_str = sChatTypeTriggers[n].name; - return TRUE; - } - } - - return FALSE; -} - -void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) -{ - LLFirstUse::otherAvatarChatFirst(false); - - LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; - - LLWString raw_text = self->mChatBox->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - - S32 length = raw_text.length(); - - if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - { - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - /* Doesn't work -- can't tell the difference between a backspace - that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - self->mInputEditor->setText( new_text ); - self->mInputEditor->setCursorToEnd(); - length = length - 1; - } - */ - - KEY key = gKeyboard->currentKey(); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - S32 outlength = self->mChatBox->getLength(); // in characters - - // Select to end of line, starting from the character - // after the last one the user typed. - self->mChatBox->setSelection(length, outlength); - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - self->mChatBox->setCursorToEnd(); - } - - //llinfos << "GESTUREDEBUG " << trigger - // << " len " << length - // << " outlen " << out_str.getLength() - // << llendl; - } -} - -// static -void LLNearbyChatBar::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) -{ - // stop typing animation - gAgent.stopTyping(); -} - -void LLNearbyChatBar::onChatBoxFocusReceived() -{ - mChatBox->setEnabled(!gDisconnected); -} - -EChatType LLNearbyChatBar::processChatTypeTriggers(EChatType type, std::string &str) -{ - U32 length = str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (length >= sChatTypeTriggers[n].name.length()) - { - std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); - - if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) - { - U32 trigger_length = sChatTypeTriggers[n].name.length(); - - // It's to remove space after trigger name - if (length > trigger_length && str[trigger_length] == ' ') - trigger_length++; - - str = str.substr(trigger_length, length); - - if (CHAT_TYPE_NORMAL == type) - return sChatTypeTriggers[n].type; - else - break; - } - } - } - - return type; -} - -void LLNearbyChatBar::sendChat( EChatType type ) -{ - if (mChatBox) - { - LLWString text = mChatBox->getConvertedText(); - if (!text.empty()) - { - // store sent line in history, duplicates will get filtered - mChatBox->updateHistory(); - // Check if this is destined for another channel - S32 channel = 0; - stripChannelNumber(text, &channel); - - std::string utf8text = wstring_to_utf8str(text); - // Try to trigger a gesture, if not chat to a script. - std::string utf8_revised_text; - if (0 == channel) - { - // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); - } - else - { - utf8_revised_text = utf8text; - } - - utf8_revised_text = utf8str_trim(utf8_revised_text); - - type = processChatTypeTriggers(type, utf8_revised_text); - - if (!utf8_revised_text.empty()) - { - // Chat with animation - sendChatFromViewer(utf8_revised_text, type, TRUE); - } - } - - mChatBox->setText(LLStringExplicit("")); - } - - gAgent.stopTyping(); - - // If the user wants to stop chatting on hitting return, lose focus - // and go out of chat mode. - if (gSavedSettings.getBOOL("CloseChatOnReturn")) - { - stopChat(); - } -} - -void LLNearbyChatBar::showNearbyChatPanel(bool show) -{ - if (!show) - { - if (mNearbyChat->getVisible() && !isMinimized()) - { - mExpandedHeight = getRect().getHeight(); - } - setResizeLimits(getMinWidth(), COLLAPSED_HEIGHT); - mNearbyChat->setVisible(FALSE); - reshape(getRect().getWidth(), COLLAPSED_HEIGHT); - enableResizeCtrls(true, true, false); - storeRectControl(); - } - else - { - mNearbyChat->setVisible(TRUE); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - reshape(getRect().getWidth(), mExpandedHeight); - enableResizeCtrls(true); - storeRectControl(); - } - - gSavedSettings.setBOOL("nearbychat_history_visibility", mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::onToggleNearbyChatPanel() -{ - showNearbyChatPanel(!mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::setMinimized(BOOL b) -{ - LLNearbyChat* nearby_chat = getChild<LLNearbyChat>("nearby_chat"); - // when unminimizing with nearby chat visible, go ahead and kill off screen chats - if (!b && nearby_chat->getVisible()) - { - nearby_chat->removeScreenChat(); - } - LLFloater::setMinimized(b); -} - -void LLNearbyChatBar::onChatBoxCommit() -{ - if (mChatBox->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } - - gAgent.stopTyping(); -} - -void LLNearbyChatBar::displaySpeakingIndicator() -{ - LLSpeakerMgr::speaker_list_t speaker_list; - LLUUID id; - - id.setNull(); - mSpeakerMgr->update(TRUE); - mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); - - for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) - { - LLPointer<LLSpeaker> s = *i; - if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) - { - id = s->mID; - break; - } - } - - if (!id.isNull()) - { - mOutputMonitor->setVisible(TRUE); - mOutputMonitor->setSpeakerId(id); - } - else - { - mOutputMonitor->setVisible(FALSE); - } -} - -void LLNearbyChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) -{ - sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); -} - -void LLNearbyChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) -{ - // Look for "/20 foo" channel chats. - S32 channel = 0; - LLWString out_text = stripChannelNumber(wtext, &channel); - std::string utf8_out_text = wstring_to_utf8str(out_text); - std::string utf8_text = wstring_to_utf8str(wtext); - - utf8_text = utf8str_trim(utf8_text); - if (!utf8_text.empty()) - { - utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); - } - - // Don't animate for chats people can't hear (chat to scripts) - if (animate && (channel == 0)) - { - if (type == CHAT_TYPE_WHISPER) - { - lldebugs << "You whisper " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_NORMAL) - { - lldebugs << "You say " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_SHOUT) - { - lldebugs << "You shout " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); - } - else - { - llinfos << "send_chat_from_viewer() - invalid volume" << llendl; - return; - } - } - else - { - if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) - { - lldebugs << "Channel chat: " << utf8_text << llendl; - } - } - - send_chat_from_viewer(utf8_out_text, type, channel); -} - -// static -void LLNearbyChatBar::startChat(const char* line) -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb ) - return; - - cb->setVisible(TRUE); - cb->setFocus(TRUE); - cb->mChatBox->setFocus(TRUE); - - if (line) - { - std::string line_string(line); - cb->mChatBox->setText(line_string); - } - - cb->mChatBox->setCursorToEnd(); -} - -// Exit "chat mode" and do the appropriate focus changes -// static -void LLNearbyChatBar::stopChat() -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb) - return; - - cb->mChatBox->setFocus(FALSE); - - // stop typing animation - gAgent.stopTyping(); -} - -// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. -// Otherwise returns input and channel 0. -LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) -{ - if (mesg[0] == '/' - && mesg[1] == '/') - { - // This is a "repeat channel send" - *channel = sLastSpecialChatChannel; - return mesg.substr(2, mesg.length() - 2); - } - else if (mesg[0] == '/' - && mesg[1] - && LLStringOps::isDigit(mesg[1])) - { - // This a special "/20" speak on a channel - S32 pos = 0; - - // Copy the channel number into a string - LLWString channel_string; - llwchar c; - do - { - c = mesg[pos+1]; - channel_string.push_back(c); - pos++; - } - while(c && pos < 64 && LLStringOps::isDigit(c)); - - // Move the pointer forward to the first non-whitespace char - // Check isspace before looping, so we can handle "/33foo" - // as well as "/33 foo" - while(c && iswspace(c)) - { - c = mesg[pos+1]; - pos++; - } - - sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); - *channel = sLastSpecialChatChannel; - return mesg.substr(pos, mesg.length() - pos); - } - else - { - // This is normal chat. - *channel = 0; - return mesg; - } -} - -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); -} - -class LLChatCommandHandler : public LLCommandHandler -{ -public: - // not allowed from outside the app - LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } - - // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - bool retval = false; - // Need at least 2 tokens to have a valid message. - if (tokens.size() < 2) - { - retval = false; - } - else - { - S32 channel = tokens[0].asInteger(); - // VWR-19499 Restrict function to chat channels greater than 0. - if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) - { - retval = true; - // Send unescaped message, see EXT-6353. - std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); - send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); - } - else - { - retval = false; - // Tell us this is an unsupported SLurl. - } - } - return retval; - } -}; - -// Creating the object registers with the dispatcher. -LLChatCommandHandler gChatHandler; - - diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h deleted file mode 100644 index 662496d338..0000000000 --- a/indra/newview/llnearbychatbar.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llnearbychatbar.h - * @brief LLNearbyChatBar class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLNEARBYCHATBAR_H -#define LL_LLNEARBYCHATBAR_H - -#include "llfloater.h" -#include "llcombobox.h" -#include "llgesturemgr.h" -#include "llchat.h" -#include "llvoiceclient.h" -#include "lloutputmonitorctrl.h" -#include "llspeakers.h" - -class LLNearbyChatBarListener; - -class LLNearbyChatBar : public LLFloater -{ - LOG_CLASS(LLNearbyChatBar); - -public: - // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChatBar(const LLSD& key); - ~LLNearbyChatBar() {} - - virtual BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - - static LLNearbyChatBar* getInstance(); - - LLLineEditor* getChatBox() { return mChatBox; } - - virtual void draw(); - - std::string getCurrentChat(); - virtual BOOL handleKeyHere( KEY key, MASK mask ); - - static void startChat(const char* line); - static void stopChat(); - - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); - static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - - void showHistory(); - void showTranslationCheckbox(BOOL show); - /*virtual*/void setMinimized(BOOL b); - -protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); - static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); - static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); - void onChatBoxFocusReceived(); - - void sendChat( EChatType type ); - void onChatBoxCommit(); - void onChatFontChange(LLFontGL* fontp); - - /* virtual */ bool applyRectControl(); - - void showNearbyChatPanel(bool show); - void onToggleNearbyChatPanel(); - - static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); - EChatType processChatTypeTriggers(EChatType type, std::string &str); - - void displaySpeakingIndicator(); - - // Which non-zero channel did we last chat on? - static S32 sLastSpecialChatChannel; - - LLLineEditor* mChatBox; - LLView* mNearbyChat; - LLOutputMonitorCtrl* mOutputMonitor; - LLLocalSpeakerMgr* mSpeakerMgr; - - S32 mExpandedHeight; - - boost::shared_ptr<LLNearbyChatBarListener> mListener; -}; - -#endif diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp index a63e1fb76e..61815d1864 100644 --- a/indra/newview/llnearbychatbarlistener.cpp +++ b/indra/newview/llnearbychatbarlistener.cpp @@ -29,14 +29,14 @@ #include "llviewerprecompiledheaders.h" #include "llnearbychatbarlistener.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagent.h" #include "llchat.h" -LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar) +LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChat & chatbar) : LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc."), mChatbar(chatbar) diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h index 9af9bc1f7b..0537275424 100644 --- a/indra/newview/llnearbychatbarlistener.h +++ b/indra/newview/llnearbychatbarlistener.h @@ -33,17 +33,17 @@ #include "lleventapi.h" class LLSD; -class LLNearbyChatBar; +class LLNearbyChat; class LLNearbyChatBarListener : public LLEventAPI { public: - LLNearbyChatBarListener(LLNearbyChatBar & chatbar); + LLNearbyChatBarListener(LLNearbyChat & chatbar); private: void sendChat(LLSD const & chat_data) const; - LLNearbyChatBar & mChatbar; + LLNearbyChat & mChatbar; }; #endif // LL_LLNEARBYCHATBARLISTENER_H diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 600fd395fb..c97e3585e1 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -1,6 +1,6 @@ /** * @file LLNearbyChatHandler.cpp - * @brief Nearby chat notification managment + * @brief Nearby chat chat managment * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -40,22 +40,24 @@ #include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance #include "llviewerwindow.h"//for screen channel position -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llrootview.h" #include "lllayoutstack.h" //add LLNearbyChatHandler to LLNotificationsUI namespace using namespace LLNotificationsUI; -//----------------------------------------------------------------------------------------------- -//LLNearbyChatScreenChannel -//----------------------------------------------------------------------------------------------- -LLToastPanelBase* createToastPanel() +static LLNearbyChatToastPanel* createToastPanel() { LLNearbyChatToastPanel* item = LLNearbyChatToastPanel::createInstance(); return item; } + +//----------------------------------------------------------------------------------------------- +//LLNearbyChatScreenChannel +//----------------------------------------------------------------------------------------------- + class LLNearbyChatScreenChannel: public LLScreenChannelBase { LOG_CLASS(LLNearbyChatScreenChannel); @@ -81,10 +83,10 @@ public: } } - void addNotification (LLSD& notification); + void addChat (LLSD& chat); void arrangeToasts (); - typedef boost::function<LLToastPanelBase* (void )> create_toast_panel_callback_t; + typedef boost::function<LLNearbyChatToastPanel* (void )> create_toast_panel_callback_t; void setCreatePanelCallback(create_toast_panel_callback_t value) { m_create_toast_panel_callback_t = value;} void onToastDestroyed (LLToast* toast, bool app_quitting); @@ -152,6 +154,8 @@ protected: bool mChannelRect; }; + + //----------------------------------------------------------------------------------------------- // LLNearbyChatToast //----------------------------------------------------------------------------------------------- @@ -255,7 +259,7 @@ void LLNearbyChatScreenChannel::updateToastFadingTime() bool LLNearbyChatScreenChannel::createPoolToast() { - LLToastPanelBase* panel= m_create_toast_panel_callback_t(); + LLNearbyChatToastPanel* panel= m_create_toast_panel_callback_t(); if(!panel) return false; @@ -277,7 +281,7 @@ bool LLNearbyChatScreenChannel::createPoolToast() return true; } -void LLNearbyChatScreenChannel::addNotification(LLSD& notification) +void LLNearbyChatScreenChannel::addChat(LLSD& chat) { //look in pool. if there is any message if(mStopProcessing) @@ -289,8 +293,8 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(m_active_toasts.size()) { - LLUUID fromID = notification["from_id"].asUUID(); // agent id or object id - std::string from = notification["from"].asString(); + LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id + std::string from = chat["from"].asString(); LLToast* toast = m_active_toasts[0].get(); if (toast) { @@ -298,7 +302,7 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) { - panel->addMessage(notification); + panel->addMessage(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -316,11 +320,11 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) LL_DEBUGS("NearbyChat") << "Empty pool" << llendl; if(!createPoolToast())//created toast will go to pool. so next call will find it return; - addNotification(notification); + addChat(chat); return; } - int chat_type = notification["chat_type"].asInteger(); + int chat_type = chat["chat_type"].asInteger(); if( ((EChatType)chat_type == CHAT_TYPE_DEBUG_MSG)) { @@ -339,10 +343,10 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) m_toast_pool.pop_back(); - LLToastPanelBase* panel = dynamic_cast<LLToastPanelBase*>(toast->getPanel()); + LLNearbyChatToastPanel* panel = dynamic_cast<LLNearbyChatToastPanel*>(toast->getPanel()); if(!panel) return; - panel->init(notification); + panel->init(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -445,10 +449,8 @@ void LLNearbyChatScreenChannel::arrangeToasts() //----------------------------------------------------------------------------------------------- boost::scoped_ptr<LLEventPump> LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); -LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id) +LLNearbyChatHandler::LLNearbyChatHandler() { - mType = type; - // Getting a Channel for our notifications LLNearbyChatScreenChannel::Params p; p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); @@ -485,28 +487,26 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); // Build notification data - LLSD notification; - notification["message"] = chat_msg.mText; - notification["from"] = chat_msg.mFromName; - notification["from_id"] = chat_msg.mFromID; - notification["time"] = chat_msg.mTime; - notification["source"] = (S32)chat_msg.mSourceType; - notification["chat_type"] = (S32)chat_msg.mChatType; - notification["chat_style"] = (S32)chat_msg.mChatStyle; + LLSD chat; + chat["message"] = chat_msg.mText; + chat["from"] = chat_msg.mFromName; + chat["from_id"] = chat_msg.mFromID; + chat["time"] = chat_msg.mTime; + chat["source"] = (S32)chat_msg.mSourceType; + chat["chat_type"] = (S32)chat_msg.mChatType; + chat["chat_style"] = (S32)chat_msg.mChatStyle; // Pass sender info so that it can be rendered properly (STORM-1021). - notification["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); + chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); if (chat_msg.mChatType == CHAT_TYPE_DIRECT && chat_msg.mText.length() > 0 && chat_msg.mText[0] == '@') { // Send event on to LLEventStream and exit - sChatWatcher->post(notification); + sChatWatcher->post(chat); return; } @@ -553,11 +553,9 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // Send event on to LLEventStream - sChatWatcher->post(notification); + sChatWatcher->post(chat); - - if( !chat_bar->isMinimized() - && nearby_chat->isInVisibleChain() + if( nearby_chat->isInVisibleChain() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && gSavedSettings.getBOOL("UseChatBubbles") ) || mChannel.isDead() @@ -604,23 +602,19 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, // Add a nearby chat toast. LLUUID id; id.generate(); - notification["id"] = id; + chat["id"] = id; std::string r_color_name = "White"; F32 r_color_alpha = 1.0f; LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); - notification["text_color"] = r_color_name; - notification["color_alpha"] = r_color_alpha; - notification["font_size"] = (S32)LLViewerChat::getChatFontSize() ; - notification["message"] = toast_msg; - channel->addNotification(notification); + chat["text_color"] = r_color_name; + chat["color_alpha"] = r_color_alpha; + chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ; + chat["message"] = toast_msg; + channel->addChat(chat); } } -void LLNearbyChatHandler::onDeleteToast(LLToast* toast) -{ -} - //----------------------------------------------------------------------------------------------- // LLNearbyChatToast diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index b0e4f62d51..a5034ac1cb 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -37,14 +37,13 @@ namespace LLNotificationsUI{ class LLNearbyChatHandler : public LLChatHandler { public: - LLNearbyChatHandler(e_notification_type type,const LLSD& id); + LLNearbyChatHandler(); virtual ~LLNearbyChatHandler(); virtual void processChat(const LLChat& chat_msg, const LLSD &args); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); static boost::scoped_ptr<LLEventPump> sChatWatcher; diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 89fe7bb3c2..2bc9cdd3c1 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -40,10 +40,10 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsModal(false) +LLAlertHandler::LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal) +: LLSysHandler(name, notification_type), + mIsModal(is_modal) { - mType = type; - LLScreenChannelBase::Params p; p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); p.display_toasts_always = true; @@ -68,79 +68,58 @@ void LLAlertHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLAlertHandler::processNotification(const LLSD& notify) +bool LLAlertHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load") + if (notification->canLogToIM() && notification->hasFormElements()) { - if (LLHandlerUtil::canSpawnSessionAndLogToIM(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - // firstly create session... - LLHandlerUtil::spawnIMSession(name, from_id); - - // ...then log message to have IM Well notified about new message - LLHandlerUtil::logToIMP2P(notification); - } - - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); - p.enable_hide_btn = false; - p.can_fade = false; - p.is_modal = mIsModal; - p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); - - // Show alert in middle of progress view (during teleport) (EXT-1093) - LLProgressView* progress = gViewerWindow->getProgressView(); - LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); - mChannel.get()->updatePositionAndSize(rc); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - } - else if (notify["sigtype"].asString() == "change") - { - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); - } - else - { - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->killToastByNotificationID(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + // firstly create session... + LLHandlerUtil::spawnIMSession(name, from_id); + + // ...then log message to have IM Well notified about new message + LLHandlerUtil::logToIMP2P(notification); } + + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); + p.enable_hide_btn = false; + p.can_fade = false; + p.is_modal = mIsModal; + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); + + // Show alert in middle of progress view (during teleport) (EXT-1093) + LLProgressView* progress = gViewerWindow->getProgressView(); + LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); + mChannel.get()->updatePositionAndSize(rc); + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } -//-------------------------------------------------------------------------- - -void LLAlertHandler::onDeleteToast(LLToast* toast) +void LLAlertHandler::onChange( LLNotificationPtr notification ) { + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); } - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index ad51389241..18cd94e685 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -37,15 +37,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) +LLGroupHandler::LLGroupHandler() +: LLSysHandler("Group Notifications", "groupnotify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLGroupHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -64,72 +62,37 @@ void LLGroupHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLGroupHandler::processNotification(const LLSD& notify) +bool LLGroupHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLHandlerUtil::logGroupNoticeToIMGroup(notification); + LLHandlerUtil::logGroupNoticeToIMGroup(notification); - LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); + LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); - // send a signal to the counter manager - mNewNotificationSignal(); + LLGroupActions::refresh_notices(); - LLGroupActions::refresh_notices(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } return false; } -//-------------------------------------------------------------------------- -void LLGroupHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); - - // send a signal to a listener to let him perform some action - // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); -} - -//-------------------------------------------------------------------------- -void LLGroupHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - - if (notification && LLNotificationManager::getInstance()->getHandlerForNotification(notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } -} //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 3569ad6447..0899625242 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -30,7 +30,7 @@ #include "llwindow.h" -//#include "llnotificationsutil.h" +#include "llnotifications.h" #include "llchannelmanager.h" #include "llchat.h" #include "llinstantmessage.h" @@ -40,20 +40,6 @@ class LLIMFloater; namespace LLNotificationsUI { -// ENotificationType enumerates all possible types of notifications that could be met -// -typedef enum e_notification_type -{ - NT_NOTIFY, - NT_NOTIFYTIP, - NT_GROUPNOTIFY, - NT_IMCHAT, - NT_GROUPCHAT, - NT_NEARBYCHAT, - NT_ALERT, - NT_ALERTMODAL, - NT_OFFER -} ENotificationType; /** * Handler of notification events. @@ -81,21 +67,8 @@ class LLEventHandler public: virtual ~LLEventHandler() {}; - // callbacks for counters - typedef boost::function<void (void)> notification_callback_t; - typedef boost::signals2::signal<void (void)> notification_signal_t; - notification_signal_t mNewNotificationSignal; - notification_signal_t mDelNotificationSignal; - boost::signals2::connection setNewNotificationCallback(notification_callback_t cb) { return mNewNotificationSignal.connect(cb); } - boost::signals2::connection setDelNotification(notification_callback_t cb) { return mDelNotificationSignal.connect(cb); } - // callback for notification/toast - typedef boost::function<void (const LLUUID id)> notification_id_callback_t; - typedef boost::signals2::signal<void (const LLUUID id)> notification_id_signal_t; - notification_id_signal_t mNotificationIDSignal; - boost::signals2::connection setNotificationIDCallback(notification_id_callback_t cb) { return mNotificationIDSignal.connect(cb); } - protected: - virtual void onDeleteToast(LLToast* toast)=0; + virtual void onDeleteToast(LLToast* toast) {} // arrange handler's channel on a screen // is necessary to unbind a moment of creation of a channel and a moment of positioning of it @@ -104,8 +77,6 @@ protected: virtual void initChannel()=0; LLHandle<LLScreenChannelBase> mChannel; - e_notification_type mType; - }; // LLSysHandler and LLChatHandler are more specific base classes @@ -115,20 +86,18 @@ protected: /** * Handler for system notifications. */ -class LLSysHandler : public LLEventHandler +class LLSysHandler : public LLEventHandler, public LLNotificationChannel { public: - LLSysHandler(); + LLSysHandler(const std::string& name, const std::string& notification_type); virtual ~LLSysHandler() {}; - virtual bool processNotification(const LLSD& notify)=0; - -protected : - static void init(); - void removeExclusiveNotifications(const LLNotificationPtr& notif); + // base interface functions + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());} - typedef std::list< std::set<std::string> > exclusive_notif_sets; - static exclusive_notif_sets sExclusiveNotificationGroups; + virtual bool processNotification(const LLNotificationPtr& notify)=0; }; /** @@ -149,15 +118,12 @@ public: class LLIMHandler : public LLSysHandler { public: - LLIMHandler(e_notification_type type, const LLSD& id); + LLIMHandler(); virtual ~LLIMHandler(); - // base interface functions - virtual bool processNotification(const LLSD& notify); - protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); + bool processNotification(const LLNotificationPtr& p); + /*virtual*/ void initChannel(); }; /** @@ -167,16 +133,15 @@ protected: class LLTipHandler : public LLSysHandler { public: - LLTipHandler(e_notification_type type, const LLSD& id); + LLTipHandler(); virtual ~LLTipHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void onRejectToast(const LLUUID& id); - virtual void initChannel(); + /*virtual*/ void initChannel(); }; /** @@ -186,18 +151,16 @@ protected: class LLScriptHandler : public LLSysHandler { public: - LLScriptHandler(e_notification_type type, const LLSD& id); + LLScriptHandler(); virtual ~LLScriptHandler(); + /*virtual*/ void onDelete(LLNotificationPtr p); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void onDeleteToast(LLToast* toast); + /*virtual*/ void initChannel(); }; @@ -207,18 +170,15 @@ protected: class LLGroupHandler : public LLSysHandler { public: - LLGroupHandler(e_notification_type type, const LLSD& id); + LLGroupHandler(); virtual ~LLGroupHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); }; /** @@ -227,16 +187,14 @@ protected: class LLAlertHandler : public LLSysHandler { public: - LLAlertHandler(e_notification_type type, const LLSD& id); + LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal); virtual ~LLAlertHandler(); - void setAlertMode(bool is_modal) { mIsModal = is_modal; } - - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); bool mIsModal; @@ -249,102 +207,71 @@ protected: class LLOfferHandler : public LLSysHandler { public: - LLOfferHandler(e_notification_type type, const LLSD& id); + LLOfferHandler(); virtual ~LLOfferHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr notification); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void initChannel(); }; /** * Handler for UI hints. */ -class LLHintHandler : public LLSingleton<LLHintHandler> +class LLHintHandler : public LLNotificationChannel { public: - LLHintHandler(); - virtual ~LLHintHandler(); + LLHintHandler() : LLNotificationChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")) + {} + virtual ~LLHintHandler() {} - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onAdd(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr p); }; /** * Handler for browser notifications */ -class LLBrowserNotification : public LLSingleton<LLBrowserNotification> +class LLBrowserNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLBrowserNotification() + : LLNotificationChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + bool processNotification(const LLNotificationPtr& p); }; /** * Handler for outbox notifications */ -class LLOutboxNotification : public LLSingleton<LLOutboxNotification> +class LLOutboxNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLOutboxNotification() + : LLNotificationChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { } + /*virtual*/ void onDelete(LLNotificationPtr p); + bool processNotification(const LLNotificationPtr& p); }; class LLHandlerUtil { public: /** - * Checks sufficient conditions to log notification message to IM session. - */ - static bool canLogToIM(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to log notification message to nearby chat session. - */ - static bool canLogToNearbyChat(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to spawn IM session. - */ - static bool canSpawnIMSession(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to add notification toast panel IM floater. - */ - static bool canAddNotifPanelToIM(const LLNotificationPtr& notification); - - /** - * Checks whether notification can be used multiple times or not. - */ - static bool isNotificationReusable(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create IM session and be written into it. - * - * This method uses canLogToIM() & canSpawnIMSession(). - */ - static bool canSpawnSessionAndLogToIM(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create toast. - */ - static bool canSpawnToast(const LLNotificationPtr& notification); - - /** * Determines whether IM floater is opened. */ static bool isIMFloaterOpened(const LLNotificationPtr& notification); /** - * Determines whether IM floater is focused. - */ - static bool isIMFloaterFocused(const LLNotificationPtr& notification); - - /** * Writes notification message to IM session. */ static void logToIM(const EInstantMessage& session_type, @@ -355,12 +282,7 @@ public: /** * Writes notification message to IM p2p session. */ - static void logToIMP2P(const LLNotificationPtr& notification); - - /** - * Writes notification message to IM p2p session. - */ - static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only); + static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only = false); /** * Writes group notice notification message to IM group session. @@ -406,13 +328,6 @@ public: */ static void decIMMesageCounter(const LLNotificationPtr& notification); -private: - - /** - * Find IM floater based on "from_id" - */ - static LLIMFloater* findIMFloater(const LLNotificationPtr& notification); - }; } diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 16c51138a9..cba22b233b 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -41,212 +41,19 @@ using namespace LLNotificationsUI; -// static -std::list< std::set<std::string> > LLSysHandler::sExclusiveNotificationGroups; - -// static -void LLSysHandler::init() -{ - std::set<std::string> online_offline_group; - online_offline_group.insert("FriendOnline"); - online_offline_group.insert("FriendOffline"); - - sExclusiveNotificationGroups.push_back(online_offline_group); -} - -LLSysHandler::LLSysHandler() -{ - if(sExclusiveNotificationGroups.empty()) - { - init(); - } -} - -void LLSysHandler::removeExclusiveNotifications(const LLNotificationPtr& notif) -{ - LLScreenChannel* channel = dynamic_cast<LLScreenChannel *>(mChannel.get()); - if (channel == NULL) - { - return; - } - - class ExclusiveMatcher: public LLScreenChannel::Matcher - { - public: - ExclusiveMatcher(const std::set<std::string>& excl_group, - const std::string& from_name) : - mExclGroup(excl_group), mFromName(from_name) - { - } - bool matches(const LLNotificationPtr notification) const - { - for (std::set<std::string>::const_iterator it = mExclGroup.begin(); it - != mExclGroup.end(); it++) - { - std::string from_name = LLHandlerUtil::getSubstitutionName(notification); - if (notification->getName() == *it && from_name == mFromName) - { - return true; - } - } - return false; - } - private: - const std::set<std::string>& mExclGroup; - const std::string& mFromName; - }; - - - for (exclusive_notif_sets::iterator it = sExclusiveNotificationGroups.begin(); it - != sExclusiveNotificationGroups.end(); it++) - { - std::set<std::string> group = *it; - std::set<std::string>::iterator g_it = group.find(notif->getName()); - if (g_it != group.end()) - { - channel->killMatchedToasts(ExclusiveMatcher(group, - LLHandlerUtil::getSubstitutionName(notif))); - } - } -} - -const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"), - REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), - OBJECT_GIVE_ITEM("ObjectGiveItem"), - OBJECT_GIVE_ITEM_UNKNOWN_USER("ObjectGiveItemUnknownUser"), - PAYMENT_RECEIVED("PaymentReceived"), - PAYMENT_SENT("PaymentSent"), - ADD_FRIEND_WITH_MESSAGE("AddFriendWithMessage"), - USER_GIVE_ITEM("UserGiveItem"), - INVENTORY_ACCEPTED("InventoryAccepted"), - INVENTORY_DECLINED("InventoryDeclined"), - OFFER_FRIENDSHIP("OfferFriendship"), - FRIENDSHIP_ACCEPTED("FriendshipAccepted"), - FRIENDSHIP_OFFERED("FriendshipOffered"), - FRIENDSHIP_ACCEPTED_BYME("FriendshipAcceptedByMe"), - FRIENDSHIP_DECLINED_BYME("FriendshipDeclinedByMe"), - FRIEND_ONLINE("FriendOnline"), FRIEND_OFFLINE("FriendOffline"), - SERVER_OBJECT_MESSAGE("ServerObjectMessage"), - TELEPORT_OFFERED("TeleportOffered"), - TELEPORT_OFFERED_MATURITY_EXCEEDED("TeleportOffered_MaturityExceeded"), - TELEPORT_OFFERED_MATURITY_BLOCKED("TeleportOffered_MaturityBlocked"), - TELEPORT_OFFER_SENT("TeleportOfferSent"), - IM_SYSTEM_MESSAGE_TIP("IMSystemMessageTip"); - +LLSysHandler::LLSysHandler(const std::string& name, const std::string& notification_type) +: LLNotificationChannel(name, "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type)) +{} // static -bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) -{ - return GRANTED_MODIFY_RIGHTS == notification->getName() - || REVOKED_MODIFY_RIGHTS == notification->getName() - || PAYMENT_RECEIVED == notification->getName() - || PAYMENT_SENT == notification->getName() - || OFFER_FRIENDSHIP == notification->getName() - || FRIENDSHIP_OFFERED == notification->getName() - || FRIENDSHIP_ACCEPTED == notification->getName() - || FRIENDSHIP_ACCEPTED_BYME == notification->getName() - || FRIENDSHIP_DECLINED_BYME == notification->getName() - || SERVER_OBJECT_MESSAGE == notification->getName() - || INVENTORY_ACCEPTED == notification->getName() - || INVENTORY_DECLINED == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName() - || TELEPORT_OFFER_SENT == notification->getName() - || IM_SYSTEM_MESSAGE_TIP == notification->getName(); -} - -// static -bool LLHandlerUtil::canLogToNearbyChat(const LLNotificationPtr& notification) -{ - return notification->getType() == "notifytip" - && FRIEND_ONLINE != notification->getName() - && FRIEND_OFFLINE != notification->getName() - && INVENTORY_ACCEPTED != notification->getName() - && INVENTORY_DECLINED != notification->getName() - && IM_SYSTEM_MESSAGE_TIP != notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnIMSession(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canAddNotifPanelToIM(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::isNotificationReusable(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnSessionAndLogToIM(const LLNotificationPtr& notification) -{ - return canLogToIM(notification) && canSpawnIMSession(notification); -} - -// static -bool LLHandlerUtil::canSpawnToast(const LLNotificationPtr& notification) +bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) { - if(INVENTORY_DECLINED == notification->getName() - || INVENTORY_ACCEPTED == notification->getName()) - { - // return false for inventory accepted/declined notifications if respective IM window is open (EXT-5909) - return ! isIMFloaterOpened(notification); - } - - if(FRIENDSHIP_ACCEPTED == notification->getName()) - { - // don't show FRIENDSHIP_ACCEPTED if IM window is opened and focused - EXT-6441 - return ! isIMFloaterFocused(notification); - } - - if(OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName()) - { - // When ANY offer arrives, show toast, unless IM window is already open - EXT-5904 - return ! isIMFloaterOpened(notification); - } - - return true; -} + bool res = false; -// static -LLIMFloater* LLHandlerUtil::findIMFloater(const LLNotificationPtr& notification) -{ LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -// static -bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) -{ - bool res = false; + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - LLIMFloater* im_floater = findIMFloater(notification); if (im_floater != NULL) { res = im_floater->getVisible() == TRUE; @@ -255,19 +62,6 @@ bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) return res; } -bool LLHandlerUtil::isIMFloaterFocused(const LLNotificationPtr& notification) -{ - bool res = false; - - LLIMFloater* im_floater = findIMFloater(notification); - if (im_floater != NULL) - { - res = im_floater->hasFocus() == TRUE; - } - - return res; -} - // static void LLHandlerUtil::logToIM(const EInstantMessage& session_type, const std::string& session_name, const std::string& from_name, @@ -329,12 +123,6 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type, } } -// static -void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification) -{ - logToIMP2P(notification, false); -} - void log_name_callback(const std::string& full_name, const std::string& from_name, const std::string& message, const LLUUID& from_id) @@ -346,9 +134,6 @@ void log_name_callback(const std::string& full_name, const std::string& from_nam // static void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) { - // don't create IM p2p session with objects, it's necessary condition to log - if (notification->getName() != OBJECT_GIVE_ITEM) - { LLUUID from_id = notification->getPayload()["from_id"]; if (from_id.isNull()) @@ -366,7 +151,6 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi gCacheName->get(from_id, false, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), from_id)); } } -} // static void LLHandlerUtil::logGroupNoticeToIMGroup( @@ -501,14 +285,10 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession( - session_id); + LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession(session_id); - if (session == NULL) + if (session) { - return; - } - LLSD arg; arg["session_id"] = session_id; session->mNumUnread--; @@ -517,3 +297,5 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) arg["participant_unread"] = session->mParticipantUnreadMessageCount; LLIMModel::getInstance()->mNewMsgSignal(arg); } +} + diff --git a/indra/newview/llnotificationhinthandler.cpp b/indra/newview/llnotificationhinthandler.cpp index f7163cb04f..271f418507 100644 --- a/indra/newview/llnotificationhinthandler.cpp +++ b/indra/newview/llnotificationhinthandler.cpp @@ -33,26 +33,6 @@ using namespace LLNotificationsUI; -LLHintHandler::LLHintHandler() -{ -} - -LLHintHandler::~LLHintHandler() -{ -} - -bool LLHintHandler::processNotification(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - std::string sigtype = notify["sigtype"].asString(); - if (sigtype == "add" || sigtype == "load") - { - LLHints::show(notification); - } - else if (sigtype == "delete") - { - LLHints::hide(notification); - } - return false; -} +void LLHintHandler::onAdd(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onLoad(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onDelete(LLNotificationPtr p) { LLHints::hide(p); } diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac5..2862ad6962 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -42,107 +42,35 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- LLNotificationManager::LLNotificationManager() { - mNotifyHandlers.clear(); init(); } //-------------------------------------------------------------------------- LLNotificationManager::~LLNotificationManager() { - BOOST_FOREACH(listener_pair_t& pair, mChannelListeners) - { - pair.second.disconnect(); - } } //-------------------------------------------------------------------------- void LLNotificationManager::init() { - LLNotificationChannel::buildChannel("Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notify")); - LLNotificationChannel::buildChannel("NotificationTips", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytip")); - LLNotificationChannel::buildChannel("Group Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "groupnotify")); - LLNotificationChannel::buildChannel("Alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("AlertModal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytoast")); - LLNotificationChannel::buildChannel("Offer", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "offer")); - LLNotificationChannel::buildChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")); - LLNotificationChannel::buildChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")); - LLNotificationChannel::buildChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")); + mChannels.push_back(new LLScriptHandler()); + mChannels.push_back(new LLTipHandler()); + mChannels.push_back(new LLGroupHandler()); + mChannels.push_back(new LLAlertHandler("Alerts", "alert", false)); + mChannels.push_back(new LLAlertHandler("AlertModal", "alertmodal", true)); + mChannels.push_back(new LLOfferHandler()); + mChannels.push_back(new LLHintHandler()); + mChannels.push_back(new LLBrowserNotification()); + mChannels.push_back(new LLOutboxNotification()); + mChannels.push_back(new LLIMHandler()); - mChannelListeners["Notifications"] = LLNotifications::instance().getChannel("Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["NotificationTips"] = LLNotifications::instance().getChannel("NotificationTips")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Group Notifications"] = LLNotifications::instance().getChannel("Group Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Alerts"] = LLNotifications::instance().getChannel("Alerts")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["AlertModal"] = LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["IM Notifications"] = LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Offer"] = LLNotifications::instance().getChannel("Offer")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Hints"] = LLNotifications::instance().getChannel("Hints")->connectChanged(boost::bind(&LLHintHandler::processNotification, LLHintHandler::getInstance(), _1)); - mChannelListeners["Browser"] = LLNotifications::instance().getChannel("Browser")->connectChanged(boost::bind(&LLBrowserNotification::processNotification, LLBrowserNotification::getInstance(), _1)); - mChannelListeners["Outbox"] = LLNotifications::instance().getChannel("Outbox")->connectChanged(boost::bind(&LLOutboxNotification::processNotification, LLOutboxNotification::getInstance(), _1)); - - mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLScriptHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = boost::shared_ptr<LLEventHandler>(new LLTipHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["groupnotify"] = boost::shared_ptr<LLEventHandler>(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); - mNotifyHandlers["alert"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - static_cast<LLAlertHandler*>(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); - mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler(NT_IMCHAT, LLSD())); - - mNotifyHandlers["nearbychat"] = boost::shared_ptr<LLEventHandler>(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); - mNotifyHandlers["offer"] = boost::shared_ptr<LLEventHandler>(new LLOfferHandler(NT_OFFER, LLSD())); -} - -//-------------------------------------------------------------------------- -bool LLNotificationManager::onNotification(const LLSD& notify) -{ - LLSysHandler* handle = NULL; - - if (LLNotifications::destroyed()) - return false; - - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - return false; - - std::string notification_type = notification->getType(); - handle = static_cast<LLSysHandler*>(mNotifyHandlers[notification_type].get()); - - if(!handle) - return false; - - return handle->processNotification(notify); + mChatHandler = boost::shared_ptr<LLNearbyChatHandler>(new LLNearbyChatHandler()); } //-------------------------------------------------------------------------- void LLNotificationManager::onChat(const LLChat& msg, const LLSD &args) { - // check ENotificationType argument - switch(args["type"].asInteger()) - { - case NT_NEARBYCHAT: - { - LLNearbyChatHandler* handle = dynamic_cast<LLNearbyChatHandler*>(mNotifyHandlers["nearbychat"].get()); - - if(handle) - handle->processChat(msg, args); - } - break; - default: //no need to handle all enum types - break; - } -} - -//-------------------------------------------------------------------------- -LLEventHandler* LLNotificationManager::getHandlerForNotification(std::string notification_type) -{ - std::map<std::string, boost::shared_ptr<LLEventHandler> >::iterator it = mNotifyHandlers.find(notification_type); - - if(it != mNotifyHandlers.end()) - return (*it).second.get(); - - return NULL; + if(mChatHandler) + mChatHandler->processChat(msg, args); } -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 27b6ba1c71..c8afdf9e46 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -28,8 +28,6 @@ #ifndef LL_LLNOTIFICATIONMANAGER_H #define LL_LLNOTIFICATIONMANAGER_H -#include "llevents.h" - #include "lluictrl.h" #include "llnotificationhandler.h" @@ -49,7 +47,6 @@ class LLToast; class LLNotificationManager : public LLSingleton<LLNotificationManager> { typedef std::pair<std::string, LLEventHandler*> eventhandlers; - typedef std::pair<const std::string, LLBoundListener> listener_pair_t; public: LLNotificationManager(); virtual ~LLNotificationManager(); @@ -59,22 +56,12 @@ public: void init(void); //TODO: combine processing and storage (*) - // this method reacts on system notifications and calls an appropriate handler - bool onNotification(const LLSD& notification); - // this method reacts on chat notifications and calls an appropriate handler void onChat(const LLChat& msg, const LLSD &args); - // get a handler for a certain type of notification - LLEventHandler* getHandlerForNotification(std::string notification_type); - - private: - //TODO (*) - std::map<std::string, boost::shared_ptr<LLEventHandler> > mNotifyHandlers; - // cruft std::map<std::string, LLChatHandler*> mChatHandlers; - - std::map<std::string, LLBoundListener> mChannelListeners; + boost::shared_ptr<class LLNearbyChatHandler> mChatHandler; + std::vector<LLNotificationChannelPtr> mChannels; }; } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 1552ed3346..6e641575fa 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -40,16 +40,14 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLOfferHandler::LLOfferHandler(e_notification_type type, const LLSD& id) +LLOfferHandler::LLOfferHandler() +: LLSysHandler("Offer", "offer") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLOfferHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,147 +66,109 @@ void LLOfferHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLOfferHandler::processNotification(const LLSD& notify) +bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { + if( notification->getPayload().has("give_inventory_notification") + && notification->getPayload()["give_inventory_notification"].asBoolean() == false) + { + // This is an original inventory offer, so add a script floater + LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + } + else + { + bool add_notif_to_im = notification->canLogToIM() && notification->hasFormElements(); - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + if (add_notif_to_im) { - // This is an original inventory offer, so add a script floater - LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + LLHandlerUtil::spawnIMSession(name, from_id); + LLHandlerUtil::addNotifPanelToIM(notification); } - else + + if (!notification->canShowToast()) { - notification->setReusable(LLHandlerUtil::isNotificationReusable(notification)); - - LLUUID session_id; - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - session_id = LLHandlerUtil::spawnIMSession(name, from_id); - } - - bool show_toast = LLHandlerUtil::canSpawnToast(notification); - bool add_notid_to_im = LLHandlerUtil::canAddNotifPanelToIM(notification); - if (add_notid_to_im) - { - LLHandlerUtil::addNotifPanelToIM(notification); - } - - if (notification->getPayload().has("SUPPRESS_TOAST") - && notification->getPayload()["SUPPRESS_TOAST"]) - { - LLNotificationsUtil::cancel(notification); - } - else if(show_toast) - { - LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); - // don't close notification on panel destroy since it will be used by IM floater - notify_box->setCloseNotificationOnDestroy(!add_notid_to_im); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1); - // we not save offer notifications to the syswell floater that should be added to the IM floater - p.can_be_stored = !add_notid_to_im; - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // if we not add notification to IM - add it to notification well - if (!add_notid_to_im) - { - // send a signal to the counter manager - mNewNotificationSignal(); - } - } - - if (LLHandlerUtil::canLogToIM(notification)) - { - // log only to file if notif panel can be embedded to IM and IM is opened - if (add_notid_to_im && LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::logToIMP2P(notification, true); - } - else - { - LLHandlerUtil::logToIMP2P(notification); - } - } + LLNotificationsUtil::cancel(notification); } - } - else if (notify["sigtype"].asString() == "delete") - { - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + else if(!notification->canLogToIM() || !LLHandlerUtil::isIMFloaterOpened(notification)) { - // Remove original inventory offer script floater - LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + // we not save offer notifications to the syswell floater that should be added to the IM floater + p.can_be_stored = !add_notif_to_im; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); } - else + + if (notification->canLogToIM()) { - if (LLHandlerUtil::canAddNotifPanelToIM(notification) - && !LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::decIMMesageCounter(notification); - } - mChannel.get()->killToastByNotificationID(notification->getID()); + // log only to file if notif panel can be embedded to IM and IM is opened + bool file_only = add_notif_to_im && LLHandlerUtil::isIMFloaterOpened(notification); + LLHandlerUtil::logToIMP2P(notification, file_only); } } return false; } -//-------------------------------------------------------------------------- - -void LLOfferHandler::onDeleteToast(LLToast* toast) +/*virtual*/ void LLOfferHandler::onChange(LLNotificationPtr p) { - if (!LLHandlerUtil::canAddNotifPanelToIM(toast->getNotification())) + LLToastNotifyPanel* panelp = LLToastNotifyPanel::getInstance(p->getID()); + if (panelp) { - // send a signal to the counter manager - mDelNotificationSignal(); + // + // HACK: if we're dealing with a notification embedded in IM, update it + // otherwise remove its toast + // + if (dynamic_cast<LLIMToastNotifyPanel*>(panelp)) + { + panelp->updateNotification(); + } + else + { + // if notification has changed, hide it + mChannel.get()->removeToastByNotificationID(p->getID()); + } } - - // send a signal to a listener to let him perform some action - // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); } -//-------------------------------------------------------------------------- -void LLOfferHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this - // don't delete notification since it may be used by IM floater - && !LLHandlerUtil::canAddNotifPanelToIM(notification)) +/*virtual*/ void LLOfferHandler::onDelete(LLNotificationPtr notification) +{ + if( notification->getPayload().has("give_inventory_notification") + && !notification->getPayload()["give_inventory_notification"] ) + { + // Remove original inventory offer script floater + LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + } + else { - LLNotifications::instance().cancel(notification); + if (notification->canLogToIM() + && notification->hasFormElements() + && !LLHandlerUtil::isIMFloaterOpened(notification)) + { + LLHandlerUtil::decIMMesageCounter(notification); + } + mChannel.get()->removeToastByNotificationID(notification->getID()); } } + diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 398f54c6f7..7e9c0d4f4b 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -37,21 +37,15 @@ using namespace LLNotificationsUI; -static const std::string SCRIPT_DIALOG ("ScriptDialog"); -static const std::string SCRIPT_DIALOG_GROUP ("ScriptDialogGroup"); -static const std::string SCRIPT_LOAD_URL ("LoadWebPage"); - //-------------------------------------------------------------------------- -LLScriptHandler::LLScriptHandler(e_notification_type type, const LLSD& id) +LLScriptHandler::LLScriptHandler() +: LLSysHandler("Notifications", "notify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLScriptHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -70,32 +64,25 @@ void LLScriptHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLScriptHandler::processNotification(const LLSD& notify) +bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add") - { - if (LLHandlerUtil::canLogToIM(notification)) + if (notification->canLogToIM()) { LLHandlerUtil::logToIMP2P(notification); } - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + if(notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); } @@ -114,60 +101,39 @@ bool LLScriptHandler::processNotification(const LLSD& notify) { channel->addToast(p); } + } - // send a signal to the counter manager - mNewNotificationSignal(); + return false; } - } - else if (notify["sigtype"].asString() == "delete") + + +void LLScriptHandler::onDelete( LLNotificationPtr notification ) { - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + if(notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } else { - mChannel.get()->killToastByNotificationID(notification->getID()); + mChannel.get()->removeToastByNotificationID(notification->getID()); } } - return false; -} + //-------------------------------------------------------------------------- void LLScriptHandler::onDeleteToast(LLToast* toast) { - // send a signal to the counter manager - mDelNotificationSignal(); - // send a signal to a listener to let him perform some action // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); - LLNotificationPtr notification = LLNotifications::getInstance()->find(toast->getNotificationID()); - if( notification && - (SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName()) ) + if( notification && notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } } -//-------------------------------------------------------------------------- -void LLScriptHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } -} - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationstorage.cpp b/indra/newview/llnotificationstorage.cpp index fb1adc7ddf..a31b95811e 100644 --- a/indra/newview/llnotificationstorage.cpp +++ b/indra/newview/llnotificationstorage.cpp @@ -84,9 +84,11 @@ bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& pay return false; } +static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications"); + void LLPersistentNotificationStorage::saveNotifications() { - // TODO - think about save optimization. + LLFastTimer _(FTM_SAVE_NOTIFICATIONS); llofstream notify_file(mFileName.c_str()); if (!notify_file.is_open()) @@ -98,10 +100,15 @@ void LLPersistentNotificationStorage::saveNotifications() LLSD output; LLSD& data = output["data"]; - LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent"); - LLNotificationSet::iterator it = history_channel->begin(); + boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent")); + if (!history_channel) + { + return; + } - for ( ; history_channel->end() != it; ++it) + for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory(); + it != end_it; + ++it) { LLNotificationPtr notification = *it; @@ -120,8 +127,11 @@ void LLPersistentNotificationStorage::saveNotifications() formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); } +static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications"); + void LLPersistentNotificationStorage::loadNotifications() { + LLFastTimer _(FTM_LOAD_NOTIFICATIONS); LLResponderRegistry::registerResponders(); LLNotifications::instance().getChannel("Persistent")-> diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index e397cfa046..507c6686fd 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -29,7 +29,7 @@ #include "llfloaterreg.h" #include "llnearbychat.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -41,15 +41,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLTipHandler::LLTipHandler(e_notification_type type, const LLSD& id) +LLTipHandler::LLTipHandler() +: LLSysHandler("NotificationTips", "notifytip") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLTipHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,102 +66,67 @@ void LLTipHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLTipHandler::processNotification(const LLSD& notify) +bool LLTipHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { // archive message in nearby chat - if (LLHandlerUtil::canLogToNearbyChat(notification)) - { - LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - - // don't show toast if Nearby Chat is opened - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - if (!nearby_chat_bar->isMinimized() && nearby_chat_bar->getVisible() && nearby_chat->getVisible()) - { - return false; - } - } - - std::string session_name = notification->getPayload()["SESSION_NAME"]; - const std::string name = notification->getSubstitutions()["NAME"]; - if (session_name.empty()) - { - session_name = name; - } - LLUUID from_id = notification->getPayload()["from_id"]; - if (LLHandlerUtil::canLogToIM(notification)) - { - LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, - notification->getMessage(), from_id, from_id); - } - - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - LLHandlerUtil::spawnIMSession(name, from_id); - } + if (notification->canLogToChat()) + { + LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - // don't spawn toast for inventory accepted/declined offers if respective IM window is open (EXT-5909) - if (!LLHandlerUtil::canSpawnToast(notification)) + // don't show toast if Nearby Chat is opened + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + if (!nearby_chat->isMinimized() && nearby_chat->getVisible()) { return false; } + } - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); - p.panel = notify_box; - p.is_tip = true; - p.can_be_stored = false; - - removeExclusiveNotifications(notification); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + std::string session_name = notification->getPayload()["SESSION_NAME"]; + const std::string name = notification->getSubstitutions()["NAME"]; + if (session_name.empty()) + { + session_name = name; } - else if (notify["sigtype"].asString() == "delete") + LLUUID from_id = notification->getPayload()["from_id"]; + if (notification->canLogToIM()) { - mChannel.get()->killToastByNotificationID(notification->getID()); + LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, + notification->getMessage(), from_id, from_id); } - return false; -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onDeleteToast(LLToast* toast) -{ -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onRejectToast(const LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); + if (notification->canLogToIM() && notification->hasFormElements()) + { + LLHandlerUtil::spawnIMSession(name, from_id); + } - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) + if (notification->canLogToIM() && LLHandlerUtil::isIMFloaterOpened(notification)) { - LLNotifications::instance().cancel(notification); + return false; } + + LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); + p.panel = notify_box; + p.is_tip = true; + p.can_be_stored = false; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 85626d8783..096e714981 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -28,6 +28,7 @@ #include "lloutputmonitorctrl.h" // library includes +#include "llfloaterreg.h" #include "llui.h" // viewer includes @@ -241,6 +242,17 @@ void LLOutputMonitorCtrl::draw() gl_rect_2d(0, monh, monw, 0, sColorBound, FALSE); } +// virtual +BOOL LLOutputMonitorCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (mSpeakerId != gAgentID) + { + LLFloaterReg::showInstance("floater_voice_volume", LLSD().with("avatar_id", mSpeakerId)); + } + + return TRUE; +} + void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/) { if (speaker_id.isNull() && mSpeakerId.notNull()) diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 2d23753d46..7b02e84744 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -68,6 +68,7 @@ public: // llview overrides virtual void draw(); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); void setPower(F32 val); F32 getPower(F32 val) const { return mPower; } diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index 5c85ec438c..35cda14f8d 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -30,15 +30,23 @@ // library include #include "llavatarname.h" +#include "llfiltereditor.h" #include "llfloater.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llscrolllistctrl.h" +#include "llmenubutton.h" // project include +#include "llavatarlistitem.h" +#include "llblocklist.h" +#include "llblockedlistitem.h" #include "llfloateravatarpicker.h" #include "llfloatersidepanelcontainer.h" +#include "llinventorylistitem.h" +#include "llinventorymodel.h" #include "llsidetraypanelcontainer.h" +#include "llviewercontrol.h" static LLRegisterPanelClassWrapper<LLPanelBlockedList> t_panel_blocked_list("panel_block_list_sidetray"); @@ -54,26 +62,35 @@ const std::string BLOCKED_PARAM_NAME = "blocked_to_select"; LLPanelBlockedList::LLPanelBlockedList() : LLPanel() { - mCommitCallbackRegistrar.add("Block.ClickPick", boost::bind(&LLPanelBlockedList::onPickBtnClick, this)); - mCommitCallbackRegistrar.add("Block.ClickBlockByName", boost::bind(&LLPanelBlockedList::onBlockByNameClick, this)); - mCommitCallbackRegistrar.add("Block.ClickRemove", boost::bind(&LLPanelBlockedList::onRemoveBtnClick, this)); -} - -LLPanelBlockedList::~LLPanelBlockedList() -{ - LLMuteList::getInstance()->removeObserver(this); + mCommitCallbackRegistrar.add("Block.Action", boost::bind(&LLPanelBlockedList::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("Block.Check", boost::bind(&LLPanelBlockedList::isActionChecked, this, _2)); } BOOL LLPanelBlockedList::postBuild() { - mBlockedList = getChild<LLScrollListCtrl>("blocked"); + mBlockedList = getChild<LLBlockList>("blocked"); mBlockedList->setCommitOnSelectionChange(TRUE); - childSetCommitCallback("back", boost::bind(&LLPanelBlockedList::onBackBtnClick, this), NULL); + switch (gSavedSettings.getU32("BlockPeopleSortOrder")) + { + case E_SORT_BY_NAME: + mBlockedList->sortByName(); + break; + + case E_SORT_BY_TYPE: + mBlockedList->sortByType(); + break; + } + + // Use the context menu of the Block list for the Block tab gear menu. + LLToggleableMenu* blocked_gear_menu = mBlockedList->getContextMenu(); + if (blocked_gear_menu) + { + getChild<LLMenuButton>("blocked_gear_btn")->setMenu(blocked_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } - LLMuteList::getInstance()->addObserver(this); - - refreshBlockedList(); + getChild<LLButton>("unblock_btn")->setCommitCallback(boost::bind(&LLPanelBlockedList::unblockItem, this)); + getChild<LLFilterEditor>("blocked_filter_input")->setCommitCallback(boost::bind(&LLPanelBlockedList::onFilterEdit, this, _2)); return LLPanel::postBuild(); } @@ -94,78 +111,77 @@ void LLPanelBlockedList::onOpen(const LLSD& key) void LLPanelBlockedList::selectBlocked(const LLUUID& mute_id) { - mBlockedList->selectByID(mute_id); + mBlockedList->selectItemByUUID(mute_id); } void LLPanelBlockedList::showPanelAndSelect(const LLUUID& idToSelect) { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with(BLOCKED_PARAM_NAME, idToSelect)); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with(BLOCKED_PARAM_NAME, idToSelect)); } ////////////////////////////////////////////////////////////////////////// // Private Section ////////////////////////////////////////////////////////////////////////// -void LLPanelBlockedList::refreshBlockedList() +void LLPanelBlockedList::updateButtons() { - mBlockedList->deleteAllItems(); + bool hasSelected = NULL != mBlockedList->getSelectedItem(); + getChildView("unblock_btn")->setEnabled(hasSelected); +} - std::vector<LLMute> mutes = LLMuteList::getInstance()->getMutes(); - std::vector<LLMute>::iterator it; - for (it = mutes.begin(); it != mutes.end(); ++it) +void LLPanelBlockedList::unblockItem() +{ + LLBlockedListItem* item = mBlockedList->getBlockedItem(); + if (item) { - LLScrollListItem::Params item_p; - item_p.enabled(TRUE); - item_p.value(it->mID); // link UUID of blocked item with ScrollListItem - item_p.columns.add().column("item_name").value(it->mName);//.type("text"); - item_p.columns.add().column("item_type").value(it->getDisplayType());//.type("text").width(111); - - mBlockedList->addRow(item_p, ADD_BOTTOM); + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::instance().remove(mute); } } -void LLPanelBlockedList::updateButtons() +void LLPanelBlockedList::onCustomAction(const LLSD& userdata) { - bool hasSelected = NULL != mBlockedList->getFirstSelected(); - getChildView("Unblock")->setEnabled(hasSelected); -} - + const std::string command_name = userdata.asString(); - -void LLPanelBlockedList::onBackBtnClick() -{ - LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent()); - if(parent) + if ("block_obj_by_name" == command_name) + { + blockObjectByName(); + } + else if ("block_res_by_name" == command_name) + { + blockResidentByName(); + } + else if ("sort_by_name" == command_name) + { + mBlockedList->sortByName(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_type" == command_name) { - parent->openPreviousPanel(); + mBlockedList->sortByType(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_TYPE); } } -void LLPanelBlockedList::onRemoveBtnClick() +BOOL LLPanelBlockedList::isActionChecked(const LLSD& userdata) { - std::string name = mBlockedList->getSelectedItemLabel(); - LLUUID id = mBlockedList->getStringUUIDSelectedItem(); - LLMute mute(id, name); - - S32 last_selected = mBlockedList->getFirstSelectedIndex(); - if (LLMuteList::getInstance()->remove(mute)) + std::string item = userdata.asString(); + U32 sort_order = gSavedSettings.getU32("BlockPeopleSortOrder"); + + if ("sort_by_name" == item) + { + return E_SORT_BY_NAME == sort_order; + } + else if ("sort_by_type" == item) { - // Above removals may rebuild this dialog. - - if (last_selected == mBlockedList->getItemCount()) - { - // we were on the last item, so select the last item again - mBlockedList->selectNthItem(last_selected - 1); - } - else - { - // else select the item after the last item previously selected - mBlockedList->selectNthItem(last_selected); - } + return E_SORT_BY_TYPE == sort_order; } + + return false; } -void LLPanelBlockedList::onPickBtnClick() +void LLPanelBlockedList::blockResidentByName() { const BOOL allow_multiple = FALSE; const BOOL close_on_select = TRUE; @@ -176,11 +192,19 @@ void LLPanelBlockedList::onPickBtnClick() // addDependentFloater(picker); } -void LLPanelBlockedList::onBlockByNameClick() +void LLPanelBlockedList::blockObjectByName() { LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); } +void LLPanelBlockedList::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mBlockedList->setNameFilter(filter); +} + void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (names.empty() || ids.empty()) return; diff --git a/indra/newview/llpanelblockedlist.h b/indra/newview/llpanelblockedlist.h index 74ad82e32d..332349dfc0 100644 --- a/indra/newview/llpanelblockedlist.h +++ b/indra/newview/llpanelblockedlist.h @@ -30,21 +30,15 @@ #include "llpanel.h" #include "llmutelist.h" #include "llfloater.h" -// #include <vector> -// class LLButton; -// class LLLineEditor; -// class LLMessageSystem; -// class LLUUID; class LLAvatarName; -class LLScrollListCtrl; +class LLBlockList; -class LLPanelBlockedList - : public LLPanel, public LLMuteListObserver +class LLPanelBlockedList : public LLPanel { public: LLPanelBlockedList(); - ~LLPanelBlockedList(); + ~LLPanelBlockedList(){}; virtual BOOL postBuild(); virtual void draw(); @@ -59,25 +53,31 @@ public: * If it is LLUUID::null, nothing will be selected. */ static void showPanelAndSelect(const LLUUID& idToSelect); - - // LLMuteListObserver callback interface implementation. - /* virtual */ void onChange() { refreshBlockedList();} private: - void refreshBlockedList(); + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_TYPE = 1, + } ESortOrder; + void updateButtons(); // UI callbacks - void onBackBtnClick(); - void onRemoveBtnClick(); - void onPickBtnClick(); - void onBlockByNameClick(); + void unblockItem(); + void blockResidentByName(); + void blockObjectByName(); + void onFilterEdit(const std::string& search_string); + + // List commnads + void onCustomAction(const LLSD& userdata); + BOOL isActionChecked(const LLSD& userdata); void callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); static void callbackBlockByName(const std::string& text); private: - LLScrollListCtrl* mBlockedList; + LLBlockList* mBlockedList; }; //----------------------------------------------------------------------------- diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index eda0749cdb..389baa86cd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -1,31 +1,30 @@ -/** +/** * @file llpanelavatar.cpp * @brief LLPanelAvatar and related class implementations * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - #include "llfloaterreg.h" #include "llpanelimcontrolpanel.h" @@ -39,393 +38,7 @@ #include "llavatarlist.h" #include "llparticipantlist.h" #include "llimview.h" -#include "llvoicechannel.h" #include "llspeakers.h" #include "lltrans.h" -void LLPanelChatControlPanel::onCallButtonClicked() -{ - gIMMgr->startCall(mSessionId); -} - -void LLPanelChatControlPanel::onEndCallButtonClicked() -{ - gIMMgr->endCall(mSessionId); -} - -void LLPanelChatControlPanel::onOpenVoiceControlsClicked() -{ - LLFloaterReg::showInstance("voice_controls"); -} - -void LLPanelChatControlPanel::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) - { - return; - } - - updateCallButton(); -} - -void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - updateButtons(new_state); -} - -void LLPanelChatControlPanel::updateCallButton() -{ - // hide/show call button - bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); - - if (!session) - { - getChildView("call_btn")->setEnabled(false); - return; - } - - bool session_initialized = session->mSessionInitialized; - bool callback_enabled = session->mCallBackEnabled; - - BOOL enable_connect = session_initialized - && voice_enabled - && callback_enabled; - getChildView("call_btn")->setEnabled(enable_connect); -} - -void LLPanelChatControlPanel::updateButtons(LLVoiceChannel::EState state) -{ - bool is_call_started = state >= LLVoiceChannel::STATE_CALL_STARTED; - getChildView("end_call_btn_panel")->setVisible( is_call_started); - getChildView("voice_ctrls_btn_panel")->setVisible( is_call_started && findChild<LLView>("voice_ctrls_btn_panel")); - getChildView("call_btn_panel")->setVisible( ! is_call_started); - - getChildView("volume_ctrl_panel")->setVisible(state == LLVoiceChannel::STATE_CONNECTED); - - updateCallButton(); - -} - -LLPanelChatControlPanel::~LLPanelChatControlPanel() -{ - mVoiceChannelStateChangeConnection.disconnect(); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } -} - -BOOL LLPanelChatControlPanel::postBuild() -{ - childSetAction("call_btn", boost::bind(&LLPanelChatControlPanel::onCallButtonClicked, this)); - childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); - childSetAction("voice_ctrls_btn", boost::bind(&LLPanelChatControlPanel::onOpenVoiceControlsClicked, this)); - - LLVoiceClient::getInstance()->addObserver(this); - - return TRUE; -} - -void LLPanelChatControlPanel::setSessionId(const LLUUID& session_id) -{ - //Method is called twice for AdHoc and Group chat. Second time when server init reply received - mSessionId = session_id; - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionId); - if(voice_channel) - { - mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback(boost::bind(&LLPanelChatControlPanel::onVoiceChannelStateChanged, this, _1, _2)); - - //call (either p2p, group or ad-hoc) can be already in started state - updateButtons(voice_channel->getState()); - } -} - -LLPanelIMControlPanel::LLPanelIMControlPanel() -{ -} - -LLPanelIMControlPanel::~LLPanelIMControlPanel() -{ - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); -} - -BOOL LLPanelIMControlPanel::postBuild() -{ - childSetAction("view_profile_btn", boost::bind(&LLPanelIMControlPanel::onViewProfileButtonClicked, this)); - childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); - - childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this)); - childSetAction("teleport_btn", boost::bind(&LLPanelIMControlPanel::onTeleportButtonClicked, this)); - childSetAction("pay_btn", boost::bind(&LLPanelIMControlPanel::onPayButtonClicked, this)); - - childSetAction("mute_btn", boost::bind(&LLPanelIMControlPanel::onClickMuteVolume, this)); - childSetAction("block_btn", boost::bind(&LLPanelIMControlPanel::onClickBlock, this)); - childSetAction("unblock_btn", boost::bind(&LLPanelIMControlPanel::onClickUnblock, this)); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback(boost::bind(&LLPanelIMControlPanel::onVolumeChange, this, _2)); - - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); - - setFocusReceivedCallback(boost::bind(&LLPanelIMControlPanel::onFocusReceived, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -void LLPanelIMControlPanel::draw() -{ - bool is_muted = LLMuteList::getInstance()->isMuted(mAvatarID); - - getChild<LLUICtrl>("block_btn_panel")->setVisible(!is_muted); - getChild<LLUICtrl>("unblock_btn_panel")->setVisible(is_muted); - - if (getChildView("volume_ctrl_panel")->getVisible()) - { - - bool is_muted_voice = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - mute_btn->setValue( is_muted_voice ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted_voice ); - - F32 volume; - - if (is_muted_voice) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - - LLPanelChatControlPanel::draw(); -} - -void LLPanelIMControlPanel::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } -} - -void LLPanelIMControlPanel::onClickBlock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->add(mute); -} - -void LLPanelIMControlPanel::onClickUnblock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->remove(mute); -} - -void LLPanelIMControlPanel::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - -void LLPanelIMControlPanel::onTeleportButtonClicked() -{ - LLAvatarActions::offerTeleport(mAvatarID); -} -void LLPanelIMControlPanel::onPayButtonClicked() -{ - LLAvatarActions::pay(mAvatarID); -} - -void LLPanelIMControlPanel::onViewProfileButtonClicked() -{ - LLAvatarActions::showProfile(mAvatarID); -} - -void LLPanelIMControlPanel::onAddFriendButtonClicked() -{ - LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - std::string full_name = avatar_icon->getFullName(); - LLAvatarActions::requestFriendshipDialog(mAvatarID, full_name); -} - -void LLPanelIMControlPanel::onShareButtonClicked() -{ - LLAvatarActions::share(mAvatarID); -} - -void LLPanelIMControlPanel::onFocusReceived() -{ - // Disable all the buttons (Call, Teleport, etc) if disconnected. - if (gDisconnected) - { - setAllChildrenEnabled(FALSE); - } -} - -void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - LLIMModel& im_model = LLIMModel::instance(); - - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); - mAvatarID = im_model.getOtherParticipantID(session_id); - LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this); - - // Disable "Add friend" button for friends. - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } - - getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(mAvatarID); - - // Disable most profile buttons if the participant is - // not really an SL avatar (e.g., an Avaline caller). - LLIMModel::LLIMSession* im_session = - im_model.findIMSession(session_id); - if( im_session && !im_session->mOtherParticipantIsAvatar ) - { - getChildView("view_profile_btn")->setEnabled(FALSE); - getChildView("add_friend_btn")->setEnabled(FALSE); - - getChildView("share_btn")->setEnabled(FALSE); - getChildView("teleport_btn")->setEnabled(FALSE); - getChildView("pay_btn")->setEnabled(FALSE); - - getChild<LLTextBox>("avatar_name")->setValue(im_session->mName); - getChild<LLTextBox>("avatar_name")->setToolTip(im_session->mName); - } - else - { - // If the participant is an avatar, fetch the currect name - gCacheName->get(mAvatarID, false, - boost::bind(&LLPanelIMControlPanel::onNameCache, this, _1, _2, _3)); - } -} - -//virtual -void LLPanelIMControlPanel::changed(U32 mask) -{ - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } -} - -void LLPanelIMControlPanel::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group) -{ - if ( id == mAvatarID ) - { - std::string avatar_name = full_name; - getChild<LLTextBox>("avatar_name")->setValue(avatar_name); - getChild<LLTextBox>("avatar_name")->setToolTip(avatar_name); - - bool is_linden = LLStringUtil::endsWith(full_name, " Linden"); - getChild<LLUICtrl>("mute_btn")->setEnabled( !is_linden); - } -} - -LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): -mParticipantList(NULL) -{ -} - -BOOL LLPanelGroupControlPanel::postBuild() -{ - childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -LLPanelGroupControlPanel::~LLPanelGroupControlPanel() -{ - delete mParticipantList; - mParticipantList = NULL; -} - -// virtual -void LLPanelGroupControlPanel::draw() -{ - // Need to resort the participant list if it's in sort by recent speaker order. - if (mParticipantList) - mParticipantList->update(); - LLPanelChatControlPanel::draw(); -} - -void LLPanelGroupControlPanel::onGroupInfoButtonClicked() -{ - LLGroupActions::show(mGroupID); -} - -void LLPanelGroupControlPanel::onSortMenuItemClicked(const LLSD& userdata) -{ - // TODO: Check this code when when sort order menu will be added. (EM) - if (false && !mParticipantList) - return; - - std::string chosen_item = userdata.asString(); - - if (chosen_item == "sort_name") - { - mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); - } - -} - -void LLPanelGroupControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - LLPanelChatControlPanel::onVoiceChannelStateChanged(old_state, new_state); - mParticipantList->setSpeakingIndicatorsVisible(new_state >= LLVoiceChannel::STATE_CALL_STARTED); -} - -void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - mGroupID = session_id; - - // for group and Ad-hoc chat we need to include agent into list - if(!mParticipantList) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); - mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true,false); - } -} - - -LLPanelAdHocControlPanel::LLPanelAdHocControlPanel(const LLUUID& session_id):LLPanelGroupControlPanel(session_id) -{ -} - -BOOL LLPanelAdHocControlPanel::postBuild() -{ - //We don't need LLPanelGroupControlPanel::postBuild() to be executed as there is no group_info_btn at AdHoc chat - return LLPanelChatControlPanel::postBuild(); -} diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index bba847b5d4..02915ec4bb 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -28,14 +28,12 @@ #define LL_LLPANELIMCONTROLPANEL_H #include "llpanel.h" -#include "llvoicechannel.h" #include "llcallingcard.h" class LLParticipantList; -class LLPanelChatControlPanel +class LLPanelChatControlPanel : public LLPanel - , public LLVoiceClientStatusObserver { public: LLPanelChatControlPanel() : @@ -44,21 +42,6 @@ public: virtual BOOL postBuild(); - void onCallButtonClicked(); - void onEndCallButtonClicked(); - void onOpenVoiceControlsClicked(); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - virtual void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); - - void updateButtons(LLVoiceChannel::EState state); - - // Enables/disables call button depending on voice availability - void updateCallButton(); - virtual void setSessionId(const LLUUID& session_id); const LLUUID& getSessionId() { return mSessionId; } @@ -69,41 +52,6 @@ private: boost::signals2::connection mVoiceChannelStateChangeConnection; }; - -class LLPanelIMControlPanel : public LLPanelChatControlPanel, LLFriendObserver -{ -public: - LLPanelIMControlPanel(); - ~LLPanelIMControlPanel(); - - BOOL postBuild(); - - void setSessionId(const LLUUID& session_id); - - // LLFriendObserver trigger - virtual void changed(U32 mask); - -protected: - void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group); - -private: - void onViewProfileButtonClicked(); - void onAddFriendButtonClicked(); - void onShareButtonClicked(); - void onTeleportButtonClicked(); - void onPayButtonClicked(); - void onFocusReceived(); - - void onClickMuteVolume(); - void onClickBlock(); - void onClickUnblock(); - /*virtual*/ void draw(); - void onVolumeChange(const LLSD& data); - - LLUUID mAvatarID; -}; - - class LLPanelGroupControlPanel : public LLPanelChatControlPanel { public: @@ -121,9 +69,7 @@ protected: LLParticipantList* mParticipantList; private: - void onGroupInfoButtonClicked(); void onSortMenuItemClicked(const LLSD& userdata); - /*virtual*/ void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); }; class LLPanelAdHocControlPanel : public LLPanelGroupControlPanel diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 68a3b6d1cd..4bbab52e5a 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -102,7 +102,7 @@ void LLCheckFolderState::doFolder(LLFolderViewFolder* folder) // Counting only folders that pass the filter. // The listener check allow us to avoid counting the folder view // object itself because it has no listener assigned. - if (folder->hasFilteredDescendants() && folder->getListener()) + if (folder->getViewModelItem()->descendantsPassedFilter()) { if (folder->isOpen()) { @@ -138,7 +138,7 @@ private: // virtual void LLOpenFolderByID::doFolder(LLFolderViewFolder* folder) { - if (folder->getListener() && folder->getListener()->getUUID() == mFolderID) + if (folder->getViewModelItem() && static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem())->getUUID() == mFolderID) { if (!folder->isOpen()) { @@ -281,28 +281,21 @@ void LLLandmarksPanel::onShowOnMap() //virtual void LLLandmarksPanel::onShowProfile() { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(!cur_item) return; - cur_item->getListener()->performAction(mCurrentSelectedList->getModel(),"about"); + cur_item->performAction(mCurrentSelectedList->getModel(),"about"); } // virtual void LLLandmarksPanel::onTeleport() { - LLFolderViewItem* current_item = getCurSelectedItem(); - if (!current_item) - { - llwarns << "There are no selected list. No actions are performed." << llendl; - return; - } - - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp && listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* view_model_item = getCurSelectedViewModelItem(); + if (view_model_item && view_model_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - listenerp->openItem(); + view_model_item->openItem(); } } @@ -360,7 +353,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() LLFolderViewItem* cur_item = mFavoritesInventoryPanel->getRootFolder()->getCurSelectedItem(); if (!cur_item) return; - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { LLSD key; @@ -376,7 +369,7 @@ void LLLandmarksPanel::updateShowFolderState() if (!mLandmarksInventoryPanel->getFilter()) return; - bool show_all_folders = mLandmarksInventoryPanel->getRootFolder()->getFilterSubString().empty(); + bool show_all_folders = mLandmarksInventoryPanel->getFilterSubString().empty(); if (show_all_folders) { show_all_folders = category_has_descendents(mLandmarksInventoryPanel); @@ -417,8 +410,9 @@ void LLLandmarksPanel::setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_ bool LLLandmarksPanel::isLandmarkSelected() const { - LLFolderViewItem* current_item = getCurSelectedItem(); - if(current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* current_item = getCurSelectedViewModelItem(); + + if(current_item && current_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { return true; } @@ -440,10 +434,10 @@ bool LLLandmarksPanel::isReceivedFolderSelected() const void LLLandmarksPanel::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb) { - LLFolderViewItem* cur_item = getCurSelectedItem(); - if(cur_item && cur_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); + if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getListener()->getUUID(), cb); + LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb); if (landmark) { cb(landmark); @@ -456,6 +450,17 @@ LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } +LLFolderViewModelItemInventory* LLLandmarksPanel::getCurSelectedViewModelItem() const +{ + LLFolderViewItem* cur_item = getCurSelectedItem(); + if (cur_item) + { + return static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); + } + return NULL; +} + + LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, const std::string& tab_name, const LLUUID& obj_id, @@ -466,7 +471,7 @@ LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPa LLFolderView* root = inventory_list->getRootFolder(); - LLFolderViewItem* item = root->getItemByID(obj_id); + LLFolderViewItem* item = inventory_list->getItemByID(obj_id); if (!item) return NULL; @@ -508,12 +513,12 @@ void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data) // We have to make request to sever to get parcel_id and snaption_id. if(isLandmarkSelected()) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return; - LLUUID id = cur_item->getListener()->getUUID(); + LLUUID id = cur_item->getUUID(); LLInventoryItem* inv_item = mCurrentSelectedList->getModel()->getItem(id); doActionOnCurSelectedLandmark(boost::bind( - &LLLandmarksPanel::doProcessParcelInfo, this, _1, cur_item, inv_item, parcel_data)); + &LLLandmarksPanel::doProcessParcelInfo, this, _1, getCurSelectedItem(), inv_item, parcel_data)); } } @@ -665,20 +670,20 @@ void LLLandmarksPanel::deselectOtherThan(const LLPlacesInventoryPanel* inventory { if (inventory_list != mFavoritesInventoryPanel) { - mFavoritesInventoryPanel->getRootFolder()->clearSelection(); + mFavoritesInventoryPanel->clearSelection(); } if (inventory_list != mLandmarksInventoryPanel) { - mLandmarksInventoryPanel->getRootFolder()->clearSelection(); + mLandmarksInventoryPanel->clearSelection(); } if (inventory_list != mMyInventoryPanel) { - mMyInventoryPanel->getRootFolder()->clearSelection(); + mMyInventoryPanel->clearSelection(); } if (inventory_list != mLibraryInventoryPanel) { - mLibraryInventoryPanel->getRootFolder()->clearSelection(); + mLibraryInventoryPanel->clearSelection(); } } @@ -731,14 +736,9 @@ void LLLandmarksPanel::onActionsButtonClick() { LLToggleableMenu* menu = mGearFolderMenu; - LLFolderViewItem* cur_item = NULL; if(mCurrentSelectedList) { - cur_item = mCurrentSelectedList->getRootFolder()->getCurSelectedItem(); - if(!cur_item) - return; - - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = getCurSelectedViewModelItem(); if(!listenerp) return; @@ -776,6 +776,9 @@ void LLLandmarksPanel::onTrashButtonClick() const void LLLandmarksPanel::onAddAction(const LLSD& userdata) const { + LLFolderViewModelItemInventory* view_model = getCurSelectedViewModelItem(); + LLFolderViewItem* item = getCurSelectedItem(); + std::string command_name = userdata.asString(); if("add_landmark" == command_name) { @@ -791,24 +794,24 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const } else if ("category" == command_name) { - LLFolderViewItem* item = getCurSelectedItem(); if (item && mCurrentSelectedList == mLandmarksInventoryPanel) { - LLFolderViewEventListener* folder_bridge = NULL; - if (item-> getListener()->getInventoryType() + LLFolderViewModelItem* folder_bridge = NULL; + + if (view_model->getInventoryType() == LLInventoryType::IT_LANDMARK) { // for a landmark get parent folder bridge - folder_bridge = item->getParentFolder()->getListener(); + folder_bridge = item->getParentFolder()->getViewModelItem(); } - else if (item-> getListener()->getInventoryType() + else if (view_model->getInventoryType() == LLInventoryType::IT_CATEGORY) { // for a folder get its own bridge - folder_bridge = item->getListener(); + folder_bridge = view_model; } - menu_create_inventory_item(mCurrentSelectedList->getRootFolder(), + menu_create_inventory_item(mCurrentSelectedList, dynamic_cast<LLFolderBridge*> (folder_bridge), LLSD( "category"), gInventory.findCategoryUUIDForType( LLFolderType::FT_LANDMARK)); @@ -816,7 +819,7 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const else { //in case My Landmarks tab is completely empty (thus cannot be determined as being selected) - menu_create_inventory_item(mLandmarksInventoryPanel->getRootFolder(), NULL, LLSD("category"), + menu_create_inventory_item(mLandmarksInventoryPanel, NULL, LLSD("category"), gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK)); if (mMyLandmarksAccordionTab) @@ -834,9 +837,9 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const std::string command_name = userdata.asString(); if("copy_slurl" == command_name) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(cur_item) - LLLandmarkActions::copySLURLtoClipboard(cur_item->getListener()->getUUID()); + LLLandmarkActions::copySLURLtoClipboard(cur_item->getUUID()); } else if ( "paste" == command_name) { @@ -848,7 +851,7 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const } else { - mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name); + mCurrentSelectedList->doToSelected(command_name); } } @@ -893,7 +896,7 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata) { if(mCurrentSelectedList) { - mCurrentSelectedList->getRootFolder()->doToSelected(&gInventory, userdata); + mCurrentSelectedList->doToSelected(userdata); } } } @@ -977,18 +980,13 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (!root_folder_view) return false; - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_uuids = root_folder_view->getSelectionList(); // Allow to execute the command only if it can be applied to all selected items. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; - // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) return false; if (!canItemBeModified(command_name, item)) return false; @@ -1012,10 +1010,10 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const if ("show_on_map" == command_name) { - LLFolderViewItem* cur_item = root_folder_view->getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return false; - LLViewerInventoryItem* inv_item = cur_item->getInventoryItem(); + LLViewerInventoryItem* inv_item = dynamic_cast<LLViewerInventoryItem*>(cur_item->getInventoryObject()); if (!inv_item) return false; LLUUID asset_uuid = inv_item->getAssetUUID(); @@ -1049,7 +1047,7 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (mCurrentSelectedList) { - std::set<LLUUID> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); if (!selection.empty()) { return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); @@ -1105,27 +1103,23 @@ void LLLandmarksPanel::onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param) { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = root_folder_view->getSelectionList(); // Iterate through selected items to find out if any of these items are in Trash // or all the items are in Trash category. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_items.begin(); iter != selected_items.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) continue; - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if(!listenerp) continue; // Trash category itself should not be included because it can't be // actually restored from trash. - are_all_items_in_trash &= listenerp->isItemInTrash() && *iter != trash_id; + are_all_items_in_trash &= listenerp->isItemInTrash() && listenerp->getUUID() != trash_id; // If there are any selected items in Trash including the Trash category itself // we show "Restore Item" in context menu and hide other irrelevant items. @@ -1164,7 +1158,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold bool can_be_modified = false; // landmarks can be modified in any other accordion... - if (item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + if (static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem())->getInventoryType() == LLInventoryType::IT_LANDMARK) { can_be_modified = true; @@ -1202,7 +1196,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold if (can_be_modified) { - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if ("cut" == command_name) { @@ -1262,8 +1256,9 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data); if (item) { - LLFolderViewItem* fv_item = (mCurrentSelectedList && mCurrentSelectedList->getRootFolder()) ? - mCurrentSelectedList->getRootFolder()->getItemByID(item->getUUID()) : NULL; + LLFolderViewItem* fv_item = mCurrentSelectedList + ? mCurrentSelectedList->getItemByID(item->getUUID()) + : NULL; if (fv_item) { @@ -1391,7 +1386,7 @@ void LLLandmarksPanel::doCreatePick(LLLandmark* landmark) static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::string& string) { // When search is cleared, restore the old folder state. - if (!inventory_list->getRootFolder()->getFilterSubString().empty() && string == "") + if (!inventory_list->getFilterSubString().empty() && string == "") { inventory_list->setFilterSubString(LLStringUtil::null); // Re-open folders that were open before @@ -1405,7 +1400,7 @@ static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::strin } // save current folder open state if no filter currently applied - if (inventory_list->getRootFolder()->getFilterSubString().empty()) + if (inventory_list->getFilterSubString().empty()) { inventory_list->saveFolderState(); } diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index b2f4e92473..aa5f69739d 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -44,6 +44,7 @@ class LLMenuGL; class LLToggleableMenu; class LLInventoryPanel; class LLPlacesInventoryPanel; +class LLFolderViewModelItemInventory; class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver { @@ -87,6 +88,7 @@ protected: bool isReceivedFolderSelected() const; void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb); LLFolderViewItem* getCurSelectedItem() const; + LLFolderViewModelItemInventory* getCurSelectedViewModelItem() const; /** * Selects item with "obj_id" in "inventory_list" and scrolls accordion diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c11597f532..fea27b37d3 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -47,12 +47,14 @@ #include "llresmgr.h" #include "llscrollcontainer.h" #include "llsdserialize.h" +#include "llsdparam.h" #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltooldraganddrop.h" #include "llviewermenu.h" #include "llviewertexturelist.h" #include "llsidepanelinventory.h" +#include "llfolderview.h" const std::string FILTERS_FILENAME("filters.xml"); @@ -171,7 +173,10 @@ BOOL LLPanelMainInventory::postBuild() { LLSD recent_items = savedFilterState.get( recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); + LLInventoryFilter::Params p; + LLParamSDParser parser; + parser.readSD(recent_items, p); + recent_items_panel->getFilter()->fromParams(p); } } @@ -212,21 +217,25 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) if (filter) { LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLInventoryFilter::Params p; + filter->toParams(p); + if (p.validateBlock(false)) + { + LLParamSDParser().writeSD(filterState, p); + filterRoot[filter->getName()] = filterState; + } } } - LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items"); - if (recent_items_panel) + LLInventoryFilter* filter = findChild<LLInventoryPanel>("Recent Items")->getFilter(); + if (filter) { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } + LLSD filterState; + LLInventoryFilter::Params p; + filter->toParams(p); + LLParamSDParser parser; + parser.writeSD(filterState, p); + filterRoot[filter->getName()] = filterState; } std::ostringstream filterSaveName; @@ -285,7 +294,7 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) void LLPanelMainInventory::doToSelected(const LLSD& userdata) { - getPanel()->getRootFolder()->doToSelected(&gInventory, userdata); + getPanel()->doToSelected(userdata); } void LLPanelMainInventory::closeAllFolders() @@ -306,7 +315,7 @@ void LLPanelMainInventory::newWindow() void LLPanelMainInventory::doCreate(const LLSD& userdata) { - menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); + menu_create_inventory_item(getPanel(), NULL, userdata); } void LLPanelMainInventory::resetFilters() @@ -417,7 +426,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) } // save current folder open state if no filter currently applied - if (!mActivePanel->getRootFolder()->isFilterModified()) + if (!mActivePanel->getFilter()->isNotDefault()) { mSavedFolderState->setApply(FALSE); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -961,7 +970,7 @@ void LLPanelMainInventory::onTrashButtonClick() void LLPanelMainInventory::onClipboardAction(const LLSD& userdata) { std::string command_name = userdata.asString(); - getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name); + getActivePanel()->doToSelected(command_name); } void LLPanelMainInventory::saveTexture(const LLSD& userdata) @@ -972,7 +981,7 @@ void LLPanelMainInventory::saveTexture(const LLSD& userdata) return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES); if (preview_texture) { @@ -1045,7 +1054,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID item_id = current_item->getListener()->getUUID(); + const LLUUID item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item) { @@ -1060,7 +1069,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - current_item->getListener()->performAction(getActivePanel()->getModel(), "goto"); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); } if (command_name == "find_links") @@ -1070,8 +1079,8 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); - const std::string &item_name = current_item->getListener()->getName(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); + const std::string &item_name = current_item->getViewModelItem()->getName(); mFilterSubString = item_name; LLInventoryFilter *filter = mActivePanel->getFilter(); filter->setFilterSubString(item_name); @@ -1089,11 +1098,11 @@ bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata) LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (current_item) { - LLViewerInventoryItem *inv_item = current_item->getInventoryItem(); + LLViewerInventoryItem *inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject()); if(inv_item) { bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); - LLInventoryType::EType curr_type = current_item->getListener()->getInventoryType(); + LLInventoryType::EType curr_type = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryType(); return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT); } } @@ -1110,15 +1119,14 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (root) { can_delete = TRUE; - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.empty()) return FALSE; - for (std::set<LLUUID>::iterator iter = selection_set.begin(); + for (std::set<LLFolderViewItem*>::iterator iter = selection_set.begin(); iter != selection_set.end(); ++iter) { - const LLUUID &item_id = (*iter); - LLFolderViewItem *item = root->getItemByID(item_id); - const LLFolderViewEventListener *listener = item->getListener(); + LLFolderViewItem *item = *iter; + const LLFolderViewModelItemInventory *listener = static_cast<const LLFolderViewModelItemInventory*>(item->getViewModelItem()); llassert(listener); if (!listener) return FALSE; can_delete &= listener->isItemRemovable(); @@ -1136,7 +1144,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsLinkType() && !item->getIsBrokenLink()) { @@ -1148,11 +1156,11 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "find_links") { LLFolderView* root = getActivePanel()->getRootFolder(); - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.size() != 1) return FALSE; LLFolderViewItem* current_item = root->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLInventoryObject *obj = gInventory.getObject(item_id); if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType())) { @@ -1165,7 +1173,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsBrokenLink()) { diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp index 66c9c323cb..3547156197 100644 --- a/indra/newview/llpanelmarketplaceinbox.cpp +++ b/indra/newview/llpanelmarketplaceinbox.cpp @@ -94,7 +94,7 @@ LLInventoryPanel * LLPanelMarketplaceInbox::setupInventoryPanel() mInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mInventoryPanel->setSortOrder(LLInventoryFilter::SO_DATE); + mInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_DATE); mInventoryPanel->getFilter()->markDefault(); // Set selection callback for proper update of inventory status buttons @@ -139,7 +139,7 @@ U32 LLPanelMarketplaceInbox::getFreshItemCount() const if (mInventoryPanel) { - const LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); + LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); if (inbox_folder) { diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index 678e4f2843..6e5a522297 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -29,7 +29,8 @@ #include "llpanelmarketplaceinboxinventory.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -110,6 +111,7 @@ void LLInboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& para LLAssetType::AT_CATEGORY, LLInventoryType::IT_CATEGORY, this, + &mInventoryViewModel, NULL, root_id); @@ -121,14 +123,6 @@ LLFolderViewFolder * LLInboxInventoryPanel::createFolderViewFolder(LLInvFVBridge LLInboxFolderViewFolder::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -141,14 +135,6 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b LLInboxFolderViewItem::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -207,7 +193,7 @@ void LLInboxFolderViewFolder::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) @@ -229,15 +215,16 @@ void LLInboxFolderViewFolder::deFreshify() gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } -void LLInboxFolderViewFolder::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; - - if (mParentFolder == mRoot) - { - computeFreshness(); - } -} +// TODO RN: move this behavior to modelview? +//void LLInboxFolderViewFolder::setCreationDate(time_t creation_date_utc) +//{ +// mCreationDate = creation_date_utc; +// +// if (LLFolderViewItem::mParentFolder == mRoot) +// { +// computeFreshness(); +// } +//} // // LLInboxFolderViewItem Implementation @@ -253,9 +240,9 @@ LLInboxFolderViewItem::LLInboxFolderViewItem(const Params& p) #endif } -BOOL LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +BOOL LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder) { - BOOL retval = LLFolderViewItem::addToFolder(folder, root); + BOOL retval = LLFolderViewItem::addToFolder(folder); #if SUPPORTING_FRESH_ITEM_COUNT // Compute freshness if our parent is the root folder for the inbox @@ -303,7 +290,7 @@ void LLInboxFolderViewItem::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index d6b827ee3e..209f3a4098 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -81,8 +81,6 @@ public: bool isFresh() const { return mFresh; } protected: - void setCreationDate(time_t creation_date_utc); - bool mFresh; }; @@ -102,7 +100,7 @@ public: LLInboxFolderViewItem(const Params& p); - BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); + BOOL addToFolder(LLFolderViewFolder* folder); BOOL handleDoubleClick(S32 x, S32 y, MASK mask); void draw(); diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.cpp b/indra/newview/llpanelmarketplaceoutboxinventory.cpp index ff62cb23db..2885dd6266 100644 --- a/indra/newview/llpanelmarketplaceoutboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceoutboxinventory.cpp @@ -28,8 +28,8 @@ #include "llpanelmarketplaceoutboxinventory.h" -#include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -77,6 +77,7 @@ void LLOutboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& par LLAssetType::AT_CATEGORY, LLInventoryType::IT_CATEGORY, this, + &mInventoryViewModel, NULL, root_id); @@ -88,14 +89,6 @@ LLFolderViewFolder * LLOutboxInventoryPanel::createFolderViewFolder(LLInvFVBridg LLOutboxFolderViewFolder::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -108,14 +101,6 @@ LLFolderViewItem * LLOutboxInventoryPanel::createFolderViewItem(LLInvFVBridge * LLFolderViewItem::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 1ca24f3031..5887f4d244 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -71,12 +71,13 @@ /// Class LLTaskInvFVBridge ///---------------------------------------------------------------------------- -class LLTaskInvFVBridge : public LLFolderViewEventListener +class LLTaskInvFVBridge : public LLFolderViewModelItemInventory { protected: LLUUID mUUID; std::string mName; mutable std::string mDisplayName; + mutable std::string mSearchableName; LLPanelObjectInventory* mPanel; U32 mFlags; LLAssetType::EType mAssetType; @@ -102,26 +103,29 @@ public: S32 getPrice(); static bool commitBuyItem(const LLSD& notification, const LLSD& response); - // LLFolderViewEventListener functionality + // LLFolderViewModelItemInventory functionality virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + virtual const std::string& getSearchableName() const; + virtual PermissionMask getPermissionMask() const { return PERM_NONE; } /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); + virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual BOOL canOpenItem() const { return FALSE; } virtual void closeItem() {} - virtual void previewItem(); virtual void selectItem() {} virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemMovable() const; virtual BOOL isItemRemovable() const; virtual BOOL removeItem(); - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* parent_listener); + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -131,11 +135,15 @@ public: virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); virtual BOOL isUpToDate() const { return TRUE; } - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return LLInventoryType::IT_NONE; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + virtual EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const { return findInvObject(); } + // LLDragAndDropBridge functionality + virtual LLToolDragAndDrop::ESource getDragSource() const { return LLToolDragAndDrop::SOURCE_WORLD; } virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -147,7 +155,8 @@ LLTaskInvFVBridge::LLTaskInvFVBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, - U32 flags): + U32 flags) +: LLFolderViewModelItemInventory(panel->getRootViewModel()), mUUID(uuid), mName(name), mPanel(panel), @@ -330,15 +339,27 @@ const std::string& LLTaskInvFVBridge::getDisplayName() const } } + mSearchableName.assign(mDisplayName + getLabelSuffix()); + return mDisplayName; } +const std::string& LLTaskInvFVBridge::getSearchableName() const +{ + return mSearchableName; +} + + // BUG: No creation dates for task inventory time_t LLTaskInvFVBridge::getCreationDate() const { return 0; } +void LLTaskInvFVBridge::setCreationDate(time_t creation_date_utc) +{} + + LLUIImagePtr LLTaskInvFVBridge::getIcon() const { const BOOL item_is_multi = (mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS); @@ -352,11 +373,6 @@ void LLTaskInvFVBridge::openItem() lldebugs << "LLTaskInvFVBridge::openItem()" << llendl; } -void LLTaskInvFVBridge::previewItem() -{ - openItem(); -} - BOOL LLTaskInvFVBridge::isItemRenameable() const { if(gAgent.isGodlike()) return TRUE; @@ -467,7 +483,7 @@ BOOL LLTaskInvFVBridge::removeItem() return FALSE; } -void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLTaskInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { if (!mPanel) { @@ -507,7 +523,7 @@ void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& } } -void LLTaskInvFVBridge::move(LLFolderViewEventListener* parent_listener) +void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } @@ -709,7 +725,7 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemRemovable() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -717,6 +733,7 @@ public: std::string& tooltip_msg); virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); + virtual EInventorySortGroup getSortGroup() const { return SG_NORMAL_FOLDER; } }; LLTaskCategoryBridge::LLTaskCategoryBridge( @@ -739,15 +756,7 @@ const std::string& LLTaskCategoryBridge::getDisplayName() const if (cat) { - // Localize "Contents" folder. - if (cat->getParentUUID().isNull() && cat->getName() == "Contents") - { - mDisplayName.assign(LLTrans::getString("ViewerObjectContents")); - } - else - { - mDisplayName.assign(cat->getName()); - } + mDisplayName.assign(cat->getName()); } return mDisplayName; @@ -775,7 +784,7 @@ void LLTaskCategoryBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } -BOOL LLTaskCategoryBridge::hasChildren() const +bool LLTaskCategoryBridge::hasChildren() const { // return TRUE if we have or do know know if we have children. // *FIX: For now, return FALSE - we will know for sure soon enough. @@ -1514,7 +1523,7 @@ BOOL LLPanelObjectInventory::postBuild() void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { - mFolders->doToSelected(&gInventory, userdata); + LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); } void LLPanelObjectInventory::clearContents() @@ -1526,6 +1535,8 @@ void LLPanelObjectInventory::clearContents() LLToolDragAndDrop::getInstance()->endDrag(); } + clearItemIDs(); + if( mScroller ) { // removes mFolders @@ -1541,21 +1552,26 @@ void LLPanelObjectInventory::reset() { clearContents(); - //setBorderVisible(FALSE); - mCommitCallbackRegistrar.pushScope(); // push local callbacks LLRect dummy_rect(0, 1, 1, 0); LLFolderView::Params p; p.name = "task inventory"; p.title = "task inventory"; - p.task_id = getTaskUUID(); p.parent_panel = this; p.tool_tip= LLTrans::getString("PanelContentsTooltip"); p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL); + p.folder_indentation = -14; // subtract space normally reserved for folder expanders + p.view_model = &mInventoryViewModel; mFolders = LLUICtrlFactory::create<LLFolderView>(p); // this ensures that we never say "searching..." or "no items found" - mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + //TODO RN: make this happen by manipulating filter object directly + LLInventoryFilter* inventoryFilter = dynamic_cast<LLInventoryFilter*>(mFolders->getFolderViewModel()->getFilter()); + if(inventoryFilter) + { + inventoryFilter->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + } + mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); if (hasFocus()) @@ -1600,7 +1616,7 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, iter != inventory->end(); ) { LLInventoryObject* item = *iter++; - LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properites", item->getUUID()); + LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properties", item->getUUID()); if(floater) { floater->refresh(); @@ -1615,15 +1631,20 @@ void LLPanelObjectInventory::updateInventory() // << " panel UUID: " << panel->mTaskUUID << "\n" // << " task UUID: " << object->mID << llendl; // We're still interested in this task's inventory. - std::set<LLUUID> selected_items; + std::vector<LLUUID> selected_item_ids; + std::set<LLFolderViewItem*> selected_items; BOOL inventory_has_focus = FALSE; - if (mHaveInventory) + if (mHaveInventory && mFolders) { selected_items = mFolders->getSelectionList(); inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); } - - reset(); + for (std::set<LLFolderViewItem*>::iterator it = selected_items.begin(), end_it = selected_items.end(); + it != end_it; + ++it) + { + selected_item_ids.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); if (objectp) @@ -1631,19 +1652,21 @@ void LLPanelObjectInventory::updateInventory() LLInventoryObject* inventory_root = objectp->getInventoryRoot(); LLInventoryObject::object_list_t contents; objectp->getInventoryContents(contents); + if (inventory_root) { - createFolderViews(inventory_root, contents); - mHaveInventory = TRUE; + reset(); mIsInventoryEmpty = FALSE; + createFolderViews(inventory_root, contents); mFolders->setEnabled(TRUE); } else { // TODO: create an empty inventory mIsInventoryEmpty = TRUE; - mHaveInventory = TRUE; } + + mHaveInventory = TRUE; } else { @@ -1653,11 +1676,12 @@ void LLPanelObjectInventory::updateInventory() } // restore previous selection - std::set<LLUUID>::iterator selection_it; - BOOL first_item = TRUE; - for (selection_it = selected_items.begin(); selection_it != selected_items.end(); ++selection_it) + std::vector<LLUUID>::iterator selection_it; + bool first_item = true; + for (selection_it = selected_item_ids.begin(); selection_it != selected_item_ids.end(); ++selection_it) { - LLFolderViewItem* selected_item = mFolders->getItemByID(*selection_it); + LLFolderViewItem* selected_item = getItemByID(*selection_it); + if (selected_item) { //HACK: "set" first item then "change" each other one to get keyboard focus right @@ -1673,7 +1697,10 @@ void LLPanelObjectInventory::updateInventory() } } - mFolders->requestArrange(); + if (mFolders) + { + mFolders->requestArrange(); + } mInventoryNeedsUpdate = FALSE; // Edit menu handler is set in onFocusReceived } @@ -1694,19 +1721,20 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root bridge = LLTaskInvFVBridge::createObjectBridge(this, inventory_root); if(bridge) { - LLFolderViewFolder* new_folder = NULL; LLFolderViewFolder::Params p; p.name = inventory_root->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); + p.tool_tip = p.name; p.root = mFolders; p.listener = bridge; - p.tool_tip = p.name; - new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); - new_folder->addToFolder(mFolders, mFolders); + + LLFolderViewFolder* new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); + new_folder->addToFolder(mFolders); new_folder->toggleOpen(); - createViewsForCategory(&contents, inventory_root, new_folder); + if (!contents.empty()) + { + createViewsForCategory(&contents, inventory_root, new_folder); + } } } @@ -1738,8 +1766,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewFolder::Params p; p.name = obj->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); p.root = mFolders; p.listener = bridge; p.tool_tip = p.name; @@ -1751,7 +1777,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewItem::Params params; params.name(obj->getName()); - params.icon(bridge->getIcon()); params.creation_date(bridge->getCreationDate()); params.root(mFolders); params.listener(bridge); @@ -1759,7 +1784,8 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li params.tool_tip = params.name; view = LLUICtrlFactory::create<LLFolderViewItem> (params); } - view->addToFolder(folder, mFolders); + view->addToFolder(folder); + addItemID(obj->getUUID(), view); } } @@ -1827,6 +1853,7 @@ void LLPanelObjectInventory::refresh() removeVOInventoryListener(); clearContents(); } + mInventoryViewModel.setTaskID(mTaskUUID); //llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl; } @@ -1914,7 +1941,10 @@ void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; - + if (self->mFolders) + { + self->mFolders->update(); + } if (self->mInventoryNeedsUpdate) { self->updateInventory(); @@ -1939,3 +1969,32 @@ void LLPanelObjectInventory::onFocusReceived() LLPanel::onFocusReceived(); } + + +LLFolderViewItem* LLPanelObjectInventory::getItemByID( const LLUUID& id ) +{ + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +void LLPanelObjectInventory::removeItemID( const LLUUID& id ) +{ + mItemMap.erase(id); +} + +void LLPanelObjectInventory::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLPanelObjectInventory::clearItemIDs() +{ + mItemMap.clear(); +} + diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h index 607b705f7f..f497c695b3 100644 --- a/indra/newview/llpanelobjectinventory.h +++ b/indra/newview/llpanelobjectinventory.h @@ -29,6 +29,7 @@ #include "llvoinventorylistener.h" #include "llpanel.h" +#include "llinventorypanel.h" // for LLFolderViewModelInventory #include "llinventory.h" @@ -55,6 +56,8 @@ public: virtual BOOL postBuild(); + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } + void doToSelected(const LLSD& userdata); void refresh(); @@ -85,8 +88,15 @@ protected: LLInventoryObject* parent, LLFolderViewFolder* folder); void clearContents(); + LLFolderViewItem* getItemByID(const LLUUID& id); + + void addItemID( const LLUUID& id, LLFolderViewItem* itemp ); + void removeItemID(const LLUUID& id); + void clearItemIDs(); private: + std::map<LLUUID, LLFolderViewItem*> mItemMap; + LLScrollContainer* mScroller; LLFolderView* mFolders; @@ -94,6 +104,7 @@ private: BOOL mHaveInventory; BOOL mIsInventoryEmpty; BOOL mInventoryNeedsUpdate; + LLFolderViewModelInventory mInventoryViewModel; }; #endif // LL_LLPANELOBJECTINVENTORY_H diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 35e2e96bab..d690a18477 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -270,7 +270,7 @@ private: if (inventory_panel->getVisible()) { - inventory_panel->setSortOrder(sort_order); + inventory_panel->getFolderViewModel()->setSorter(sort_order); } else { @@ -738,7 +738,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) } // save current folder open state if no filter currently applied - if (mInventoryItemsPanel->getRootFolder()->getFilterSubString().empty()) + if (mInventoryItemsPanel->getFilterSubString().empty()) { mSavedFolderState->setApply(FALSE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -885,13 +885,13 @@ LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionT { if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> selected_uuids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - result.second = selected_uuids.size(); + result.second = selected_items.size(); if (result.second == 1) { - result.first = getWearableTypeByItemUUID(*(selected_uuids.begin())); + result.first = getWearableTypeByItemUUID(static_cast<LLFolderViewModelItemInventory*>((*selected_items.begin())->getViewModelItem())->getUUID()); } } else if (mWearableItemsList != NULL && mWearableItemsList->getVisible()) @@ -1310,7 +1310,7 @@ void LLPanelOutfitEdit::getCurrentItemUUID(LLUUID& selected_id) LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); if (!curr_item) return; - LLFolderViewEventListener* listenerp = curr_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(curr_item->getViewModelItem()); if (!listenerp) return; selected_id = listenerp->getUUID(); @@ -1327,9 +1327,13 @@ void LLPanelOutfitEdit::getSelectedItemsUUID(uuid_vec_t& uuid_list) void (uuid_vec_t::* tmp)(LLUUID const &) = &uuid_vec_t::push_back; if (mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - - std::for_each(item_set.begin(), item_set.end(), boost::bind( tmp, &uuid_list, _1)); + std::set<LLFolderViewItem*> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = item_set.begin(), end_it = item_set.end(); + it != end_it; + ++it) + { + uuid_list.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } else if (mWearablesListViewPanel->getVisible()) { @@ -1374,13 +1378,13 @@ void LLPanelOutfitEdit::saveListSelection() { if(mWearablesListViewPanel->getVisible()) { - std::set<LLUUID> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); if(!selected_ids.size()) return; - for (std::set<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + for (std::set<LLFolderViewItem*>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - mWearableItemsList->selectItemByUUID(*item_id, true); + mWearableItemsList->selectItemByUUID(static_cast<LLFolderViewModelItemInventory*>((*item_id)->getViewModelItem())->getUUID(), true); } mWearableItemsList->scrollToShowFirstSelectedItem(); } @@ -1398,7 +1402,7 @@ void LLPanelOutfitEdit::saveListSelection() for(std::vector<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - LLFolderViewItem* item = root->getItemByID(*item_id); + LLFolderViewItem* item = mInventoryItemsPanel->getItemByID(*item_id); if (!item) continue; LLFolderViewFolder* parent = item->getParentFolder(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index f1380e7a36..260de40eef 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -72,6 +72,7 @@ static const std::string NEARBY_TAB_NAME = "nearby_panel"; static const std::string FRIENDS_TAB_NAME = "friends_panel"; static const std::string GROUP_TAB_NAME = "groups_panel"; static const std::string RECENT_TAB_NAME = "recent_panel"; +static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; @@ -492,26 +493,37 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), - mFilterSubString(LLStringUtil::null), - mFilterSubStringOrig(LLStringUtil::null), - mFilterEditor(NULL), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), mNearbyList(NULL), mRecentList(NULL), mGroupList(NULL), - mNearbyGearButton(NULL), - mFriendsGearButton(NULL), - mGroupsGearButton(NULL), - mRecentGearButton(NULL), mMiniMap(NULL) { mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); - mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + + mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); + mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Group.Minus", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Chat", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Gear", boost::bind(&LLPanelPeople::onGearButtonClicked, this, _1)); + + mCommitCallbackRegistrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); + + mEnableCallbackRegistrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); + + mEnableCallbackRegistrar.add("People.Group.Plus.Validate", boost::bind(&LLPanelPeople::onGroupPlusButtonValidate, this)); } LLPanelPeople::~LLPanelPeople() @@ -525,13 +537,6 @@ LLPanelPeople::~LLPanelPeople() { LLVoiceClient::getInstance()->removeObserver(this); } - - if (mGroupPlusMenuHandle.get()) mGroupPlusMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mGroupsViewSortMenuHandle.get()) mGroupsViewSortMenuHandle.get()->die(); - if (mRecentViewSortMenuHandle.get()) mRecentViewSortMenuHandle.get()->die(); - } void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -553,11 +558,15 @@ void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LL BOOL LLPanelPeople::postBuild() { - mFilterEditor = getChild<LLFilterEditor>("filter_input"); - mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); + mSavedFilters.resize(mTabContainer->getTabCount()); + mSavedOriginalFilters.resize(mTabContainer->getTabCount()); LLPanel* friends_tab = getChild<LLPanel>(FRIENDS_TAB_NAME); // updater is active only if panel is visible to user. @@ -601,14 +610,6 @@ BOOL LLPanelPeople::postBuild() setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); - LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME); - groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this)); - groups_panel->childSetAction("plus_btn", boost::bind(&LLPanelPeople::onGroupPlusButtonClicked, this)); - - LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME); - friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); - friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); @@ -629,6 +630,19 @@ BOOL LLPanelPeople::postBuild() mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + LLMenuButton* groups_gear_btn = getChild<LLMenuButton>("groups_gear_btn"); + + // Use the context menu of the Groups list for the Groups tab gear menu. + LLToggleableMenu* groups_gear_menu = mGroupList->getContextMenu(); + if (groups_gear_menu) + { + groups_gear_btn->setMenu(groups_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + else + { + llwarns << "People->Groups list menu not found" << llendl; + } + LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all"); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList)); @@ -637,70 +651,9 @@ BOOL LLPanelPeople::postBuild() accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mOnlineFriendList)); - buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this)); - buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); - buttonSetAction("chat_btn", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); - buttonSetAction("im_btn", boost::bind(&LLPanelPeople::onImButtonClicked, this)); - buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this)); - buttonSetAction("group_call_btn", boost::bind(&LLPanelPeople::onGroupCallButtonClicked, this)); - buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); - buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); - // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(NEARBY_TAB_NAME); - // Create menus. - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); - registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); - registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); - registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); - registrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); - registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); - - enable_registrar.add("People.Group.Minus.Enable", boost::bind(&LLPanelPeople::isRealGroup, this)); - enable_registrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); - - mNearbyGearButton = getChild<LLMenuButton>("nearby_view_sort_btn"); - mFriendsGearButton = getChild<LLMenuButton>("friends_viewsort_btn"); - mGroupsGearButton = getChild<LLMenuButton>("groups_viewsort_btn"); - mRecentGearButton = getChild<LLMenuButton>("recent_viewsort_btn"); - - LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGroupPlusMenuHandle = plus_menu->getHandle(); - - LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(nearby_view_sort) - { - mNearbyViewSortMenuHandle = nearby_view_sort->getHandle(); - mNearbyGearButton->setMenu(nearby_view_sort); - } - - LLToggleableMenu* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(friend_view_sort) - { - mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); - mFriendsGearButton->setMenu(friend_view_sort); - } - - LLToggleableMenu* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(group_view_sort) - { - mGroupsViewSortMenuHandle = group_view_sort->getHandle(); - mGroupsGearButton->setMenu(group_view_sort); - } - - LLToggleableMenu* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(recent_view_sort) - { - mRecentViewSortMenuHandle = recent_view_sort->getHandle(); - mRecentGearButton->setMenu(recent_view_sort); - } - LLVoiceClient::getInstance()->addObserver(this); // call this method in case some list is empty and buttons can be in inconsistent state @@ -735,9 +688,11 @@ void LLPanelPeople::updateFriendListHelpText() if (no_friends_text->getVisible()) { //update help text for empty lists - std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; + const std::string& filter = mSavedOriginalFilters[mTabContainer->getCurrentPanelIndex()]; + + std::string message_name = filter.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig); + args["[SEARCH_TERM]"] = LLURI::escape(filter); no_friends_text->setText(getString(message_name, args)); } } @@ -821,31 +776,9 @@ void LLPanelPeople::updateRecentList() mRecentList->setDirty(); } -void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL visible) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setVisible(visible); -} - -void LLPanelPeople::buttonSetEnabled(const std::string& btn_name, bool enabled) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setEnabled(enabled); -} - -void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setClickedCallback(cb); -} - void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); - bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME); bool group_tab_active = (cur_tab == GROUP_TAB_NAME); //bool recent_tab_active = (cur_tab == RECENT_TAB_NAME); @@ -856,28 +789,15 @@ void LLPanelPeople::updateButtons() bool item_selected = (selected_uuids.size() == 1); bool multiple_selected = (selected_uuids.size() >= 1); - buttonSetVisible("group_info_btn", group_tab_active); - buttonSetVisible("chat_btn", group_tab_active); - buttonSetVisible("view_profile_btn", !group_tab_active); - buttonSetVisible("im_btn", !group_tab_active); - buttonSetVisible("call_btn", !group_tab_active); - buttonSetVisible("group_call_btn", group_tab_active); - buttonSetVisible("teleport_btn", friends_tab_active); - buttonSetVisible("share_btn", nearby_tab_active || friends_tab_active); - if (group_tab_active) { - bool cur_group_active = true; - if (item_selected) { selected_id = mGroupList->getSelectedUUID(); - cur_group_active = (gAgent.getGroupID() == selected_id); } LLPanel* groups_panel = mTabContainer->getCurrentPanel(); - groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected - groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); + groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected } else { @@ -893,26 +813,20 @@ void LLPanelPeople::updateButtons() LLPanel* cur_panel = mTabContainer->getCurrentPanel(); if (cur_panel) { - cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend); + if (cur_panel->hasChild("add_friend_btn", TRUE)) + cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend); + if (friends_tab_active) { - cur_panel->getChildView("del_btn")->setEnabled(multiple_selected); + cur_panel->getChildView("friends_del_btn")->setEnabled(multiple_selected); + } + + if (!group_tab_active) + { + cur_panel->getChildView("gear_btn")->setEnabled(multiple_selected); } } } - - bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled(); - - buttonSetEnabled("view_profile_btn",item_selected); - buttonSetEnabled("share_btn", item_selected); - buttonSetEnabled("im_btn", multiple_selected); // allow starting the friends conference for multiple selection - buttonSetEnabled("call_btn", multiple_selected && enable_calls); - buttonSetEnabled("teleport_btn", multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids)); - - bool none_group_selected = item_selected && selected_id.isNull(); - buttonSetEnabled("group_info_btn", !none_group_selected); - buttonSetEnabled("group_call_btn", !none_group_selected && enable_calls); - buttonSetEnabled("chat_btn", !none_group_selected); } std::string LLPanelPeople::getActiveTabName() const @@ -943,6 +857,9 @@ LLUUID LLPanelPeople::getCurrentItemID() const if (cur_tab == GROUP_TAB_NAME) return mGroupList->getSelectedUUID(); + if (cur_tab == BLOCKED_TAB_NAME) + return LLUUID::null; // FIXME? + llassert(0 && "unknown tab selected"); return LLUUID::null; } @@ -963,6 +880,8 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const mRecentList->getSelectedUUIDs(selected_uuids); else if (cur_tab == GROUP_TAB_NAME) mGroupList->getSelectedUUIDs(selected_uuids); + else if (cur_tab == BLOCKED_TAB_NAME) + selected_uuids.clear(); // FIXME? else llassert(0 && "unknown tab selected"); @@ -1031,49 +950,60 @@ void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save } } -bool LLPanelPeople::isRealGroup() -{ - return getCurrentItemID() != LLUUID::null; -} - void LLPanelPeople::onFilterEdit(const std::string& search_string) { - mFilterSubStringOrig = search_string; - LLStringUtil::trimHead(mFilterSubStringOrig); + const S32 cur_tab_idx = mTabContainer->getCurrentPanelIndex(); + std::string& filter = mSavedOriginalFilters[cur_tab_idx]; + std::string& saved_filter = mSavedFilters[cur_tab_idx]; + + filter = search_string; + LLStringUtil::trimHead(filter); + // Searches are case-insensitive - std::string search_upper = mFilterSubStringOrig; + std::string search_upper = filter; LLStringUtil::toUpper(search_upper); - if (mFilterSubString == search_upper) + if (saved_filter == search_upper) return; - mFilterSubString = search_upper; + saved_filter = search_upper; - //store accordion tabs state before any manipulation with accordion tabs - if(!mFilterSubString.empty()) + // Apply new filter to the current tab. + const std::string cur_tab = getActiveTabName(); + if (cur_tab == NEARBY_TAB_NAME) + { + mNearbyList->setNameFilter(filter); + } + else if (cur_tab == FRIENDS_TAB_NAME) + { + // store accordion tabs opened/closed state before any manipulation with accordion tabs + if (!saved_filter.empty()) { notifyChildren(LLSD().with("action","store_state")); } - - // Apply new filter. - mNearbyList->setNameFilter(mFilterSubStringOrig); - mOnlineFriendList->setNameFilter(mFilterSubStringOrig); - mAllFriendList->setNameFilter(mFilterSubStringOrig); - mRecentList->setNameFilter(mFilterSubStringOrig); - mGroupList->setNameFilter(mFilterSubStringOrig); + mOnlineFriendList->setNameFilter(filter); + mAllFriendList->setNameFilter(filter); setAccordionCollapsedByUser("tab_online", false); setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); - //restore accordion tabs state _after_ all manipulations... - if(mFilterSubString.empty()) + // restore accordion tabs state _after_ all manipulations + if(saved_filter.empty()) { notifyChildren(LLSD().with("action","restore_state")); } } + else if (cur_tab == GROUP_TAB_NAME) + { + mGroupList->setNameFilter(filter); + } + else if (cur_tab == RECENT_TAB_NAME) + { + mRecentList->setNameFilter(filter); + } +} void LLPanelPeople::onTabSelected(const LLSD& param) { @@ -1081,11 +1011,6 @@ void LLPanelPeople::onTabSelected(const LLSD& param) updateButtons(); showFriendsAccordionsIfNeeded(); - - if (GROUP_TAB_NAME == tab_name) - mFilterEditor->setLabel(getString("groups_filter_label")); - else - mFilterEditor->setLabel(getString("people_filter_label")); } void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) @@ -1127,12 +1052,6 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) updateButtons(); } -void LLPanelPeople::onViewProfileButtonClicked() -{ - LLUUID id = getCurrentItemID(); - LLAvatarActions::showProfile(id); -} - void LLPanelPeople::onAddFriendButtonClicked() { LLUUID id = getCurrentItemID(); @@ -1191,11 +1110,6 @@ void LLPanelPeople::onDeleteFriendButtonClicked() } } -void LLPanelPeople::onGroupInfoButtonClicked() -{ - LLGroupActions::show(getCurrentItemID()); -} - void LLPanelPeople::onChatButtonClicked() { LLUUID group_id = getCurrentItemID(); @@ -1203,6 +1117,14 @@ void LLPanelPeople::onChatButtonClicked() LLGroupActions::startIM(group_id); } +void LLPanelPeople::onGearButtonClicked(LLUICtrl* btn) +{ + uuid_vec_t selected_uuids; + getCurrentItemIDs(selected_uuids); + // Spawn at bottom left corner of the button. + LLPanelPeopleMenus::gNearbyMenu.show(btn, selected_uuids, 0, 0); +} + void LLPanelPeople::onImButtonClicked() { uuid_vec_t selected_uuids; @@ -1219,11 +1141,6 @@ void LLPanelPeople::onImButtonClicked() } } -void LLPanelPeople::onActivateButtonClicked() -{ - LLGroupActions::activate(mGroupList->getSelectedUUID()); -} - // static void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { @@ -1231,19 +1148,15 @@ void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAv LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); } -void LLPanelPeople::onGroupPlusButtonClicked() +bool LLPanelPeople::onGroupPlusButtonValidate() { if (!gAgent.canJoinGroups()) { LLNotificationsUtil::add("JoinedTooManyGroups"); - return; + return false; } - LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get(); - if (!plus_menu) - return; - - showGroupMenu(plus_menu); + return true; } void LLPanelPeople::onGroupMinusButtonClicked() @@ -1288,10 +1201,6 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) @@ -1324,10 +1233,6 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) { setSortOrder(mNearbyList, E_SORT_BY_DISTANCE); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata) @@ -1361,10 +1266,6 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { mRecentList->toggleIcons(); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata) @@ -1393,40 +1294,6 @@ bool LLPanelPeople::onRecentViewSortMenuItemCheck(const LLSD& userdata) return false; } -void LLPanelPeople::onCallButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - - if (selected_uuids.size() == 1) - { - // initiate a P2P voice chat with the selected user - LLAvatarActions::startCall(getCurrentItemID()); - } - else if (selected_uuids.size() > 1) - { - // initiate an ad-hoc voice chat with multiple users - LLAvatarActions::startAdhocCall(selected_uuids); - } -} - -void LLPanelPeople::onGroupCallButtonClicked() -{ - LLGroupActions::startCall(getCurrentItemID()); -} - -void LLPanelPeople::onTeleportButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - LLAvatarActions::offerTeleport(selected_uuids); -} - -void LLPanelPeople::onShareButtonClicked() -{ - LLAvatarActions::share(getCurrentItemID()); -} - void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 46c58cd139..da27f83074 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -80,31 +80,22 @@ private: std::string getActiveTabName() const; LLUUID getCurrentItemID() const; void getCurrentItemIDs(uuid_vec_t& selected_uuids) const; - void buttonSetVisible(std::string btn_name, BOOL visible); - void buttonSetEnabled(const std::string& btn_name, bool enabled); - void buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb); void showGroupMenu(LLMenuGL* menu); void setSortOrder(LLAvatarList* list, ESortOrder order, bool save = true); // UI callbacks void onFilterEdit(const std::string& search_string); void onTabSelected(const LLSD& param); - void onViewProfileButtonClicked(); void onAddFriendButtonClicked(); void onAddFriendWizButtonClicked(); void onDeleteFriendButtonClicked(); - void onGroupInfoButtonClicked(); void onChatButtonClicked(); + void onGearButtonClicked(LLUICtrl* btn); void onImButtonClicked(); - void onCallButtonClicked(); - void onGroupCallButtonClicked(); - void onTeleportButtonClicked(); - void onShareButtonClicked(); void onMoreButtonClicked(); - void onActivateButtonClicked(); void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListCommitted(LLAvatarList* list); - void onGroupPlusButtonClicked(); + bool onGroupPlusButtonValidate(); void onGroupMinusButtonClicked(); void onGroupPlusMenuItemClicked(const LLSD& userdata); @@ -113,8 +104,6 @@ private: void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); - //returns false only if group is "none" - bool isRealGroup(); bool onFriendsViewSortMenuItemCheck(const LLSD& userdata); bool onRecentViewSortMenuItemCheck(const LLSD& userdata); bool onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -135,7 +124,6 @@ private: bool isAccordionCollapsedByUser(LLUICtrl* acc_tab); bool isAccordionCollapsedByUser(const std::string& name); - LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; @@ -144,24 +132,13 @@ private: LLGroupList* mGroupList; LLNetMap* mMiniMap; - LLHandle<LLView> mGroupPlusMenuHandle; - LLHandle<LLView> mNearbyViewSortMenuHandle; - LLHandle<LLView> mFriendsViewSortMenuHandle; - LLHandle<LLView> mGroupsViewSortMenuHandle; - LLHandle<LLView> mRecentViewSortMenuHandle; + std::vector<std::string> mSavedOriginalFilters; + std::vector<std::string> mSavedFilters; Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; Updater* mButtonsUpdater; - - LLMenuButton* mNearbyGearButton; - LLMenuButton* mFriendsGearButton; - LLMenuButton* mGroupsGearButton; - LLMenuButton* mRecentGearButton; - - std::string mFilterSubString; - std::string mFilterSubStringOrig; }; #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index f12c4de2f7..c9eebe24d3 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -51,6 +51,7 @@ LLContextMenu* NearbyMenu::createMenu() // set up the callbacks for all of the avatar menu items LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLContextMenu* menu; if ( mUUIDs.size() == 1 ) { @@ -67,20 +68,21 @@ LLContextMenu* NearbyMenu::createMenu() registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, id)); registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); + registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id)); enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); // create the context menu from the XUI - return createFromFile("menu_people_nearby.xml"); + menu = createFromFile("menu_people_nearby.xml"); } else { // Set up for multi-selected People // registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs)); // *TODO: unimplemented - registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs)); - registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs)); + registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs, LLUUID::null)); + registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs, LLUUID::null)); registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); registrar.add("Avatar.RemoveFriend",boost::bind(&LLAvatarActions::removeFriendsDialog, mUUIDs)); // registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mUUIDs)); // *TODO: unimplemented @@ -88,8 +90,10 @@ LLContextMenu* NearbyMenu::createMenu() enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); // create the context menu from the XUI - return createFromFile("menu_people_nearby_multiselect.xml"); + menu = createFromFile("menu_people_nearby_multiselect.xml"); } + + return menu; } bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index 280cc11179..854deb00d0 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -230,7 +230,7 @@ void LLPanelTopInfoBar::buildLocationString(std::string& loc_str, bool show_coor void LLPanelTopInfoBar::setParcelInfoText(const std::string& new_text) { LLRect old_rect = getRect(); - const LLFontGL* font = mParcelInfoText->getDefaultFont(); + const LLFontGL* font = mParcelInfoText->getFont(); S32 new_text_width = font->getWidth(new_text); mParcelInfoText->setText(new_text); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 975a6c67d8..47518a365f 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -32,6 +32,7 @@ #include "llagent.h" #include "llimview.h" +#include "llpanelpeoplemenus.h" #include "llnotificationsutil.h" #include "llparticipantlist.h" #include "llspeakers.h" @@ -197,10 +198,10 @@ private: uuid_set_t mAvalineCallers; }; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu/* = true*/, - bool exclude_agent /*= true*/, + bool exclude_agent /*= true*/, bool can_toggle_icons /*= true*/) : mSpeakerMgr(data_source), mAvatarList(avatar_list), @@ -233,8 +234,9 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, if (use_context_menu) { - mParticipantListMenu = new LLParticipantListMenu(*this); - mAvatarList->setContextMenu(mParticipantListMenu); + //mParticipantListMenu = new LLParticipantListMenu(*this); + //mAvatarList->setContextMenu(mParticipantListMenu); + mAvatarList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); } else { @@ -473,6 +475,7 @@ void LLParticipantList::update() { mSpeakerMgr->update(true); + // Need to resort the participant list if it's in sort by recent speaker order. if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered()) { // Resort avatar list @@ -670,7 +673,7 @@ bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents:: return mParent.onSpeakerMuteEvent(event, userdata); } -LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() +/*LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() { // set up the callbacks for all of the avatar menu items LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; @@ -708,7 +711,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() main_menu->arrangeAndClear(); return main_menu; -} +}*/ void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) { diff --git a/indra/newview/llplacesinventorybridge.cpp b/indra/newview/llplacesinventorybridge.cpp index fe4cc0f55f..1a5f64e295 100644 --- a/indra/newview/llplacesinventorybridge.cpp +++ b/indra/newview/llplacesinventorybridge.cpp @@ -85,34 +85,33 @@ void LLPlacesLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) void LLPlacesFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + LLInventoryPanel* inv_panel = mInventoryPanel.get(); + bool is_open = false; + if (inv_panel) { - std::vector<std::string> items; - std::vector<std::string> disabled_items; + LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); + is_open = (NULL != folder) && folder->isOpen(); + } - LLInventoryPanel* inv_panel = mInventoryPanel.get(); - bool is_open = false; - if (inv_panel) - { - LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); - is_open = (NULL != folder) && folder->isOpen(); - } + // collect all items' names + fill_items_with_menu_items(items, menu); - // collect all items' names - fill_items_with_menu_items(items, menu); + // remove expand or collapse menu item depend on folder state + std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); + std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); + if (it != items.end()) items.erase(it); - // remove expand or collapse menu item depend on folder state - std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); - std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); - if (it != items.end()) items.erase(it); - // Disabled items are processed via LLLandmarksPanel::isActionEnabled() - // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 + // Disabled items are processed via LLLandmarksPanel::isActionEnabled() + // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 - // repeat parent functionality - sSelf = getHandle(); // necessary for "New Folder" functionality + // repeat parent functionality + sSelf = getHandle(); // necessary for "New Folder" functionality - hide_context_entries(menu, items, disabled_items); - } + hide_context_entries(menu, items, disabled_items); } //virtual @@ -140,7 +139,7 @@ LLFolderViewFolder* LLPlacesFolderBridge::getFolder() LLInventoryPanel* inv_panel = mInventoryPanel.get(); if (inv_panel) { - folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); + folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); } return folder; @@ -152,6 +151,7 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags/* = 0x00*/) const @@ -175,6 +175,7 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -188,6 +189,7 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); diff --git a/indra/newview/llplacesinventorybridge.h b/indra/newview/llplacesinventorybridge.h index 52beacef9c..791502990b 100644 --- a/indra/newview/llplacesinventorybridge.h +++ b/indra/newview/llplacesinventorybridge.h @@ -89,6 +89,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp index f7823f4fe8..c46681f556 100644 --- a/indra/newview/llplacesinventorypanel.cpp +++ b/indra/newview/llplacesinventorypanel.cpp @@ -30,7 +30,8 @@ #include "llplacesinventorypanel.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" +#include "llfolderview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -59,42 +60,9 @@ LLPlacesInventoryPanel::~LLPlacesInventoryPanel() void LLPlacesInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) { - // Determine the root folder in case specified, and - // build the views starting with that folder. - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(params.start_folder); - - LLUUID root_id; - - if ("LIBRARY" == params.start_folder()) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); - } - - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - LLPlacesFolderView::Params p; - p.name = getName(); - p.title = getLabel(); - p.rect = folder_rect; - p.listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - p.parent_panel = this; - p.allow_multiselect = mAllowMultiSelect; - p.use_ellipses = true; // truncate inventory item text so remove horizontal scroller - mFolderRoot = (LLFolderView*)LLUICtrlFactory::create<LLPlacesFolderView>(p); + LLInventoryPanel::buildFolderView(params); } - // save current folder open state void LLPlacesInventoryPanel::saveFolderState() { @@ -161,7 +129,7 @@ BOOL LLPlacesFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask) // then determine its type and set necessary menu handle if (getCurSelectedItem()) { - LLInventoryType::EType inventory_type = getCurSelectedItem()->getListener()->getInventoryType(); + LLInventoryType::EType inventory_type = static_cast<LLFolderViewModelItemInventory*>(getCurSelectedItem()->getViewModelItem())->getInventoryType(); inventory_type_menu_handle_t::iterator it_handle = mMenuHandlesByInventoryType.find(inventory_type); if (it_handle != mMenuHandlesByInventoryType.end()) diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..29eb5ce69e 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -305,7 +305,11 @@ BOOL LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask) { if (mEditorCore) { - return mEditorCore->handleKeyHere(key, mask); + BOOL handled = mEditorCore->handleKeyHere(key, mask); + if (!handled) + { + LLFloater::handleKeyHere(key, mask); + } } return FALSE; diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca..a4a0198305 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -253,12 +253,26 @@ void LLScreenChannel::addToast(const LLToast::Params& p) { bool store_toast = false, show_toast = false; - mDisplayToastsAlways ? show_toast = true : show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + if (mDisplayToastsAlways) + { + show_toast = true; + } + else + { + show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + } store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; if(!show_toast && !store_toast) { - mRejectToastSignal(p.notif_id); + LLNotificationPtr notification = LLNotifications::instance().find(p.notif_id); + + if (notification && + (!notification->canLogToIM() || !notification->hasFormElements())) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } return; } @@ -371,7 +385,7 @@ void LLScreenChannel::storeToast(ToastElem& toast_elem) const LLToast* toast = toast_elem.getToast(); if (toast) { - mStoredToastList.push_back(toast_elem); + mStoredToastList.push_back(toast_elem); mOnStoreToast(toast->getPanel(), toast->getNotificationID()); } } @@ -410,14 +424,14 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) LLToast* toast = it->getToast(); if (toast) { - if(toast->getVisible()) - { - // toast is already in channel - return; - } + if(toast->getVisible()) + { + // toast is already in channel + return; + } - toast->setIsHidden(false); - toast->startTimer(); + toast->setIsHidden(false); + toast->startTimer(); mToastList.push_back(*it); } @@ -425,34 +439,12 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) } //-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) -{ - // *TODO: may be remove this function - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if( it == mStoredToastList.end() ) - return; - - const LLToast* toast = it->getToast(); - if (toast) - { - mRejectToastSignal(toast->getNotificationID()); - } - - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - if (it != mStoredToastList.end()) - { - mStoredToastList.erase(it); - } -} - -//-------------------------------------------------------------------------- void LLScreenChannel::killToastByNotificationID(LLUUID id) { // searching among toasts on a screen std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + LLNotificationPtr notification = LLNotifications::instance().find(id); + if (!notification) return; if( it != mToastList.end()) { @@ -465,42 +457,67 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) // the toast will be destroyed. if(toast && toast->isNotificationValid()) { - mRejectToastSignal(toast->getNotificationID()); + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } } else { - - deleteToast(toast); - mToastList.erase(it); - redrawToasts(); + removeToastByNotificationID(id); } - return; } - - // searching among stored toasts - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if (it != mStoredToastList.end()) + else { - LLToast* toast = it->getToast(); - if (toast) + // searching among stored toasts + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + + if( it != mStoredToastList.end() ) { - // send signal to a listener to let him perform some action on toast rejecting - mRejectToastSignal(toast->getNotificationID()); - deleteToast(toast); + LLToast* toast = it->getToast(); + if (toast) + { + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } + deleteToast(toast); + } + } + + // Call find() once more, because the mStoredToastList could have been changed + // via notification cancellation and the iterator could have become invalid. + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + if (it != mStoredToastList.end()) + { + mStoredToastList.erase(it); } } +} + +void LLScreenChannel::removeToastByNotificationID(LLUUID id) +{ + std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + while( it != mToastList.end()) + { + deleteToast(it->getToast()); + mToastList.erase(it); + redrawToasts(); + // find next toast with matching id + it = find(mToastList.begin(), mToastList.end(), id); + } - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. it = find(mStoredToastList.begin(), mStoredToastList.end(), id); if (it != mStoredToastList.end()) { + deleteToast(it->getToast()); mStoredToastList.erase(it); } - } + void LLScreenChannel::killMatchedToasts(const Matcher& matcher) { std::list<const LLToast*> to_delete = findToasts(matcher); @@ -521,11 +538,11 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) LLToast* toast = it->getToast(); if (toast) { - LLPanel* old_panel = toast->getPanel(); - toast->removeChild(old_panel); - delete old_panel; - toast->insertPanel(panel); - toast->startTimer(); + LLPanel* old_panel = toast->getPanel(); + toast->removeChild(old_panel); + delete old_panel; + toast->insertPanel(panel); + toast->startTimer(); } redrawToasts(); } @@ -679,7 +696,7 @@ void LLScreenChannel::showToastsCentre() return; } - LLRect toast_rect; + LLRect toast_rect; S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2; std::vector<ToastElem>::reverse_iterator it; diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 56a9cf8b4b..e5f4807ab7 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -84,6 +84,7 @@ public: // kill or modify a toast by its ID virtual void killToastByNotificationID(LLUUID id) {}; virtual void modifyToastNotificationByID(LLUUID id, LLSD data) {}; + virtual void removeToastByNotificationID(LLUUID id){}; // hide all toasts from screen, but not remove them from a channel virtual void hideToastsFromScreen() {}; @@ -175,6 +176,7 @@ public: void addToast(const LLToast::Params& p); // kill or modify a toast by its ID void killToastByNotificationID(LLUUID id); + void removeToastByNotificationID(LLUUID id); void killMatchedToasts(const Matcher& matcher); void modifyToastByNotificationID(LLUUID id, LLPanel* panel); // hide all toasts from screen, but not remove them from a channel @@ -195,8 +197,6 @@ public: void loadStoredToastsToChannel(); // finds a toast among stored by its Notification ID and throws it on a screen to a channel void loadStoredToastByNotificationIDToChannel(LLUUID id); - // removes a toast from stored finding it by its Notification ID - void removeStoredToastByNotificationID(LLUUID id); // removes from channel all toasts that belongs to the certain IM session void removeToastsBySessionID(LLUUID id); // remove all storable toasts from screen and store them @@ -227,16 +227,12 @@ public: // Channel's signals // signal on storing of faded toasts event - typedef boost::function<void (LLPanel* info_panel, const LLUUID id)> store_tost_callback_t; - typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_tost_signal_t; - store_tost_signal_t mOnStoreToast; - boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // signal on rejecting of a toast event - typedef boost::function<void (LLUUID id)> reject_tost_callback_t; - typedef boost::signals2::signal<void (LLUUID id)> reject_tost_signal_t; - reject_tost_signal_t mRejectToastSignal; boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } + typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_toast_signal_t; + boost::signals2::connection addOnStoreToastCallback(store_toast_signal_t::slot_type cb) { return mOnStoreToast.connect(cb); } private: + store_toast_signal_t mOnStoreToast; + class ToastElem { public: diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 853656905c..b143240187 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -38,7 +38,7 @@ #include "llfiltereditor.h" #include "llfloaterreg.h" #include "llfloaterworldmap.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "lloutfitobserver.h" #include "llpaneleditwearable.h" #include "llpaneloutfitsinventory.h" @@ -267,11 +267,11 @@ void LLSidepanelAppearance::onOpenOutfitButtonClicked() if (inventory_panel) { LLFolderView* root = inventory_panel->getRootFolder(); - LLFolderViewItem *outfit_folder = root->getItemByID(outfit_link->getLinkedUUID()); + LLFolderViewItem *outfit_folder = inventory_panel->getItemByID(outfit_link->getLinkedUUID()); if (outfit_folder) { outfit_folder->setOpen(!outfit_folder->isOpen()); - root->setSelectionFromRoot(outfit_folder,TRUE); + root->setSelection(outfit_folder,TRUE); root->scrollToShowSelection(); } } diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 4f9ab318a5..47bd620fc6 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -36,6 +36,7 @@ #include "llfirstuse.h" #include "llfloatersidepanelcontainer.h" #include "llfoldertype.h" +#include "llfolderview.h" #include "llhttpclient.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -383,10 +384,10 @@ void LLSidepanelInventory::onToggleInboxBtn() { inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); if (inboxPanel->isInVisibleChain()) - { - gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); - } + { + gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } +} else { gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim()); @@ -472,7 +473,7 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action) } } - current_item->getListener()->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); } void LLSidepanelInventory::onWearButtonClicked() @@ -662,7 +663,7 @@ LLInventoryItem *LLSidepanelInventory::getSelectedItem() return NULL; } } - const LLUUID &item_id = current_item->getListener()->getUUID(); + const LLUUID &item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLInventoryItem *item = gInventory.getItem(item_id); return item; } @@ -671,7 +672,7 @@ U32 LLSidepanelInventory::getSelectedCount() { int count = 0; - std::set<LLUUID> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); count += selection_list.size(); if ((count == 0) && mInboxEnabled && (mInventoryPanelInbox != NULL)) @@ -722,9 +723,9 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox) updateVerbs(); } -std::set<LLUUID> LLSidepanelInventory::getInboxSelectionList() +std::set<LLFolderViewItem*> LLSidepanelInventory::getInboxSelectionList() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected_uuids; if (mInboxEnabled && (mInventoryPanelInbox != NULL)) { diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index a33607f50d..e8b2808d4f 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -63,7 +63,7 @@ public: BOOL isMainInventoryPanelActive() const; void clearSelections(bool clearMain, bool clearInbox); - std::set<LLUUID> getInboxSelectionList(); + std::set<LLFolderViewItem*> getInboxSelectionList(); void showItemInfoPanel(); void showTaskInfoPanel(); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 325a2d3004..de7fb542f8 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2762,7 +2762,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera) void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color) { - gGL.diffuseColor4fv(color.mV); + gGL.color4fv(color.mV); gGL.begin(LLRender::LINES); { gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV); @@ -4010,7 +4010,7 @@ void renderAgentTarget(LLVOAvatar* avatar) if (avatar->isSelf()) { renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); - renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); + renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(0, 1, 0, 0.8f)); renderCrossHairs(avatar->mRoot.getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f)); renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f)); } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index b9358cf37c..1c6f51e131 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -29,7 +29,6 @@ #include "llevent.h" #include "lleventtimer.h" -#include "llspeakers.h" #include "llvoicechannel.h" class LLSpeakerMgr; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index ee7a234bbe..0b70184920 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -63,7 +63,7 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" -#include "llnearbychat.h" +#include "llimfloatercontainer.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llteleporthistory.h" @@ -1378,14 +1378,9 @@ bool idle_startup() LLVoiceClient::getInstance()->updateSettings(); display_startup(); - //gCacheName is required for nearby chat history loading - //so I just moved nearby history loading a few states further - if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) - { - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - if (nearby_chat) nearby_chat->loadHistory(); - } - display_startup(); + // create a container's instance for start a controlling conversation windows + // by the voice's events + LLIMFloaterContainer::getInstance(); // *Note: this is where gWorldMap used to be initialized. @@ -2164,7 +2159,6 @@ bool idle_startup() LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); - LLIMFloater::initIMFloater(); display_startup(); return TRUE; diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..18e0d9d0d2 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -433,13 +433,19 @@ BOOL LLIMWellWindow::ObjectRowPanel::handleRightMouseDown(S32 x, S32 y, MASK mas ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS +LLNotificationWellWindow::WellNotificationChannel::WellNotificationChannel(LLNotificationWellWindow* well_window) +: LLNotificationChannel(LLNotificationChannel::Params().name(well_window->getPathname())), + mWellWindow(well_window) +{ + connectToChannel("Notifications"); + connectToChannel("Group Notifications"); + connectToChannel("Offer"); +} + LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) -: LLSysWellWindow(key) +: LLSysWellWindow(key) { - // init connections to the list's update events - connectListUpdaterToSignal("notify"); - connectListUpdaterToSignal("groupnotify"); - connectListUpdaterToSignal("offer"); + mNotificationUpdates.reset(new WellNotificationChannel(this)); } // static @@ -519,7 +525,7 @@ void LLNotificationWellWindow::initChannel() LLSysWellWindow::initChannel(); if(mChannel) { - mChannel->setOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); + mChannel->addOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); } } @@ -546,20 +552,6 @@ void LLNotificationWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) addItem(p); } -void LLNotificationWellWindow::connectListUpdaterToSignal(std::string notification_type) -{ - LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); - LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); - if(n_handler) - { - n_handler->setNotificationIDCallback(boost::bind(&LLNotificationWellWindow::removeItemByID, this, _1)); - } - else - { - llwarns << "LLSysWellWindow::connectListUpdaterToSignal() - could not get a handler for '" << notification_type <<"' type of notifications" << llendl; - } -} - void LLNotificationWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); @@ -574,6 +566,12 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) mChannel->killToastByNotificationID(id); } +void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) +{ + removeItemByID(notify->getID()); +} + + /************************************************************************/ @@ -867,4 +865,4 @@ bool LLIMWellWindow::confirmCloseAll(const LLSD& notification, const LLSD& respo return false; } -// EOF + diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 272e9cfcb1..8758c8c4e5 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -34,6 +34,7 @@ #include "llscreenchannel.h" #include "llscrollcontainer.h" #include "llimview.h" +#include "llnotifications.h" #include "boost/shared_ptr.hpp" @@ -111,7 +112,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); - + /*virtual*/ void onAdd(LLNotificationPtr notify); // Operating with items void addItem(LLSysWellItem::Params p); @@ -119,6 +120,18 @@ public: void closeAll(); protected: + struct WellNotificationChannel : public LLNotificationChannel + { + WellNotificationChannel(LLNotificationWellWindow*); + void onDelete(LLNotificationPtr notify) + { + mWellWindow->removeItemByID(notify->getID()); + } + + LLNotificationWellWindow* mWellWindow; + }; + + LLNotificationChannelPtr mNotificationUpdates; /*virtual*/ const std::string& getAnchorViewName() { return NOTIFICATION_WELL_ANCHOR_NAME; } private: @@ -126,12 +139,8 @@ private: void initChannel(); void clearScreenChannels(); - void onStoreToast(LLPanel* info_panel, LLUUID id); - // connect counter and list updaters to the corresponding signals - void connectListUpdaterToSignal(std::string notification_type); - // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); @@ -160,6 +169,7 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ec36cf48c2..2d3f144588 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -39,7 +39,7 @@ #include "llfocusmgr.h" #include "llviewertexture.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventory.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" @@ -58,6 +58,7 @@ #include "lltoolmgr.h" #include "lltoolpipette.h" #include "llfiltereditor.h" +#include "llwindow.h" #include "lltool.h" #include "llviewerwindow.h" @@ -186,7 +187,7 @@ protected: F32 mContextConeOpacity; LLSaveFolderState mSavedFolderState; BOOL mSelectedItemPinned; - + LLRadioGroup* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; @@ -372,7 +373,7 @@ BOOL LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask) { if (!root_folder->getCurSelectedItem()) { - LLFolderViewItem* itemp = root_folder->getItemByID(gInventory.getRootFolderID()); + LLFolderViewItem* itemp = mInventoryPanel->getItemByID(gInventory.getRootFolderID()); if (itemp) { root_folder->setSelection(itemp, FALSE, FALSE); @@ -637,10 +638,9 @@ void LLFloaterTexturePicker::draw() LLFolderView* folder_view = mInventoryPanel->getRootFolder(); if (!folder_view) return; - LLInventoryFilter* filter = folder_view->getFilter(); - if (!filter) return; + LLFolderViewFilter* filter = static_cast<LLFolderViewModelInventory*>(folder_view->getFolderViewModel())->getFilter(); - bool is_filter_active = folder_view->getCompletedFilterGeneration() < filter->getCurrentGeneration() && + bool is_filter_active = folder_view->getViewModelItem()->getLastFilterGeneration() < filter->getCurrentGeneration() && filter->isNotDefault(); // After inventory panel filter is applied we have to update @@ -651,8 +651,9 @@ void LLFloaterTexturePicker::draw() if (!is_filter_active && !mSelectedItemPinned) { folder_view->setPinningSelectedItem(mSelectedItemPinned); - folder_view->dirtyFilter(); - folder_view->arrangeFromRoot(); + folder_view->getViewModelItem()->dirtyFilter(); + //TODO RN: test..still works without this? + //folder_view->arrangeFromRoot(); mSelectedItemPinned = TRUE; } @@ -815,7 +816,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem if (items.size()) { LLFolderViewItem* first_item = items.front(); - LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID()); + LLInventoryItem* itemp = gInventory.getItem(static_cast<LLFolderViewModelItemInventory*>(first_item->getViewModelItem())->getUUID()); mNoCopyTextureSelected = FALSE; if (itemp) { @@ -1011,7 +1012,7 @@ void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) else if (mInventoryPanel->getFilterSubString().empty()) { // first letter in search term, save existing folder open state - if (!mInventoryPanel->getRootFolder()->isFilterModified()) + if (!mInventoryPanel->getFilter()->isNotDefault()) { mSavedFolderState.setApply(FALSE); mInventoryPanel->getRootFolder()->applyFunctorRecursively(mSavedFolderState); @@ -1325,7 +1326,7 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) // (i.e. op == TEXTURE_SELECT) or texture changes via DnD. else if (mCommitOnSelection || op == TEXTURE_SELECT) mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? - + if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work. { setTentative( FALSE ); @@ -1337,10 +1338,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) } else { - mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); - lldebugs << "mImageItemID: " << mImageItemID << llendl; - mImageAssetID = floaterp->getAssetID(); - lldebugs << "mImageAssetID: " << mImageAssetID << llendl; + mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); + lldebugs << "mImageItemID: " << mImageItemID << llendl; + mImageAssetID = floaterp->getAssetID(); + lldebugs << "mImageAssetID: " << mImageAssetID << llendl; } if (op == TEXTURE_SELECT && mOnSelectCallback) diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index e1d99b1bcb..ea62f758f8 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -169,6 +169,7 @@ public: // get/set Toast's flags or states // get information whether the notification corresponding to the toast is valid or not bool isNotificationValid(); + // get toast's Notification ID const LLUUID getNotificationID() const { return mNotificationID;} // get toast's Session ID @@ -212,7 +213,7 @@ private: //LLRootHandle<LLToast> mHandle; - LLPanel* mWrapperPanel; + LLPanel* mWrapperPanel; // timer counts a lifetime of a toast std::auto_ptr<LLToastLifeTimer> mTimer; @@ -220,8 +221,8 @@ private: F32 mToastLifetime; // in seconds F32 mToastFadingTime; // in seconds - LLPanel* mPanel; - LLButton* mHideBtn; + LLPanel* mPanel; + LLButton* mHideBtn; LLColor4 mBgColor; bool mCanFade; diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 75178a6ef8..ed350ea144 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -51,7 +51,7 @@ const S32 LLToastGroupNotifyPanel::DEFAULT_MESSAGE_MAX_LINE_COUNT = 7; -LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification) +LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notification) : LLToastPanel(notification), mInventoryOffer(NULL) { @@ -112,7 +112,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification style.font = date_font; pMessageText->appendText(timeStr + "\n", TRUE, style); - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //attachment diff --git a/indra/newview/lltoastgroupnotifypanel.h b/indra/newview/lltoastgroupnotifypanel.h index 7794ec9f63..dfdc6ae559 100644 --- a/indra/newview/lltoastgroupnotifypanel.h +++ b/indra/newview/lltoastgroupnotifypanel.h @@ -47,13 +47,10 @@ class LLToastGroupNotifyPanel public: void close(); - static bool onNewNotification(const LLSD& notification); - - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". - LLToastGroupNotifyPanel(LLNotificationPtr& notification); + LLToastGroupNotifyPanel(const LLNotificationPtr& notification); /*virtual*/ ~LLToastGroupNotifyPanel(); protected: diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index a803387576..279dd69bc7 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -41,11 +41,11 @@ public: struct Params { LLNotificationPtr notification; - LLUUID avatar_id; - LLUUID session_id; - std::string from; - std::string time; - std::string message; + LLUUID avatar_id, + session_id; + std::string from, + time, + message; Params() {} }; diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index d629f3abac..4a49922656 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -45,6 +45,9 @@ const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding S32 BUTTON_WIDTH = 90; +// *TODO: magic numbers(???) - copied from llnotify.cpp(250) +const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; + //static const LLFontGL* LLToastNotifyPanel::sFont = NULL; @@ -52,172 +55,12 @@ const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL; LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal; -LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) : -LLToastPanel(notification), -mTextBox(NULL), -mInfoPanel(NULL), -mControlPanel(NULL), -mNumOptions(0), -mNumButtons(0), -mAddedDefaultBtn(false), -mCloseNotificationOnDestroy(true) +LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) +: LLToastPanel(notification), + LLInstanceTracker<LLToastNotifyPanel, LLUUID>(notification->getID()) { - buildFromFile( "panel_notification.xml"); - if(rect != LLRect::null) - { - this->setShape(rect); - } - mInfoPanel = getChild<LLPanel>("info_panel"); - mControlPanel = getChild<LLPanel>("control_panel"); - BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); - // customize panel's attributes - // is it intended for displaying a tip? - mIsTip = notification->getType() == "notifytip"; - // is it a script dialog? - mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); - // is it a caution? - // - // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the - // notify xml template specifies that it is a caution - // tip-style notification handle 'caution' differently -they display the tip in a different color - mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; - - // setup parameters - // get a notification message - mMessage = notification->getMessage(); - // init font variables - if (!sFont) - { - sFont = LLFontGL::getFontSansSerif(); - sFontSmall = LLFontGL::getFontSansSerifSmall(); - } - // initialize - setFocusRoot(!mIsTip); - // get a form for the notification - LLNotificationFormPtr form(notification->getForm()); - // get number of elements - mNumOptions = form->getNumElements(); - - // customize panel's outfit - // preliminary adjust panel's layout - //move to the end - //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - - // adjust text options according to the notification type - // add a caution textbox at the top of a caution notification - if (mIsCaution && !mIsTip) - { - mTextBox = getChild<LLTextBox>("caution_text_box"); - } - else - { - mTextBox = getChild<LLTextEditor>("text_editor_box"); - } - - // *TODO: magic numbers(???) - copied from llnotify.cpp(250) - const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; - - mTextBox->setMaxTextLength(MAX_LENGTH); - mTextBox->setVisible(TRUE); - mTextBox->setPlainText(!show_images); - mTextBox->setValue(notification->getMessage()); - - // add buttons for a script notification - if (mIsTip) - { - adjustPanelForTipNotice(); + init(rect, show_images); } - else - { - std::vector<index_button_pair_t> buttons; - buttons.reserve(mNumOptions); - S32 buttons_width = 0; - // create all buttons and accumulate they total width to reshape mControlPanel - for (S32 i = 0; i < mNumOptions; i++) - { - LLSD form_element = form->getElement(i); - if (form_element["type"].asString() != "button") - { - // not a button. - continue; - } - if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) - { - // a textbox pretending to be a button. - continue; - } - LLButton* new_button = createButton(form_element, TRUE); - buttons_width += new_button->getRect().getWidth(); - S32 index = form_element["index"].asInteger(); - buttons.push_back(index_button_pair_t(index,new_button)); - } - if (buttons.empty()) - { - addDefaultButton(); - } - else - { - const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel - S32 button_panel_height = mControlPanel->getRect().getHeight(); - //try get an average h_pad to spread out buttons - S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); - if(h_pad < 2*HPAD) - { - /* - * Probably it is a scriptdialog toast - * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. - * In last case set default h_pad to avoid heaping of buttons - */ - S32 button_per_row = button_panel_width / BUTTON_WIDTH; - h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row - if(h_pad < 2*HPAD) // still not enough space between buttons ? - { - h_pad = 2*HPAD; - } - } - if (mIsScriptDialog) - { - // we are using default width for script buttons so we can determinate button_rows - //to get a number of rows we divide the required width of the buttons to button_panel_width - S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); - //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; - //reserve one row for the ignore_btn - button_rows++; - //calculate required panel height for scripdialog notification. - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; - } - else - { - // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width - //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); - S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); - //calculate required panel height - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; - } - - // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed - adjustPanelForScriptNotice(button_panel_width, button_panel_height); - updateButtonsLayout(buttons, h_pad); - // save buttons for later use in disableButtons() - mButtons.assign(buttons.begin(), buttons.end()); - } - } - // adjust panel's height to the text size - mInfoPanel->setFollowsAll(); - snapToMessageHeight(mTextBox, MAX_LENGTH); - - if(notification->isReusable()) - { - mButtonClickConnection = sButtonClickSignal.connect( - boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2)); - - if(notification->isRespondedTo()) - { - // User selected an option in toast, now disable required buttons in IM window - disableRespondedOptions(notification); - } - } -} void LLToastNotifyPanel::addDefaultButton() { LLSD form_element; @@ -235,7 +78,6 @@ void LLToastNotifyPanel::addDefaultButton() } LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_option) { - InstanceAndS32* userdata = new InstanceAndS32; userdata->mSelf = this; userdata->mButtonName = is_option ? form_element["name"].asString() : ""; @@ -245,14 +87,15 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt LLButton::Params p; bool make_small_btn = form_element["index"].asInteger() == -1 || form_element["index"].asInteger() == -2; const LLFontGL* font = make_small_btn ? sFontSmall: sFont; // for block and ignore buttons in script dialog - p.name(form_element["name"].asString()); - p.label(form_element["text"].asString()); - p.font(font); + p.name = form_element["name"].asString(); + p.label = form_element["text"].asString(); + p.font = font; p.rect.height = BTN_HEIGHT; p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata)); p.rect.width = BUTTON_WIDTH; p.auto_resize = false; p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + p.enabled = !form_element.has("enabled") || form_element["enabled"].asBoolean(); if (mIsCaution) { p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor")); @@ -287,16 +130,11 @@ LLToastNotifyPanel::~LLToastNotifyPanel() mButtonClickConnection.disconnect(); std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer()); - if (mCloseNotificationOnDestroy && LLNotificationsUtil::find(mNotification->getID()) != NULL) - { - // let reusable notification be deleted - mNotification->setReusable(false); - if (!mNotification->isPersistent()) + if (mIsTip) { LLNotifications::getInstance()->cancel(mNotification); } } -} void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 h_pad) { @@ -393,210 +231,374 @@ void LLToastNotifyPanel::adjustPanelForTipNotice() } } -typedef std::set<std::string> button_name_set_t; -typedef std::map<std::string, button_name_set_t> disable_button_map_t; +//typedef std::set<std::string> button_name_set_t; +//typedef std::map<std::string, button_name_set_t> disable_button_map_t; +// +//disable_button_map_t initUserGiveItemDisableButtonMap() +//{ +// // see EXT-5905 for disable rules +// +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Show"); +// disable_map.insert(std::make_pair("Show", buttons)); +// +// buttons.insert("Discard"); +// disable_map.insert(std::make_pair("Discard", buttons)); +// +// buttons.insert("Mute"); +// disable_map.insert(std::make_pair("Mute", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initTeleportOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Teleport"); +// buttons.insert("Cancel"); +// +// disable_map.insert(std::make_pair("Teleport", buttons)); +// disable_map.insert(std::make_pair("Cancel", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initFriendshipOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Accept"); +// buttons.insert("Decline"); +// +// disable_map.insert(std::make_pair("Accept", buttons)); +// disable_map.insert(std::make_pair("Decline", buttons)); +// +// return disable_map; +//} +// +//button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) +//{ +// static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); +// static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); +// static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); +// +// disable_button_map_t::const_iterator it; +// disable_button_map_t::const_iterator it_end; +// disable_button_map_t search_map; +// +// if("UserGiveItem" == notification_name) +// { +// search_map = user_give_item_disable_map; +// } +// else if("TeleportOffered" == notification_name) +// { +// search_map = teleport_offered_disable_map; +// } +// else if("OfferFriendship" == notification_name) +// { +// search_map = friendship_offered_disable_map; +// } +// +// it = search_map.find(button_name); +// it_end = search_map.end(); +// +// if(it_end != it) +// { +// return it->second; +// } +// return button_name_set_t(); +//} + +//void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) +//{ + //button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); + + //std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); + //for ( ; it != mButtons.end(); it++) + //{ + // LLButton* btn = it->second; + // if(buttons.find(btn->getName()) != buttons.end()) + // { + // btn->setEnabled(FALSE); + // } + //} +//} -disable_button_map_t initUserGiveItemDisableButtonMap() +// static +void LLToastNotifyPanel::onClickButton(void* data) { - // see EXT-5905 for disable rules - - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Show"); - disable_map.insert(std::make_pair("Show", buttons)); + InstanceAndS32* self_and_button = (InstanceAndS32*)data; + LLToastNotifyPanel* self = self_and_button->mSelf; + std::string button_name = self_and_button->mButtonName; - buttons.insert("Discard"); - disable_map.insert(std::make_pair("Discard", buttons)); + LLSD response = self->mNotification->getResponseTemplate(); + if (!self->mAddedDefaultBtn && !button_name.empty()) + { + response[button_name] = true; + } - buttons.insert("Mute"); - disable_map.insert(std::make_pair("Mute", buttons)); + // disable all buttons + self->mControlPanel->setEnabled(FALSE); - return disable_map; + // this might repost notification with new form data/enabled buttons + self->mNotification->respond(response); } -disable_button_map_t initTeleportOfferedDisableButtonMap() +void LLToastNotifyPanel::init( LLRect rect, bool show_images ) { - disable_button_map_t disable_map; - button_name_set_t buttons; + deleteAllChildren(); - buttons.insert("Teleport"); - buttons.insert("Cancel"); + mTextBox = NULL; + mInfoPanel = NULL; + mControlPanel = NULL; + mNumOptions = 0; + mNumButtons = 0; + mAddedDefaultBtn = false; - disable_map.insert(std::make_pair("Teleport", buttons)); - disable_map.insert(std::make_pair("Cancel", buttons)); + buildFromFile( "panel_notification.xml"); + if(rect != LLRect::null) + { + this->setShape(rect); + } + mInfoPanel = getChild<LLPanel>("info_panel"); + mInfoPanel->setFollowsAll(); - return disable_map; -} + mControlPanel = getChild<LLPanel>("control_panel"); + BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); + // customize panel's attributes + // is it intended for displaying a tip? + mIsTip = mNotification->getType() == "notifytip"; + // is it a script dialog? + mIsScriptDialog = (mNotification->getName() == "ScriptDialog" || mNotification->getName() == "ScriptDialogGroup"); + // is it a caution? + // + // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the + // notify xml template specifies that it is a caution + // tip-style notification handle 'caution' differently -they display the tip in a different color + mIsCaution = mNotification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; -disable_button_map_t initFriendshipOfferedDisableButtonMap() + // setup parameters + // get a notification message + mMessage = mNotification->getMessage(); + // init font variables + if (!sFont) { - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Accept"); - buttons.insert("Decline"); - - disable_map.insert(std::make_pair("Accept", buttons)); - disable_map.insert(std::make_pair("Decline", buttons)); - - return disable_map; + sFont = LLFontGL::getFontSansSerif(); + sFontSmall = LLFontGL::getFontSansSerifSmall(); } + // initialize + setFocusRoot(!mIsTip); + // get a form for the notification + LLNotificationFormPtr form(mNotification->getForm()); + // get number of elements + mNumOptions = form->getNumElements(); -button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) -{ - static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); - static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); - static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); - - disable_button_map_t::const_iterator it; - disable_button_map_t::const_iterator it_end; - disable_button_map_t search_map; + // customize panel's outfit + // preliminary adjust panel's layout + //move to the end + //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - if("UserGiveItem" == notification_name) - { - search_map = user_give_item_disable_map; - } - else if(("TeleportOffered" == notification_name) || ("TeleportOffered_MaturityExceeded" == notification_name)) + // adjust text options according to the notification type + // add a caution textbox at the top of a caution notification + if (mIsCaution && !mIsTip) { - search_map = teleport_offered_disable_map; + mTextBox = getChild<LLTextBox>("caution_text_box"); } - else if("OfferFriendship" == notification_name) + else { - search_map = friendship_offered_disable_map; + mTextBox = getChild<LLTextEditor>("text_editor_box"); } - it = search_map.find(button_name); - it_end = search_map.end(); + mTextBox->setMaxTextLength(MAX_LENGTH); + mTextBox->setVisible(TRUE); + mTextBox->setPlainText(!show_images); + mTextBox->setValue(mNotification->getMessage()); - if(it_end != it) + // add buttons for a script notification + if (mIsTip) { - return it->second; + adjustPanelForTipNotice(); } - return button_name_set_t(); -} - -void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) + else { - button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); - - std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); - for ( ; it != mButtons.end(); it++) + std::vector<index_button_pair_t> buttons; + buttons.reserve(mNumOptions); + S32 buttons_width = 0; + // create all buttons and accumulate they total width to reshape mControlPanel + for (S32 i = 0; i < mNumOptions; i++) { - LLButton* btn = it->second; - if(buttons.find(btn->getName()) != buttons.end()) + LLSD form_element = form->getElement(i); + if (form_element["type"].asString() != "button") { - btn->setEnabled(FALSE); + // not a button. + continue; } + if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) + { + // a textbox pretending to be a button. + continue; } + LLButton* new_button = createButton(form_element, TRUE); + buttons_width += new_button->getRect().getWidth(); + S32 index = form_element["index"].asInteger(); + buttons.push_back(index_button_pair_t(index,new_button)); } - -// static -void LLToastNotifyPanel::onClickButton(void* data) + if (buttons.empty()) { - InstanceAndS32* self_and_button = (InstanceAndS32*)data; - LLToastNotifyPanel* self = self_and_button->mSelf; - std::string button_name = self_and_button->mButtonName; - - LLSD response = self->mNotification->getResponseTemplate(); - if (!self->mAddedDefaultBtn && !button_name.empty()) - { - response[button_name] = true; + addDefaultButton(); } - - bool is_reusable = self->mNotification->isReusable(); - // When we call respond(), LLOfferInfo will delete itself in inventory_offer_callback(), - // lets copy it while it's still valid. - LLOfferInfo* old_info = static_cast<LLOfferInfo*>(self->mNotification->getResponder()); - LLOfferInfo* new_info = NULL; - if(is_reusable && old_info) + else { - new_info = new LLOfferInfo(*old_info); - self->mNotification->setResponder(new_info); - } - - self->mNotification->respond(response); - - if(is_reusable) + const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel + S32 button_panel_height = mControlPanel->getRect().getHeight(); + //try get an average h_pad to spread out buttons + S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); + if(h_pad < 2*HPAD) { - sButtonClickSignal(self->mNotification->getID(), button_name); - } - else + /* + * Probably it is a scriptdialog toast + * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. + * In last case set default h_pad to avoid heaping of buttons + */ + S32 button_per_row = button_panel_width / BUTTON_WIDTH; + h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row + if(h_pad < 2*HPAD) // still not enough space between buttons ? { - // disable all buttons - self->mControlPanel->setEnabled(FALSE); + h_pad = 2*HPAD; } } - -void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) + if (mIsScriptDialog) { - if(mNotification->getID() == notification_id) + // we are using default width for script buttons so we can determinate button_rows + //to get a number of rows we divide the required width of the buttons to button_panel_width + S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); + //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; + //reserve one row for the ignore_btn + button_rows++; + //calculate required panel height for scripdialog notification. + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; + } + else { - disableButtons(mNotification->getName(), btn_name); - } + // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width + //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); + S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); + //calculate required panel height + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; } -void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) -{ - LLSD response = notification->getResponse(); - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - // that after multiple responses there can be many pressed buttons - // need to process them all - disableButtons(notification->getName(), response_it->first); + // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed + adjustPanelForScriptNotice(button_panel_width, button_panel_height); + updateButtonsLayout(buttons, h_pad); + // save buttons for later use in disableButtons() + //mButtons.assign(buttons.begin(), buttons.end()); } } + // adjust panel's height to the text size + snapToMessageHeight(mTextBox, MAX_LENGTH); } + +//void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) +//{ +// if(mNotification->getID() == notification_id) +// { +// disableButtons(mNotification->getName(), btn_name); +// } +//} + +//void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) +//{ +// LLSD response = notification->getResponse(); +// for (LLSD::map_const_iterator response_it = response.beginMap(); +// response_it != response.endMap(); ++response_it) +// { +// if (response_it->second.isBoolean() && response_it->second.asBoolean()) +// { +// // that after multiple responses there can be many pressed buttons +// // need to process them all +// disableButtons(notification->getName(), response_it->first); +// } +// } +//} + + ////////////////////////////////////////////////////////////////////////// LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */, - bool show_images /* = true */) - : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images) + bool show_images /* = true */, LLTextBase* parent_text) +: mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images), + mParentText(parent_text) { - mTextBox->setFollowsAll(); + compactButtons(); } LLIMToastNotifyPanel::~LLIMToastNotifyPanel() { - // We shouldn't delete notification when IM floater exists - // since that notification will be reused by IM floater. - // This may happened when IM floater reloads messages, exactly when user - // changes layout of IM chat log(disable/enable plaintext mode). - // See EXT-6500 - LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID); - if (im_floater != NULL && !im_floater->isDead()) - { - mCloseNotificationOnDestroy = false; - } } void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) + { + LLToastPanel::reshape(width, height, called_from_parent); + + snapToMessageHeight(mTextBox, MAX_LENGTH); + } + +void LLIMToastNotifyPanel::compactButtons() { - S32 text_height = mTextBox->getTextBoundingRect().getHeight(); - S32 widget_height = mTextBox->getRect().getHeight(); - S32 delta = text_height - widget_height; - LLRect rc = getRect(); + mTextBox->setFollowsAll(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta); - height = rc.getHeight(); - width = rc.getWidth(); + //we can't set follows in xml since it broke toasts behavior + setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP); - bool is_width_changed = width != getRect().getWidth(); + const child_list_t* children = getControlPanel()->getChildList(); + S32 offset = 0; + // Children were added by addChild() which uses push_front to insert them into list, + // so to get buttons in correct order reverse iterator is used (EXT-5906) + for (child_list_t::const_reverse_iterator it = children->rbegin(); it != children->rend(); it++) + { + LLButton * button = dynamic_cast<LLButton*> (*it); + if (button != NULL) + { + button->setOrigin( offset,button->getRect().mBottom); + button->setLeftHPad(2 * HPAD); + button->setRightHPad(2 * HPAD); + // set zero width before perform autoResize() + button->setRect(LLRect(button->getRect().mLeft, + button->getRect().mTop, + button->getRect().mLeft, + button->getRect().mBottom)); + button->setAutoResize(true); + button->autoResize(); + offset += HPAD + button->getRect().getWidth(); + button->setFollowsNone(); + } + } - LLToastPanel::reshape(width, height, called_from_parent); + if (mParentText) + { + mParentText->needsReflow(); + } +} - // Notification height required to display the text message depends on - // the width of the text box thus if panel width is changed the text box - // width is also changed then reshape() is called to adjust proper height. - if (is_width_changed) +void LLIMToastNotifyPanel::updateNotification() { - reshape(width, height, called_from_parent); + init(LLRect(), true); } + +void LLIMToastNotifyPanel::init( LLRect rect, bool show_images ) +{ + LLToastNotifyPanel::init(LLRect(), show_images); + + compactButtons(); } + // EOF + diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h index db517ec858..f93c7745af 100644 --- a/indra/newview/lltoastnotifypanel.h +++ b/indra/newview/lltoastnotifypanel.h @@ -47,7 +47,7 @@ class LLNotificationForm; * @deprecated this class will be removed after all toast panel types are * implemented in separate classes. */ -class LLToastNotifyPanel: public LLToastPanel +class LLToastNotifyPanel: public LLToastPanel, public LLInstanceTracker<LLToastNotifyPanel, LLUUID> { public: /** @@ -61,10 +61,14 @@ public: * implement right class for desired toast panel. @see LLGenericTipPanel as example. */ LLToastNotifyPanel(const LLNotificationPtr& pNotification, const LLRect& rect = LLRect::null, bool show_images = true); + + virtual void init( LLRect rect, bool show_images ); + virtual ~LLToastNotifyPanel(); LLPanel * getControlPanel() { return mControlPanel; } - void setCloseNotificationOnDestroy(bool close) { mCloseNotificationOnDestroy = close; } + virtual void updateNotification() {} + protected: LLButton* createButton(const LLSD& form_element, BOOL is_option); @@ -76,8 +80,6 @@ protected: }; std::vector<InstanceAndS32*> mBtnCallbackData; - bool mCloseNotificationOnDestroy; - typedef std::pair<int,LLButton*> index_button_pair_t; void adjustPanelForScriptNotice(S32 max_width, S32 max_height); void adjustPanelForTipNotice(); @@ -93,9 +95,9 @@ protected: /** * Disable specific button(s) based on notification name and clicked button */ - void disableButtons(const std::string& notification_name, const std::string& selected_button); + //void disableButtons(const std::string& notification_name, const std::string& selected_button); - std::vector<index_button_pair_t> mButtons; + //std::vector<index_button_pair_t> mButtons; // panel elements LLTextBase* mTextBox; @@ -118,7 +120,7 @@ protected: /** * Process response data. Will disable selected options */ - void disableRespondedOptions(const LLNotificationPtr& notification); + //void disableRespondedOptions(const LLNotificationPtr& notification); bool mIsTip; bool mAddedDefaultBtn; @@ -137,13 +139,23 @@ class LLIMToastNotifyPanel : public LLToastNotifyPanel { public: - LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect = LLRect::null, bool show_images = true); + LLIMToastNotifyPanel(LLNotificationPtr& pNotification, + const LLUUID& session_id, + const LLRect& rect = LLRect::null, + bool show_images = true, + LLTextBase* parent_text = NULL); + + void compactButtons(); + + virtual void updateNotification(); + virtual void init( LLRect rect, bool show_images ); ~LLIMToastNotifyPanel(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); protected: + LLTextBase* mParentText; LLUUID mSessionID; }; diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index c33fde99c5..187aee207c 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -70,7 +70,7 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) if (message->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = message->getDefaultFont()->getLineHeight() * maxLineCount; + S32 maxTextHeight = message->getFont()->getLineHeight() * maxLineCount; LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); @@ -80,11 +80,14 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) S32 requiredTextHeight = message->getTextBoundingRect().getHeight(); S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight); - //Calculate last delta height deducting previous heightDelta - heightDelta = newTextHeight - oldTextHeight - heightDelta; + heightDelta = newTextHeight - oldTextHeight; + S32 new_panel_height = llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT); //reshape the panel with new height - reshape( getRect().getWidth(), llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT)); + if (new_panel_height != getRect().getHeight()) + { + reshape( getRect().getWidth(), new_panel_height); + } } } @@ -98,7 +101,7 @@ LLToastPanel* LLToastPanel::buidPanelFromNotification( if ("notifytip" == notification->getType()) { // if it is online/offline notification - if ("FriendOffline" == notification->getName() || "FriendOnline" == notification->getName()) + if ("FriendOnlineOffline" == notification->getName()) { res = new LLPanelOnlineStatus(notification); } diff --git a/indra/newview/lltoastpanel.h b/indra/newview/lltoastpanel.h index 346e014d73..c22557206b 100644 --- a/indra/newview/lltoastpanel.h +++ b/indra/newview/lltoastpanel.h @@ -33,19 +33,13 @@ #include <string> -class LLToastPanelBase: public LLPanel -{ -public: - virtual void init(LLSD& data){}; -}; - /** * Base class for all panels that can be added to the toast. * All toast panels should contain necessary logic for representing certain notification * but shouldn't contain logic related to this panel lifetime control and positioning * on the parent view. */ -class LLToastPanel: public LLPanel { +class LLToastPanel : public LLPanel { public: LLToastPanel(const LLNotificationPtr&); virtual ~LLToastPanel() = 0; diff --git a/indra/newview/lltoastscriptquestion.cpp b/indra/newview/lltoastscriptquestion.cpp index feeb8ca77b..91ba8c0247 100644 --- a/indra/newview/lltoastscriptquestion.cpp +++ b/indra/newview/lltoastscriptquestion.cpp @@ -66,8 +66,8 @@ void LLToastScriptQuestion::snapToMessageHeight() if (mMessage->getVisible() && mFooter->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = (mMessage->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT) - + (mFooter->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT); + S32 maxTextHeight = (mMessage->getFont()->getLineHeight() * MAX_LINES_COUNT) + + (mFooter->getFont()->getLineHeight() * MAX_LINES_COUNT); LLRect messageRect = mMessage->getRect(); LLRect footerRect = mFooter->getRect(); diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp index 2529ec865a..45fbabad59 100644 --- a/indra/newview/lltoastscripttextbox.cpp +++ b/indra/newview/lltoastscripttextbox.cpp @@ -65,7 +65,7 @@ LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification pMessageText->clear(); LLStyle::Params style; - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //submit button diff --git a/indra/newview/lltoastscripttextbox.h b/indra/newview/lltoastscripttextbox.h index 8e69d8834d..7d33446248 100644 --- a/indra/newview/lltoastscripttextbox.h +++ b/indra/newview/lltoastscripttextbox.h @@ -39,8 +39,6 @@ class LLToastScriptTextbox public: void close(); - static bool onNewNotification(const LLSD& notification); - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 81ad96f39e..2b5acdc1e8 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -603,7 +603,7 @@ BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp BOOL LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* toolbar) { BOOL handled = FALSE; - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + LLInventoryObject* inv_item = static_cast<LLInventoryObject*>(cargo_data); LLAssetType::EType type = inv_item->getType(); if (type == LLAssetType::AT_WIDGET) diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index c69999981c..2680e4451b 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -58,7 +58,6 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llworld.h" -#include "llclipboard.h" // syntactic sugar #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) @@ -654,33 +653,41 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, sOperationId++; } + // For people drag and drop we don't need an actual inventory object, + // instead we need the current cargo id, which should be a person id. + bool is_uuid_dragged = (mSource == SOURCE_PEOPLE); + if (top_view) { handled = TRUE; for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); - EAcceptance item_acceptance = ACCEPT_NO; handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); - if (handled) - { - // use sort order to determine priority of acceptance - *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); - } } - else + else if (is_uuid_dragged) { - return; + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } + if (handled) + { + // use sort order to determine priority of acceptance + *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); } } @@ -697,20 +704,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + EAcceptance item_acceptance; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - - EAcceptance item_acceptance; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } if (handled) @@ -727,17 +741,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); // fix for EXT-3191 - if (NULL == cargo) return; - - EAcceptance item_acceptance = ACCEPT_NO; - handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, - mCargoTypes[mCurItemIndex], - (void*)cargo, - &item_acceptance, - mToolTipMsg); + if (cargo) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)cargo, + &item_acceptance, + mToolTipMsg); + } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } if (handled) { // use sort order to determine priority of acceptance @@ -757,17 +781,25 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + EAcceptance item_acceptance; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - EAcceptance item_acceptance; handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } @@ -2509,7 +2541,13 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = NULL; cat = NULL; - if(mCargoIDs.empty()) return NULL; + + if (mCargoIDs.empty() + || (mSource == SOURCE_PEOPLE)) ///< There is no inventory item for people drag and drop. + { + return NULL; + } + if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)) { // The object should be in user inventory. @@ -2545,6 +2583,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = (LLViewerInventoryItem*)gToolBarView->getDragItem(); } + if(item) return item; if(cat) return cat; return NULL; diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 41aee484db..f17300a76a 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -67,7 +67,8 @@ public: SOURCE_WORLD, SOURCE_NOTECARD, SOURCE_LIBRARY, - SOURCE_VIEWER + SOURCE_VIEWER, + SOURCE_PEOPLE }; void beginDrag(EDragAndDropType type, diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index a4b1c2155f..08ba5a5f25 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -83,6 +83,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_WIDGET, new ViewerAssetEntry(DAD_WIDGET)); + addEntry(LLViewerAssetType::AT_PERSON, new ViewerAssetEntry(DAD_PERSON)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); }; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 96303151f4..e45426a8d6 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -111,6 +111,7 @@ #include "llfloatertranslationsettings.h" #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" +#include "llfloatervoicevolume.h" #include "llfloaterwhitelistentry.h" #include "llfloaterwindowsize.h" #include "llfloaterworldmap.h" @@ -134,7 +135,6 @@ #include "llscriptfloater.h" #include "llfloatermodelpreview.h" #include "llcommandhandler.h" -#include "llnearbychatbar.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -187,8 +187,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>); - LLFloaterReg::add("chat_bar", "floater_chat_bar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChatBar>); - + LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>); @@ -221,6 +220,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); LLInspectRemoteObjectUtil::registerFloater(); + LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); LLFloaterDisplayNameUtil::registerFloater(); diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index a32a78cbf9..c7d37e102e 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -40,7 +40,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -130,7 +130,7 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - LLNearbyChatBar::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b47a41c44c..a187318eb7 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1030,12 +1030,7 @@ void CreateGestureCallback::fire(const LLUUID& inv_item) gFloaterView->adjustToFitScreen(preview, FALSE); } -void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) -{ - if (mTargetLandmarkId.isNull()) return; - gInventory.rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); -} LLInventoryCallbackManager gInventoryCallbacks; @@ -1308,7 +1303,7 @@ const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not) // ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements... -void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) +void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) { std::string type_name = userdata.asString(); @@ -1332,7 +1327,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); gInventory.notifyObservers(); - root->setSelectionByID(category, TRUE); + panel->setSelectionByID(category, TRUE); } else if ("lsl" == type_name) { @@ -1375,7 +1370,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons llwarns << "Can't create unrecognized type " << type_name << llendl; } } - root->setNeedsAutoRename(TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); } LLAssetType::EType LLViewerInventoryItem::getType() const @@ -1449,348 +1444,6 @@ const std::string& LLViewerInventoryItem::getName() const return LLInventoryItem::getName(); } -/** - * Class to store sorting order of favorites landmarks in a local file. EXT-3985. - * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. - * Data are stored in user home directory. - */ -class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> - , public LLDestroyClass<LLFavoritesOrderStorage> -{ - LOG_CLASS(LLFavoritesOrderStorage); -public: - /** - * Sets sort index for specified with LLUUID favorite landmark - */ - void setSortIndex(const LLUUID& inv_item_id, S32 sort_index); - - /** - * Gets sort index for specified with LLUUID favorite landmark - */ - S32 getSortIndex(const LLUUID& inv_item_id); - void removeSortIndex(const LLUUID& inv_item_id); - - void getSLURL(const LLUUID& asset_id); - - /** - * Implementation of LLDestroyClass. Calls cleanup() instance method. - * - * It is important this callback is called before gInventory is cleaned. - * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), - * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. - * @see cleanup() - */ - static void destroyClass(); - - const static S32 NO_INDEX; -private: - friend class LLSingleton<LLFavoritesOrderStorage>; - LLFavoritesOrderStorage() : mIsDirty(false) { load(); } - ~LLFavoritesOrderStorage() { save(); } - - /** - * Removes sort indexes for items which are not in Favorites bar for now. - */ - void cleanup(); - - const static std::string SORTING_DATA_FILE_NAME; - - void load(); - void save(); - - void saveFavoritesSLURLs(); - - // Remove record of current user's favorites from file on disk. - void removeFavoritesRecordOfUser(); - - void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); - void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); - - typedef std::map<LLUUID, S32> sort_index_map_t; - sort_index_map_t mSortIndexes; - - typedef std::map<LLUUID, std::string> slurls_map_t; - slurls_map_t mSLURLs; - - bool mIsDirty; - - struct IsNotInFavorites - { - IsNotInFavorites(const LLInventoryModel::item_array_t& items) - : mFavoriteItems(items) - { - - } - - /** - * Returns true if specified item is not found among inventory items - */ - bool operator()(const sort_index_map_t::value_type& id_index_pair) const - { - LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); - if (item.isNull()) return true; - - LLInventoryModel::item_array_t::const_iterator found_it = - std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); - - return found_it == mFavoriteItems.end(); - } - private: - LLInventoryModel::item_array_t mFavoriteItems; - }; - -}; - -const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; -const S32 LLFavoritesOrderStorage::NO_INDEX = -1; - -void LLFavoritesOrderStorage::setSortIndex(const LLUUID& inv_item_id, S32 sort_index) -{ - mSortIndexes[inv_item_id] = sort_index; - mIsDirty = true; -} - -S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) -{ - sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); - if (it != mSortIndexes.end()) - { - return it->second; - } - return NO_INDEX; -} - -void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) -{ - mSortIndexes.erase(inv_item_id); - mIsDirty = true; -} - -void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) -{ - slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); - if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached - - LLLandmark* lm = gLandmarkList.getAsset(asset_id, - boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); - if (lm) - { - onLandmarkLoaded(asset_id, lm); - } -} - -// static -void LLFavoritesOrderStorage::destroyClass() -{ - LLFavoritesOrderStorage::instance().cleanup(); - if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) - { - LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); - } - else - { - LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); - } -} - -void LLFavoritesOrderStorage::load() -{ - // load per-resident sorting information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); - } -} - -void LLFavoritesOrderStorage::saveFavoritesSLURLs() -{ - // Do not change the file if we are not logged in yet. - if (!LLLoginInstance::getInstance()->authSuccess()) - { - llwarns << "Cannot save favorites: not logged in" << llendl; - return; - } - - std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); - if (user_dir.empty()) - { - llwarns << "Cannot save favorites: empty user dir name" << llendl; - return; - } - - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - llifstream in_file; - in_file.open(filename); - LLSD fav_llsd; - if (in_file.is_open()) - { - LLSDSerialize::fromXML(fav_llsd, in_file); - } - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLSD user_llsd; - for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) - { - LLSD value; - value["name"] = (*it)->getName(); - value["asset_id"] = (*it)->getAssetUUID(); - - slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); - if (slurl_iter != mSLURLs.end()) - { - lldebugs << "Saving favorite: idx=" << (*it)->getSortField() << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; - value["slurl"] = slurl_iter->second; - user_llsd[(*it)->getSortField()] = value; - } - else - { - llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; - } - } - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; - fav_llsd[av_name.getLegacyName()] = user_llsd; - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, file); -} - -void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; - if (fav_llsd.has(av_name.getLegacyName())) - { - fav_llsd.erase(av_name.getLegacyName()); - } - - llofstream out_file; - out_file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, out_file); - -} - -void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) -{ - if (!landmark) return; - - LLVector3d pos_global; - if (!landmark->getGlobalPos(pos_global)) - { - // If global position was unknown on first getGlobalPos() call - // it should be set for the subsequent calls. - landmark->getGlobalPos(pos_global); - } - - if (!pos_global.isExactlyZero()) - { - LLLandmarkActions::getSLURLfromPosGlobal(pos_global, - boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); - } -} - -void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) -{ - lldebugs << "Saving landmark SLURL: " << slurl << llendl; - mSLURLs[asset_id] = slurl; -} - -void LLFavoritesOrderStorage::save() -{ - // nothing to save if clean - if (!mIsDirty) return; - - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ or similar. JC - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - LLSD settings_llsd; - - for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } -} - -void LLFavoritesOrderStorage::cleanup() -{ - // nothing to clean - if (!mIsDirty) return; - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - IsNotInFavorites is_not_in_fav(items); - - sort_index_map_t aTempMap; - //copy unremoved values from mSortIndexes to aTempMap - std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), - inserter(aTempMap, aTempMap.begin()), - is_not_in_fav); - - //Swap the contents of mSortIndexes and aTempMap - mSortIndexes.swap(aTempMap); -} - - -S32 LLViewerInventoryItem::getSortField() const -{ - return LLFavoritesOrderStorage::instance().getSortIndex(mUUID); -} - -void LLViewerInventoryItem::setSortField(S32 sortField) -{ - LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); - getSLURL(); -} - -void LLViewerInventoryItem::getSLURL() -{ - LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); -} - -const LLPermissions& LLViewerInventoryItem::getPermissions() const -{ - // Use the actual permissions of the symlink, not its parent. - return LLInventoryItem::getPermissions(); -} - const LLUUID& LLViewerInventoryItem::getCreatorUUID() const { if (const LLViewerInventoryItem *linked_item = getLinkedItem()) @@ -1861,17 +1514,6 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); } - -time_t LLViewerInventoryItem::getCreationDate() const -{ - return LLInventoryItem::getCreationDate(); -} - -U32 LLViewerInventoryItem::getCRC32() const -{ - return LLInventoryItem::getCRC32(); -} - // *TODO: mantipov: should be removed with LMSortPrefix patch in llinventorymodel.cpp, EXT-3985 static char getSeparator() { return '@'; } BOOL LLViewerInventoryItem::extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 7822ef4da6..3cf03c3bc5 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -34,7 +34,7 @@ #include <boost/signals2.hpp> // boost::signals2::trackable -class LLFolderView; +class LLInventoryPanel; class LLFolderBridge; class LLViewerInventoryCategory; @@ -60,10 +60,6 @@ public: virtual const LLUUID& getAssetUUID() const; virtual const LLUUID& getProtectedAssetUUID() const; // returns LLUUID::null if current agent does not have permission to expose this asset's UUID to the user virtual const std::string& getName() const; - virtual S32 getSortField() const; - virtual void setSortField(S32 sortField); - virtual void getSLURL(); //Caches SLURL for landmark. //*TODO: Find a better way to do it and remove this method from here. - virtual const LLPermissions& getPermissions() const; virtual const bool getIsFullPerm() const; // 'fullperm' in the popular sense: modify-ok & copy-ok & transfer-ok, no special god rules applied virtual const LLUUID& getCreatorUUID() const; virtual const std::string& getDescription() const; @@ -72,8 +68,11 @@ public: virtual bool isWearableType() const; virtual LLWearableType::EType getWearableType() const; virtual U32 getFlags() const; - virtual time_t getCreationDate() const; - virtual U32 getCRC32() const; // really more of a checksum. + + using LLInventoryItem::getPermissions; + using LLInventoryItem::getCreationDate; + using LLInventoryItem::setCreationDate; + using LLInventoryItem::getCRC32; static BOOL extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName); @@ -285,18 +284,6 @@ public: void fire(const LLUUID& inv_item); }; -class AddFavoriteLandmarkCallback : public LLInventoryCallback -{ -public: - AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} - void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } - -private: - void fire(const LLUUID& inv_item); - - LLUUID mTargetLandmarkId; -}; - // misc functions //void inventory_reliable_callback(void**, S32 status); @@ -372,7 +359,7 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, U32 callback_id = 0); -void menu_create_inventory_item(LLFolderView* root, +void menu_create_inventory_item(LLInventoryPanel* root, LLFolderBridge* bridge, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null); diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 1aa9fd8a45..385d3cd29a 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -31,7 +31,7 @@ #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -534,7 +534,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -543,15 +543,15 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if (LLNearbyChatBar::getInstance()->getCurrentChat().empty()) + if (LLNearbyChat::getInstance()->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' - LLNearbyChatBar::startChat("/"); + LLNearbyChat::startChat("/"); } else { // Don't overwrite existing text in chat editor - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 97ddfb48d2..7ab5a01c5f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3251,15 +3251,6 @@ bool enable_freeze_eject(const LLSD& avatar_id) return new_value; } - -void login_done(S32 which, void *user) -{ - llinfos << "Login done " << which << llendl; - - LLPanelLogin::closePanel(); -} - - bool callback_leave_group(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 0baf119d70..d03a58e9e3 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -183,6 +183,7 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLMessageSystem* msg = gMessageSystem; const LLSD& payload = notification["payload"]; + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); // add friend to recent people list LLRecentPeople::instance().add(payload["from_id"]); @@ -208,7 +209,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) msg->sendReliable(LLHost(payload["sender"].asString())); LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipAcceptedByMe", notification["substitutions"], payload); break; @@ -216,7 +216,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) case 1: // Decline { LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipDeclinedByMe", notification["substitutions"], payload); } @@ -245,6 +244,12 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) break; } + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Accept", false); + modified_form->setElementEnabled("Decline", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); @@ -725,7 +730,7 @@ static void highlight_inventory_objects_in_panel(const std::vector<LLUUID>& item LLFolderView* fv = inventory_panel->getRootFolder(); if (fv) { - LLFolderViewItem* fv_item = fv->getItemByID(item_id); + LLFolderViewItem* fv_item = inventory_panel->getItemByID(item_id); if (fv_item) { LLFolderViewItem* fv_folder = fv_item->getParentFolder(); @@ -813,7 +818,13 @@ private: mSelectedItems.clear(); if (LLInventoryPanel::getActiveInventoryPanel()) { - mSelectedItems = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + mSelectedItems.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } mSelectedItems.erase(mMoveIntoFolderID); } @@ -848,7 +859,15 @@ private: } // get selected items (without destination folder) - selected_items_t selected_items = active_panel->getRootFolder()->getSelectionList(); + selected_items_t selected_items; + + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + selected_items.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } selected_items.erase(mMoveIntoFolderID); // compare stored & current sets of selected items @@ -1154,7 +1173,7 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) } } } - + // Return "true" if we have a preview method for that asset type, "false" otherwise bool check_asset_previewable(const LLAssetType::EType asset_type) { @@ -1477,16 +1496,15 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); } + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + // For muting, we need to add the mute, then decline the offer. // This must be done here because: // * callback may be called immediately, // * adding the mute sends a message, // * we can't build two messages at once. - if (2 == button) // Block + if (IOR_MUTE == button) // Block { - LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - - llassert(notification_ptr != NULL); if (notification_ptr != NULL) { gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback, _1, _2, _3)); @@ -1501,6 +1519,8 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& bool busy = gAgent.getBusy(); + LLNotificationFormPtr modified_form(notification_ptr ? new LLNotificationForm(*notification_ptr->getForm()) : new LLNotificationForm()); + switch(button) { case IOR_SHOW: @@ -1544,6 +1564,11 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; break; } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + } break; // end switch (mIM) @@ -1556,9 +1581,14 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessageTip", args); } + break; case IOR_MUTE: + if (modified_form != NULL) + { + modified_form->setElementEnabled("Mute", false); + } // MUTE falls through to decline case IOR_DECLINE: { @@ -1594,6 +1624,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { busy_message(gMessageSystem, mFromID); } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + modified_form->setElementEnabled("Discard", false); + } + break; } default: @@ -1613,6 +1650,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { delete this; } + + if (notification_ptr != NULL) + { + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + } + return false; } @@ -1990,6 +2034,15 @@ bool lure_callback(const LLSD& notification, const LLSD& response) lure_id); break; } + + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Teleport", false); + modified_form->setElementEnabled("Cancel", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); @@ -2419,7 +2472,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; bool mute_im = is_muted; - if (accept_im_from_only_friend && !is_friend) + if(accept_im_from_only_friend&&!is_friend) { if (!gIMMgr->isNonFriendSessionNotified(session_id)) { @@ -2846,7 +2899,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) chat.mOwnerID = from_id; LLSD args; args["slurl"] = location; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; // Look for IRC-style emotes here so object name formatting is correct std::string prefix = message.substr(0, 4); @@ -3590,7 +3642,6 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // pass owner_id to chat so that we can display the remote // object inspect for an object that is chatting with you LLSD args; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; chat.mOwnerID = owner_id; if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) @@ -6784,7 +6835,6 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) //*TODO please rewrite all keys to the same case, lower or upper payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("TeleportOfferSent", args, payload); // Add the recepient to the recent people list. diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 7afb135470..c2cec9a335 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -56,6 +56,7 @@ // linden library includes #include "llaudioengine.h" // mute on minimize +#include "llchatentry.h" #include "indra_constants.h" #include "llassetstorage.h" #include "llerrorcontrol.h" @@ -186,7 +187,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -1549,11 +1550,12 @@ LLViewerWindow::LLViewerWindow(const Params& p) // boost::lambda::var() constructs such a functor on the fly. mWindowListener.reset(new LLWindowListener(this, boost::lambda::var(gKeyboard))); mViewerWindowListener.reset(new LLViewerWindowListener(this)); - LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); + mAlertsChannel.reset(new LLNotificationChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert"))); + mModalAlertsChannel.reset(new LLNotificationChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal"))); + + mAlertsChannel->connectChanged(&LLViewerWindow::onAlert); + mModalAlertsChannel->connectChanged(&LLViewerWindow::onAlert); LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; @@ -2489,11 +2491,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // Traverses up the hierarchy if( keyboard_focus ) { - LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChatBar>("chat_bar"); + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("chat_bar"); if (nearby_chat) { - LLLineEditor* chat_editor = nearby_chat->getChatBox(); + LLChatEntry* chat_editor = nearby_chat->getChatBox(); // arrow keys move avatar while chatting hack if (chat_editor && chat_editor->hasFocus()) @@ -2556,11 +2558,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar")->getChatBox(); + LLChatEntry* chat_editor = LLNearbyChat::getInstance()->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. - LLNearbyChatBar::getInstance()->startChat(NULL); + LLNearbyChat::getInstance()->startChat(NULL); return TRUE; } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 6efcaeaf18..ee6a7793f8 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -418,6 +418,9 @@ private: bool mActive; bool mUIVisible; + boost::shared_ptr<class LLNotificationChannel> mAlertsChannel, + mModalAlertsChannel; + LLRect mWindowRectRaw; // whole window, including UI LLRect mWindowRectScaled; // whole window, scaled by UI size LLRect mWorldViewRectRaw; // area of screen for 3D world diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 33dc12c473..e5362261cf 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -62,6 +62,7 @@ #include "llhudmanager.h" #include "llhudnametag.h" #include "llhudtext.h" // for mText/mDebugText +#include "llinitparam.h" #include "llkeyframefallmotion.h" #include "llkeyframestandmotion.h" #include "llkeyframewalkmotion.h" @@ -191,6 +192,9 @@ const S32 MAX_BUBBLE_CHAT_LENGTH = DB_CHAT_MSG_STR_LEN; const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12; const F32 CHAT_FADE_TIME = 8.0; const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f; +const F32 NAMETAG_UPDATE_THRESHOLD = 0.3f; +const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f; +const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f; const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0); @@ -222,55 +226,62 @@ struct LLTextureMaskData **/ //------------------------------------------------------------------------ -// LLVOBoneInfo +// LLVOAvatarBoneInfo // Trans/Scale/Rot etc. info about each avatar bone. Used by LLVOAvatarSkeleton. //------------------------------------------------------------------------ -class LLVOAvatarBoneInfo +struct LLVOAvatarCollisionVolumeInfo : public LLInitParam::Block<LLVOAvatarCollisionVolumeInfo> { - friend class LLVOAvatar; - friend class LLVOAvatarSkeletonInfo; -public: - LLVOAvatarBoneInfo() : mIsJoint(FALSE) {} - ~LLVOAvatarBoneInfo() + LLVOAvatarCollisionVolumeInfo() + : name("name"), + pos("pos"), + rot("rot"), + scale("scale") + {} + + Mandatory<std::string> name; + Mandatory<LLVector3> pos, + rot, + scale; +}; + +struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock<LLVOAvatarChildJoint> { - std::for_each(mChildList.begin(), mChildList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); + Alternative<Lazy<struct LLVOAvatarBoneInfo, IS_A_BLOCK> > bone; + Alternative<LLVOAvatarCollisionVolumeInfo> collision_volume; -private: - std::string mName; - BOOL mIsJoint; - LLVector3 mPos; - LLVector3 mRot; - LLVector3 mScale; - LLVector3 mPivot; - typedef std::vector<LLVOAvatarBoneInfo*> child_list_t; - child_list_t mChildList; + LLVOAvatarChildJoint() + : bone("bone"), + collision_volume("collision_volume") + {} +}; + +struct LLVOAvatarBoneInfo : public LLInitParam::Block<LLVOAvatarBoneInfo, LLVOAvatarCollisionVolumeInfo> +{ + LLVOAvatarBoneInfo() + : pivot("pivot") + {} + + Mandatory<LLVector3> pivot; + Multiple<LLVOAvatarChildJoint> children; }; //------------------------------------------------------------------------ // LLVOAvatarSkeletonInfo // Overall avatar skeleton //------------------------------------------------------------------------ -class LLVOAvatarSkeletonInfo +struct LLVOAvatarSkeletonInfo : public LLInitParam::Block<LLVOAvatarSkeletonInfo> { - friend class LLVOAvatar; -public: - LLVOAvatarSkeletonInfo() : - mNumBones(0), mNumCollisionVolumes(0) {} - ~LLVOAvatarSkeletonInfo() - { - std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); - S32 getNumBones() const { return mNumBones; } - S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; } + LLVOAvatarSkeletonInfo() + : skeleton_root(""), + num_bones("num_bones"), + num_collision_volumes("num_collision_volumes"), + version("version") + {} -private: - S32 mNumBones; - S32 mNumCollisionVolumes; - typedef std::vector<LLVOAvatarBoneInfo*> bone_info_list_t; - bone_info_list_t mBoneInfoList; + Mandatory<std::string> version; + Mandatory<S32> num_bones, + num_collision_volumes; + Mandatory<LLVOAvatarChildJoint> skeleton_root; }; //----------------------------------------------------------------------------- @@ -595,7 +606,7 @@ private: // Static Data //----------------------------------------------------------------------------- LLXmlTree LLVOAvatar::sXMLTree; -LLXmlTree LLVOAvatar::sSkeletonXMLTree; +LLXMLNodePtr LLVOAvatar::sSkeletonXMLTree; LLVOAvatarSkeletonInfo* LLVOAvatar::sAvatarSkeletonInfo = NULL; LLVOAvatar::LLVOAvatarXmlInfo* LLVOAvatar::sAvatarXmlInfo = NULL; LLVOAvatarDictionary *LLVOAvatar::sAvatarDictionary = NULL; @@ -698,9 +709,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLMemType mt(LLMemType::MTYPE_AVATAR); //VTResume(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer = new LLVoiceVisualizer(); +#endif // XXX_STINSON_CHUI_REWORK lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl; @@ -805,14 +820,14 @@ void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string c //------------------------------------------------------------------------ LLVOAvatar::~LLVOAvatar() { - if (!mFullyLoaded) - { + if (!mFullyLoaded) + { debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud"); - } - else - { + } + else + { debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding"); - } + } lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl; @@ -875,7 +890,11 @@ void LLVOAvatar::markDead() mNameText = NULL; sNumVisibleChatBubbles--; } +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->markDead(); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer->setStopSpeaking(); +#endif // XXX_STINSON_CHUI_REWORK LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ; LLViewerObject::markDead(); } @@ -1213,18 +1232,6 @@ void LLVOAvatar::initClass() llerrs << "Error parsing skeleton file: " << skeleton_path << llendl; } - // Process XML data - - // avatar_skeleton.xml - if (sAvatarSkeletonInfo) - { //this can happen if a login attempt failed - delete sAvatarSkeletonInfo; - } - sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; - if (!sAvatarSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot())) - { - llerrs << "Error parsing skeleton XML file: " << skeleton_path << llendl; - } // parse avatar_lad.xml if (sAvatarXmlInfo) { //this can happen if a login attempt failed @@ -1273,7 +1280,7 @@ void LLVOAvatar::initClass() void LLVOAvatar::cleanupClass() { deleteAndClear(sAvatarXmlInfo); - sSkeletonXMLTree.cleanup(); + sSkeletonXMLTree = NULL; sXMLTree.cleanup(); } @@ -1419,7 +1426,9 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); +#endif // XXX_STINSON_CHUI_REWORK } @@ -1745,33 +1754,39 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //------------------------------------------------------------------------- // parse the file //------------------------------------------------------------------------- - BOOL parsesuccess = sSkeletonXMLTree.parseFile( filename, FALSE ); - if (!parsesuccess) + LLXMLNodePtr skeleton_xml; + BOOL parsesuccess = LLXMLNode::parseFile(filename, skeleton_xml, NULL); + + if (!parsesuccess || skeleton_xml.isNull()) { llerrs << "Can't parse skeleton file: " << filename << llendl; return FALSE; } - // now sanity check xml file - LLXmlTreeNode* root = sSkeletonXMLTree.getRoot(); - if (!root) + // Process XML data + if (sAvatarSkeletonInfo) + { //this can happen if a login attempt failed + delete sAvatarSkeletonInfo; + } + sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; + + LLXUIParser parser; + parser.readXUI(skeleton_xml, *sAvatarSkeletonInfo, filename); + if (!sAvatarSkeletonInfo->validateBlock()) { - llerrs << "No root node found in avatar skeleton file: " << filename << llendl; - return FALSE; + llerrs << "Error parsing skeleton XML file: " << filename << llendl; } - if( !root->hasName( "linden_skeleton" ) ) + if( !skeleton_xml->hasName( "linden_skeleton" ) ) { llerrs << "Invalid avatar skeleton file header: " << filename << llendl; return FALSE; } - std::string version; - static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version"); - if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") ) + if (sAvatarSkeletonInfo->version() != "1.0") { - llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl; + llerrs << "Invalid avatar skeleton file version: " << sAvatarSkeletonInfo->version() << " in file: " << filename << llendl; return FALSE; } @@ -1780,14 +1795,13 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //----------------------------------------------------------------------------- // setupBone() -//----------------------------------------------------------------------------- -BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) +//----------------------------------------------------------- +BOOL LLVOAvatar::setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLViewerJoint* joint = NULL; - - if (info->mIsJoint) + if (info.bone.isChosen()) { joint = (LLViewerJoint*)getCharacterJoint(joint_num); if (!joint) @@ -1795,7 +1809,23 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent llwarns << "Too many bones" << llendl; return FALSE; } - joint->setName( info->mName ); + joint->setName( info.bone().name ); + joint->setPosition(info.bone().pos); + joint->setRotation(mayaQ(info.bone().rot().mV[VX], info.bone().rot().mV[VY], info.bone().rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.bone().scale); + joint->setSkinOffset( info.bone().pivot ); + joint_num++; + + for (LLInitParam::ParamIterator<LLVOAvatarChildJoint>::const_iterator child_it = info.bone().children.begin(), + end_it = info.bone().children.end(); + child_it != end_it; + ++child_it) + { + if (!setupBone(*child_it, joint, volume_num, joint_num)) + { + return FALSE; + } + } } else // collision volume { @@ -1805,7 +1835,11 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent return FALSE; } joint = (LLViewerJoint*)(&mCollisionVolumes[volume_num]); - joint->setName( info->mName ); + joint->setName( info.collision_volume.name); + joint->setPosition(info.collision_volume.pos); + joint->setRotation(mayaQ(info.collision_volume.rot().mV[VX], info.collision_volume.rot().mV[VY], info.collision_volume.rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.collision_volume.scale); + volume_num++; } // add to parent @@ -1814,34 +1848,8 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent parent->addChild( joint ); } - joint->setPosition(info->mPos); - joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY], - info->mRot.mV[VZ], LLQuaternion::XYZ)); - joint->setScale(info->mScale); - joint->setDefaultFromCurrentXform(); - if (info->mIsJoint) - { - joint->setSkinOffset( info->mPivot ); - joint_num++; - } - else // collision volume - { - volume_num++; - } - - // setup children - LLVOAvatarBoneInfo::child_list_t::const_iterator iter; - for (iter = info->mChildList.begin(); iter != info->mChildList.end(); ++iter) - { - LLVOAvatarBoneInfo *child_info = *iter; - if (!setupBone(child_info, joint, volume_num, joint_num)) - { - return FALSE; - } - } - return TRUE; } @@ -1855,36 +1863,32 @@ BOOL LLVOAvatar::buildSkeleton(const LLVOAvatarSkeletonInfo *info) //------------------------------------------------------------------------- // allocate joints //------------------------------------------------------------------------- - if (!allocateCharacterJoints(info->mNumBones)) + if (!allocateCharacterJoints(info->num_bones)) { - llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl; + llerrs << "Can't allocate " << info->num_bones() << " joints" << llendl; return FALSE; } //------------------------------------------------------------------------- // allocate volumes //------------------------------------------------------------------------- - if (info->mNumCollisionVolumes) + if (info->num_collision_volumes) { - if (!allocateCollisionVolumes(info->mNumCollisionVolumes)) + if (!allocateCollisionVolumes(info->num_collision_volumes)) { - llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl; + llerrs << "Can't allocate " << info->num_collision_volumes() << " collision volumes" << llendl; return FALSE; } } S32 current_joint_num = 0; S32 current_volume_num = 0; - LLVOAvatarSkeletonInfo::bone_info_list_t::const_iterator iter; - for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter) + + if (!setupBone(info->skeleton_root, NULL, current_volume_num, current_joint_num)) { - LLVOAvatarBoneInfo *info = *iter; - if (!setupBone(info, NULL, current_volume_num, current_joint_num)) - { llerrs << "Error parsing bone in skeleton file" << llendl; return FALSE; } - } return TRUE; } @@ -2096,15 +2100,15 @@ void LLVOAvatar::releaseMeshData() LLFace* facep = mDrawable->getFace(0); if (facep) { - facep->setSize(0, 0); - for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) - { - facep = mDrawable->getFace(i); + facep->setSize(0, 0); + for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) + { + facep = mDrawable->getFace(i); if (facep) { - facep->setSize(0, 0); - } - } + facep->setSize(0, 0); + } + } } } @@ -2353,11 +2357,11 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); // Print out arrival information once we have name of avatar. - if (has_name && getNVPair("FirstName")) - { - mDebugExistenceTimer.reset(); + if (has_name && getNVPair("FirstName")) + { + mDebugExistenceTimer.reset(); debugAvatarRezTime("AvatarRezArrivedNotification","avatar arrived"); - } + } if(retval & LLViewerObject::INVALID_UPDATE) { @@ -2525,6 +2529,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { +#ifdef XXX_STINSON_CHUI_REWORK bool render_visualizer = voice_enabled; // Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled. @@ -2537,6 +2542,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } mVoiceVisualizer->setVoiceEnabled(render_visualizer); +#endif // XXX_STINSON_CHUI_REWORK if ( voice_enabled ) { @@ -2612,6 +2618,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } } +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------------- // here we get the approximate head position and set as sound source for the voice symbol // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) @@ -2629,6 +2636,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) tagPos[VZ] += ( mBodySize[VZ] + 0.125f ); mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos ); } +#endif // XXX_STINSON_CHUI_REWORK }//if ( voiceEnabled ) } @@ -2868,8 +2876,8 @@ void LLVOAvatar::idleUpdateLoadingEffect() { LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; mFirstFullyVisible = FALSE; - LLAppearanceMgr::instance().onFirstFullyVisible(); - } + LLAppearanceMgr::instance().onFirstFullyVisible(); + } if (isFullyLoaded() && mFirstFullyVisible && !isSelf()) { LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; @@ -3017,43 +3025,43 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - BOOL new_name = FALSE; - if (visible_chat != mVisibleChat) - { - mVisibleChat = visible_chat; - new_name = TRUE; - } - - if (sRenderGroupTitles != mRenderGroupTitles) - { - mRenderGroupTitles = sRenderGroupTitles; - new_name = TRUE; - } - - // First Calculate Alpha - // If alpha > 0, create mNameText if necessary, otherwise delete it - F32 alpha = 0.f; - if (mAppAngle > 5.f) - { - const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; - if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + BOOL new_name = FALSE; + if (visible_chat != mVisibleChat) { - alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + mVisibleChat = visible_chat; + new_name = TRUE; } - else + + if (sRenderGroupTitles != mRenderGroupTitles) { - // ...not fading, full alpha - alpha = 1.f; + mRenderGroupTitles = sRenderGroupTitles; + new_name = TRUE; } - } - else if (mAppAngle > 2.f) - { - // far away is faded out also - alpha = (mAppAngle-2.f)/3.f; - } + + // First Calculate Alpha + // If alpha > 0, create mNameText if necessary, otherwise delete it + F32 alpha = 0.f; + if (mAppAngle > 5.f) + { + const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; + if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + { + alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + } + else + { + // ...not fading, full alpha + alpha = 1.f; + } + } + else if (mAppAngle > 2.f) + { + // far away is faded out also + alpha = (mAppAngle-2.f)/3.f; + } if (alpha <= 0.f) - { + { if (mNameText) { mNameText->markDead(); @@ -3063,22 +3071,21 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - if (!mNameText) - { + if (!mNameText) + { mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject( - LLHUDObject::LL_HUD_NAME_TAG) ); + LLHUDObject::LL_HUD_NAME_TAG) ); //mNameText->setMass(10.f); - mNameText->setSourceObject(this); + mNameText->setSourceObject(this); mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP); - mNameText->setVisibleOffScreen(TRUE); - mNameText->setMaxLines(11); - mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); - sNumVisibleChatBubbles++; - new_name = TRUE; - } + mNameText->setVisibleOffScreen(TRUE); + mNameText->setMaxLines(11); + mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); + sNumVisibleChatBubbles++; + new_name = TRUE; + } - LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last); - mNameText->setPositionAgent(name_position); + idleUpdateNameTagPosition(root_pos_last); idleUpdateNameTagText(new_name); idleUpdateNameTagAlpha(new_name, alpha); } @@ -3117,192 +3124,191 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) { debugAvatarRezTime("AvatarRezLeftAppearanceNotification","left appearance mode"); } - } - // Rebuild name tag if state change detected - if (mNameString.empty() - || new_name - || (!title && !mTitle.empty()) - || (title && mTitle != title->getString()) - || is_away != mNameAway - || is_busy != mNameBusy - || is_muted != mNameMute - || is_appearance != mNameAppearance - || is_friend != mNameFriend - || is_cloud != mNameCloud) - { - LLColor4 name_tag_color = getNameTagColor(is_friend); + // Rebuild name tag if state change detected + if (mNameString.empty() + || new_name + || (!title && !mTitle.empty()) + || (title && mTitle != title->getString()) + || is_away != mNameAway + || is_busy != mNameBusy + || is_muted != mNameMute + || is_appearance != mNameAppearance + || is_friend != mNameFriend + || is_cloud != mNameCloud) + { + LLColor4 name_tag_color = getNameTagColor(is_friend); - clearNameTag(); + clearNameTag(); - if (is_away || is_muted || is_busy || is_appearance) - { - std::string line; - if (is_away) + if (is_away || is_muted || is_busy || is_appearance) { - line += LLTrans::getString("AvatarAway"); - line += ", "; - } - if (is_busy) - { - line += LLTrans::getString("AvatarBusy"); - line += ", "; - } - if (is_muted) - { - line += LLTrans::getString("AvatarMuted"); - line += ", "; - } - if (is_appearance) - { - line += LLTrans::getString("AvatarEditingAppearance"); - line += ", "; + std::string line; + if (is_away) + { + line += LLTrans::getString("AvatarAway"); + line += ", "; + } + if (is_busy) + { + line += LLTrans::getString("AvatarBusy"); + line += ", "; + } + if (is_muted) + { + line += LLTrans::getString("AvatarMuted"); + line += ", "; + } + if (is_appearance) + { + line += LLTrans::getString("AvatarEditingAppearance"); + line += ", "; + } + if (is_cloud) + { + line += LLTrans::getString("LoadingData"); + line += ", "; + } + // trim last ", " + line.resize( line.length() - 2 ); + addNameTagLine(line, name_tag_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); } - if (is_cloud) + + if (sRenderGroupTitles + && title && title->getString() && title->getString()[0] != '\0') { - line += LLTrans::getString("LoadingData"); - line += ", "; + std::string title_str = title->getString(); + LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); + addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); } - // trim last ", " - line.resize( line.length() - 2 ); - addNameTagLine(line, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); - } - if (sRenderGroupTitles - && title && title->getString() && title->getString()[0] != '\0') - { - std::string title_str = title->getString(); - LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); - addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); - } + static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames"); + static LLUICachedControl<bool> show_usernames("NameTagShowUsernames"); - static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames"); - static LLUICachedControl<bool> show_usernames("NameTagShowUsernames"); - - if (LLAvatarNameCache::useDisplayNames()) - { - LLAvatarName av_name; - if (!LLAvatarNameCache::get(getID(), &av_name)) + if (LLAvatarNameCache::useDisplayNames()) { - // ...call this function back when the name arrives - // and force a rebuild - LLAvatarNameCache::get(getID(), - boost::bind(&LLVOAvatar::clearNameTag, this)); - } + LLAvatarName av_name; + if (!LLAvatarNameCache::get(getID(), &av_name)) + { + // ...call this function back when the name arrives + // and force a rebuild + LLAvatarNameCache::get(getID(), + boost::bind(&LLVOAvatar::clearNameTag, this)); + } - // Might be blank if name not available yet, that's OK - if (show_display_names) - { - addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerif()); + // Might be blank if name not available yet, that's OK + if (show_display_names) + { + addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerif()); + } + // Suppress SLID display if display name matches exactly (ugh) + if (show_usernames && !av_name.mIsDisplayNameDefault) + { + // *HACK: Desaturate the color + LLColor4 username_color = name_tag_color * 0.83f; + addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); + } } - // Suppress SLID display if display name matches exactly (ugh) - if (show_usernames && !av_name.mIsDisplayNameDefault) + else { - // *HACK: Desaturate the color - LLColor4 username_color = name_tag_color * 0.83f; - addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + const LLFontGL* font = LLFontGL::getFontSansSerif(); + std::string full_name = LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); + addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); } - } - else - { - const LLFontGL* font = LLFontGL::getFontSansSerif(); - std::string full_name = - LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); - addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); + + mNameAway = is_away; + mNameBusy = is_busy; + mNameMute = is_muted; + mNameAppearance = is_appearance; + mNameFriend = is_friend; + mNameCloud = is_cloud; + mTitle = title ? title->getString() : ""; + LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR); + new_name = TRUE; } - mNameAway = is_away; - mNameBusy = is_busy; - mNameMute = is_muted; - mNameAppearance = is_appearance; - mNameFriend = is_friend; - mNameCloud = is_cloud; - mTitle = title ? title->getString() : ""; - LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR); - new_name = TRUE; - } + if (mVisibleChat) + { + mNameText->setFont(LLFontGL::getFontSansSerif()); + mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT); + mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f); - if (mVisibleChat) - { - mNameText->setFont(LLFontGL::getFontSansSerif()); - mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT); - mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f); - - char line[MAX_STRING]; /* Flawfinder: ignore */ - line[0] = '\0'; - std::deque<LLChat>::iterator chat_iter = mChats.begin(); - mNameText->clearString(); + char line[MAX_STRING]; /* Flawfinder: ignore */ + line[0] = '\0'; + std::deque<LLChat>::iterator chat_iter = mChats.begin(); + mNameText->clearString(); - LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" ); - LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f); - LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f); - if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) - { - ++chat_iter; - } + LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" ); + LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f); + LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f); + if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) + { + ++chat_iter; + } - for(; chat_iter != mChats.end(); ++chat_iter) - { - F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f); - LLFontGL::StyleFlags style; - switch(chat_iter->mChatType) + for(; chat_iter != mChats.end(); ++chat_iter) { + F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f); + LLFontGL::StyleFlags style; + switch(chat_iter->mChatType) + { case CHAT_TYPE_WHISPER: - style = LLFontGL::ITALIC; - break; + style = LLFontGL::ITALIC; + break; case CHAT_TYPE_SHOUT: - style = LLFontGL::BOLD; - break; + style = LLFontGL::BOLD; + break; default: - style = LLFontGL::NORMAL; - break; - } - if (chat_fade_amt < 1.f) - { - F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f); - mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style); - } - else if (chat_fade_amt < 2.f) - { - F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f); - mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style); - } - else if (chat_fade_amt < 3.f) - { - // *NOTE: only remove lines down to minimum number - mNameText->addLine(chat_iter->mText, old_chat, style); + style = LLFontGL::NORMAL; + break; + } + if (chat_fade_amt < 1.f) + { + F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f); + mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style); + } + else if (chat_fade_amt < 2.f) + { + F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f); + mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style); + } + else if (chat_fade_amt < 3.f) + { + // *NOTE: only remove lines down to minimum number + mNameText->addLine(chat_iter->mText, old_chat, style); + } } - } - mNameText->setVisibleOffScreen(TRUE); + mNameText->setVisibleOffScreen(TRUE); - if (mTyping) - { - S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1; - switch(dot_count) + if (mTyping) { + S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1; + switch(dot_count) + { case 1: - mNameText->addLine(".", new_chat); - break; + mNameText->addLine(".", new_chat); + break; case 2: - mNameText->addLine("..", new_chat); - break; + mNameText->addLine("..", new_chat); + break; case 3: - mNameText->addLine("...", new_chat); - break; - } + mNameText->addLine("...", new_chat); + break; + } + } + } + else + { + // ...not using chat bubbles, just names + mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_CENTER); + mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); + mNameText->setVisibleOffScreen(FALSE); } - } - else - { - // ...not using chat bubbles, just names - mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_CENTER); - mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); - mNameText->setVisibleOffScreen(FALSE); } } @@ -3354,34 +3360,45 @@ void LLVOAvatar::invalidateNameTags() if (avatar->isDead()) continue; avatar->clearNameTag(); - } } // Compute name tag position during idle update -LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) +void LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) { LLQuaternion root_rot = mRoot.getWorldRotation(); + LLQuaternion inv_root_rot = ~root_rot; LLVector3 pixel_right_vec; LLVector3 pixel_up_vec; LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); camera_to_av.normalize(); - LLVector3 local_camera_at = camera_to_av * ~root_rot; + LLVector3 local_camera_at = camera_to_av * inv_root_rot; LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); local_camera_up.normalize(); - local_camera_up = local_camera_up * ~root_rot; + local_camera_up = local_camera_up * inv_root_rot; + + LLVector3 avatar_ellipsoid(mBodySize.mV[VX] * 0.4f, + mBodySize.mV[VY] * 0.4f, + mBodySize.mV[VZ] * NAMETAG_VERT_OFFSET_WEIGHT); + + local_camera_up.scaleVec(avatar_ellipsoid); + local_camera_at.scaleVec(avatar_ellipsoid); - local_camera_up.scaleVec(mBodySize * 0.5f); - local_camera_at.scaleVec(mBodySize * 0.5f); + LLVector3 head_offset = (mHeadp->getLastWorldPosition() - mRoot.getLastWorldPosition()) * inv_root_rot; - LLVector3 name_position = mRoot.getWorldPosition(); - name_position[VZ] -= mPelvisToFoot; - name_position[VZ] += (mBodySize[VZ]* 0.55f); + if (dist_vec(head_offset, mTargetRootToHeadOffset) > NAMETAG_UPDATE_THRESHOLD) + { + mTargetRootToHeadOffset = head_offset; + } + + mCurRootToHeadOffset = lerp(mCurRootToHeadOffset, mTargetRootToHeadOffset, LLCriticalDamp::getInterpolant(0.2f)); + + LLVector3 name_position = mRoot.getLastWorldPosition() + (mCurRootToHeadOffset * root_rot); name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av)); - name_position += pixel_up_vec * 15.f; + name_position += pixel_up_vec * NAMETAG_VERTICAL_SCREEN_OFFSET; - return name_position; + mNameText->setPositionAgent(name_position); } void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) @@ -3454,9 +3471,9 @@ bool LLVOAvatar::isVisuallyMuted() const static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit"); static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit"); - return LLMuteList::getInstance()->isMuted(getID()) || - (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) || - (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); + return LLMuteList::getInstance()->isMuted(getID()) + || (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) + || (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); } //------------------------------------------------------------------------ @@ -3497,8 +3514,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } - LLVector3d root_pos_global; - if (!mIsBuilt) { return FALSE; @@ -3513,7 +3528,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mTimeVisible.reset(); } - //-------------------------------------------------------------------- // the rest should only be done occasionally for far away avatars //-------------------------------------------------------------------- @@ -3916,10 +3930,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if ( playSound ) { -// F32 gain = clamp_rescale( mSpeedAccum, -// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED, -// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN ); - const F32 STEP_VOLUME = 0.1f; const LLUUID& step_sound_id = getStepSound(); @@ -4136,13 +4146,6 @@ void LLVOAvatar::updateVisibility() { releaseMeshData(); } - // this breaks off-screen chat bubbles - //if (mNameText) - //{ - // mNameText->markDead(); - // mNameText = NULL; - // sNumVisibleChatBubbles--; - //} } mVisible = visible; @@ -4158,46 +4161,6 @@ bool LLVOAvatar::shouldAlphaMask() } -U32 LLVOAvatar::renderSkinnedAttachments() -{ - /*U32 num_indices = 0; - - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) - { - LLViewerJointAttachment* attachment = iter->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - const LLViewerObject* attached_object = (*attachment_iter); - if (attached_object && !attached_object->isHUDAttachment()) - { - const LLDrawable* drawable = attached_object->mDrawable; - if (drawable) - { - for (S32 i = 0; i < drawable->getNumFaces(); ++i) - { - LLFace* face = drawable->getFace(i); - if (face->isState(LLFace::RIGGED)) - { - - } - } - } - } - - return num_indices;*/ - return 0; -} - //----------------------------------------------------------------------------- // renderSkinned() //----------------------------------------------------------------------------- @@ -4218,11 +4181,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) { //LOD changed or new mesh created, allocate new vertex buffer if needed if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4) { - updateMeshData(); + updateMeshData(); mDirtyMesh = 0; - mNeedsSkin = TRUE; - mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); - } + mNeedsSkin = TRUE; + mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); + } } if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0) @@ -4251,13 +4214,13 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) if (face) { LLVertexBuffer* vb = face->getVertexBuffer(); - if (vb) - { - vb->flush(); - } + if (vb) + { + vb->flush(); } } } + } else { mNeedsSkin = FALSE; @@ -5887,7 +5850,6 @@ BOOL LLVOAvatar::updateJointLODs() F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor); F32 area_scale = 0.16f; - { if (isSelf()) { if(gAgentCamera.cameraCustomizeAvatar() || gAgentCamera.cameraMouselook()) @@ -5917,7 +5879,6 @@ BOOL LLVOAvatar::updateJointLODs() dirtyMesh(2); return TRUE; } - } return FALSE; } @@ -6206,14 +6167,9 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) if ( pVObj ) { const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); - if ( pSkinData ) - { - const int jointCnt = pSkinData->mJointNames.size(); - bool fullRig = ( jointCnt>=20 ) ? true : false; - if ( fullRig ) - { - const int bindCnt = pSkinData->mAlternateBindMatrix.size(); - if ( bindCnt > 0 ) + if (pSkinData + && pSkinData->mJointNames.size() > 20 // full rig + && pSkinData->mAlternateBindMatrix.size() > 0) { LLVOAvatar::resetJointPositionsToDefault(); //Need to handle the repositioning of the cam, updating rig data etc during outfit editing @@ -6228,8 +6184,6 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) } } } - } -} //----------------------------------------------------------------------------- // detachObject() //----------------------------------------------------------------------------- @@ -6378,11 +6332,7 @@ void LLVOAvatar::getOffObject() at_axis.mV[VZ] = 0.f; at_axis.normalize(); gAgent.resetAxes(at_axis); - - //reset orientation -// mRoot.setRotation(avWorldRot); gAgentCamera.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f)); - gAgentCamera.setSitCamera(LLUUID::null); } } @@ -6432,7 +6382,6 @@ LLColor4 LLVOAvatar::getGlobalColor( const std::string& color_name ) const } else { -// return LLColor4( .5f, .5f, .5f, .5f ); return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color } } @@ -6597,8 +6546,8 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE); - if (!mPreviousFullyLoaded && !loading && mFullyLoaded) - { + if (!mPreviousFullyLoaded && !loading && mFullyLoaded) + { debugAvatarRezTime("AvatarRezNotification","fully loaded"); } @@ -7191,10 +7140,6 @@ LLBBox LLVOAvatar::getHUDBBox() const return bbox; } -void LLVOAvatar::rebuildHUD() -{ -} - //----------------------------------------------------------------------------- // onFirstTEMessageReceived() //----------------------------------------------------------------------------- @@ -7320,7 +7265,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) && baked_index != BAKED_SKIRT) { setTEImage(mBakedTextureDatas[baked_index].mTextureIndex, - LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)); + LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, + TRUE, + LLViewerTexture::BOOST_NONE, + LLViewerTexture::LOD_TEXTURE)); } } @@ -7579,7 +7527,7 @@ void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTextu LLUUID *avatar_idp = (LLUUID *)userdata; LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp); - + if (selfp) { LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << LL_ENDL; @@ -7630,13 +7578,6 @@ void LLVOAvatar::onBakedTextureLoaded(BOOL success, // Called when baked texture is loaded and also when we start up with a baked texture void LLVOAvatar::useBakedTexture( const LLUUID& id ) { - - - /* if(id == head_baked->getID()) - mHeadBakedLoaded = TRUE; - mLastHeadBakedID = id; - mHeadMesh0.setTexture( head_baked ); - mHeadMesh1.setTexture( head_baked ); */ for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 ); @@ -7880,111 +7821,111 @@ LLVOAvatar::LLVOAvatarXmlInfo::~LLVOAvatarXmlInfo() std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer()); } -//----------------------------------------------------------------------------- -// LLVOAvatarBoneInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) -{ - if (node->hasName("bone")) - { - mIsJoint = TRUE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - llwarns << "Bone without name" << llendl; - return FALSE; - } - } - else if (node->hasName("collision_volume")) - { - mIsJoint = FALSE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - mName = "Collision Volume"; - } - } - else - { - llwarns << "Invalid node " << node->getName() << llendl; - return FALSE; - } - - static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); - if (!node->getFastAttributeVector3(pos_string, mPos)) - { - llwarns << "Bone without position" << llendl; - return FALSE; - } - - static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); - if (!node->getFastAttributeVector3(rot_string, mRot)) - { - llwarns << "Bone without rotation" << llendl; - return FALSE; - } - - static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); - if (!node->getFastAttributeVector3(scale_string, mScale)) - { - llwarns << "Bone without scale" << llendl; - return FALSE; - } - - if (mIsJoint) - { - static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); - if (!node->getFastAttributeVector3(pivot_string, mPivot)) - { - llwarns << "Bone without pivot" << llendl; - return FALSE; - } - } - - // parse children - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; - if (!child_info->parseXml(child)) - { - delete child_info; - return FALSE; - } - mChildList.push_back(child_info); - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// LLVOAvatarSkeletonInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) -{ - static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); - if (!node->getFastAttributeS32(num_bones_string, mNumBones)) - { - llwarns << "Couldn't find number of bones." << llendl; - return FALSE; - } - - static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); - node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); - - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; - if (!info->parseXml(child)) - { - delete info; - llwarns << "Error parsing bone in skeleton file" << llendl; - return FALSE; - } - mBoneInfoList.push_back(info); - } - return TRUE; -} +////----------------------------------------------------------------------------- +//// LLVOAvatarBoneInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) +//{ +// if (node->hasName("bone")) +// { +// mIsJoint = TRUE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// llwarns << "Bone without name" << llendl; +// return FALSE; +// } +// } +// else if (node->hasName("collision_volume")) +// { +// mIsJoint = FALSE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// mName = "Collision Volume"; +// } +// } +// else +// { +// llwarns << "Invalid node " << node->getName() << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); +// if (!node->getFastAttributeVector3(pos_string, mPos)) +// { +// llwarns << "Bone without position" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); +// if (!node->getFastAttributeVector3(rot_string, mRot)) +// { +// llwarns << "Bone without rotation" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); +// if (!node->getFastAttributeVector3(scale_string, mScale)) +// { +// llwarns << "Bone without scale" << llendl; +// return FALSE; +// } +// +// if (mIsJoint) +// { +// static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); +// if (!node->getFastAttributeVector3(pivot_string, mPivot)) +// { +// llwarns << "Bone without pivot" << llendl; +// return FALSE; +// } +// } +// +// // parse children +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; +// if (!child_info->parseXml(child)) +// { +// delete child_info; +// return FALSE; +// } +// mChildList.push_back(child_info); +// } +// return TRUE; +//} +// +////----------------------------------------------------------------------------- +//// LLVOAvatarSkeletonInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) +//{ +// static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); +// if (!node->getFastAttributeS32(num_bones_string, mNumBones)) +// { +// llwarns << "Couldn't find number of bones." << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); +// node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); +// +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; +// if (!info->parseXml(child)) +// { +// delete info; +// llwarns << "Error parsing bone in skeleton file" << llendl; +// return FALSE; +// } +// mBoneInfoList.push_back(info); +// } +// return TRUE; +//} //----------------------------------------------------------------------------- // parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 6fb56a4c0b..9a69e15743 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -67,8 +67,9 @@ class LLVoiceVisualizer; class LLHUDNameTag; class LLHUDEffectSpiral; class LLTexGlobalColor; -class LLVOAvatarBoneInfo; -class LLVOAvatarSkeletonInfo; +struct LLVOAvatarBoneInfo; +struct LLVOAvatarChildJoint; +struct LLVOAvatarSkeletonInfo; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatar @@ -233,7 +234,7 @@ public: void idleUpdateWindEffect(); void idleUpdateNameTag(const LLVector3& root_pos_last); void idleUpdateNameTagText(BOOL new_name); - LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last); + void idleUpdateNameTagPosition(const LLVector3& root_pos_last); void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha); LLColor4 getNameTagColor(bool is_friend); void clearNameTag(); @@ -353,6 +354,8 @@ public: F32 mLastPelvisToFoot; F32 mPelvisFixup; F32 mLastPelvisFixup; + LLVector3 mCurRootToHeadOffset; + LLVector3 mTargetRootToHeadOffset; LLVector3 mHeadOffset; // current head position LLViewerJoint mRoot; @@ -361,7 +364,7 @@ protected: void buildCharacter(); virtual BOOL loadAvatar(); - BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); + BOOL setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info); private: BOOL mIsBuilt; // state of deferred character building @@ -405,7 +408,7 @@ public: //-------------------------------------------------------------------- private: static LLXmlTree sXMLTree; // avatar config file - static LLXmlTree sSkeletonXMLTree; // avatar skeleton file + static LLXMLNodePtr sSkeletonXMLTree; // avatar skeleton file /** Skeleton ** ** @@ -423,7 +426,6 @@ public: U32 renderRigid(); U32 renderSkinned(EAvatarRenderPass pass); F32 getLastSkinTime() { return mLastSkinTime; } - U32 renderSkinnedAttachments(); U32 renderTransparent(BOOL first_pass); void renderCollisionVolumes(); static void deleteCachedImages(bool clearAll=true); @@ -772,7 +774,6 @@ public: public: BOOL hasHUDAttachment() const; LLBBox getHUDBBox() const; - void rebuildHUD(); void resetHUDAttachments(); BOOL canAttachMoreObjects() const; BOOL canAttachMoreObjects(U32 n) const; diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 47060720e7..dcf33bce10 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -1,625 +1,642 @@ -/** - * @file llvoicevisualizer.cpp - * @brief Draws in-world speaking indicators. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -//---------------------------------------------------------------------- -// Voice Visualizer -// author: JJ Ventrella -// (information about this stuff can be found in "llvoicevisualizer.h") -//---------------------------------------------------------------------- -#include "llviewerprecompiledheaders.h" -#include "llviewercontrol.h" -#include "llglheaders.h" -#include "llsphere.h" -#include "llvoicevisualizer.h" -#include "llviewercamera.h" -#include "llviewerobject.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "llvoiceclient.h" -#include "llrender.h" - -//brent's wave image -//29de489d-0491-fb00-7dab-f9e686d31e83 - - -//-------------------------------------------------------------------------------------- -// sound symbol constants -//-------------------------------------------------------------------------------------- -const F32 HEIGHT_ABOVE_HEAD = 0.3f; // how many meters vertically above the av's head the voice symbol will appear -const F32 RED_THRESHOLD = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; // value above which speaking amplitude causes the voice symbol to turn red -const F32 GREEN_THRESHOLD = 0.2f; // value above which speaking amplitude causes the voice symbol to turn green -const F32 FADE_OUT_DURATION = 0.4f; // how many seconds it takes for a pair of waves to fade away -const F32 EXPANSION_RATE = 1.0f; // how many seconds it takes for the waves to expand to twice their original size -const F32 EXPANSION_MAX = 1.5f; // maximum size scale to which the waves can expand before popping back to 1.0 -const F32 WAVE_WIDTH_SCALE = 0.03f; // base width of the waves -const F32 WAVE_HEIGHT_SCALE = 0.02f; // base height of the waves -const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when quiet (below green threshold) -const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture -const F32 DOT_OPACITY = 0.7f; // how opaque the dot is -const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude - -//-------------------------------------------------------------------------------------- -// gesticulation constants -//-------------------------------------------------------------------------------------- -const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f; -const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f; - -//-------------------------------------------------------------------------------------- -// other constants -//-------------------------------------------------------------------------------------- -const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code. -const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL - - -//------------------------------------------------------------------ -// handles parameter updates -//------------------------------------------------------------------ -static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue) -{ - // Note: Ignore the specific event value, we look up the ones we want - LLVoiceVisualizer::setPreferences(); - return true; -} - -//------------------------------------------------------------------ -// Initialize the statics -//------------------------------------------------------------------ -bool LLVoiceVisualizer::sPrefsInitialized = false; -BOOL LLVoiceVisualizer::sLipSyncEnabled = FALSE; -F32* LLVoiceVisualizer::sOoh = NULL; -F32* LLVoiceVisualizer::sAah = NULL; -U32 LLVoiceVisualizer::sOohs = 0; -U32 LLVoiceVisualizer::sAahs = 0; -F32 LLVoiceVisualizer::sOohAahRate = 0.0f; -F32* LLVoiceVisualizer::sOohPowerTransfer = NULL; -U32 LLVoiceVisualizer::sOohPowerTransfers = 0; -F32 LLVoiceVisualizer::sOohPowerTransfersf = 0.0f; -F32* LLVoiceVisualizer::sAahPowerTransfer = NULL; -U32 LLVoiceVisualizer::sAahPowerTransfers = 0; -F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f; - - -//----------------------------------------------- -// constructor -//----------------------------------------------- -LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) -:LLHUDEffect( type ) -{ - mCurrentTime = mTimer.getTotalSeconds(); - mPreviousTime = mCurrentTime; - mStartTime = mCurrentTime; - mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f ); - mSpeakingAmplitude = 0.0f; - mCurrentlySpeaking = false; - mVoiceEnabled = false; - mMinGesticulationAmplitude = DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE; - mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE; - mSoundSymbol.mActive = true; - mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f ); - - mTimer.reset(); - - const char* sound_level_img[] = - { - "voice_meter_dot.j2c", - "voice_meter_rings.j2c", - "voice_meter_rings.j2c", - "voice_meter_rings.j2c", - "voice_meter_rings.j2c", - "voice_meter_rings.j2c", - "voice_meter_rings.j2c" - }; - - for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++) - { - mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime; - mSoundSymbol.mTexture [i] = LLViewerTextureManager::getFetchedTextureFromFile(sound_level_img[i], FALSE, LLViewerTexture::BOOST_UI); - mSoundSymbol.mWaveActive [i] = false; - mSoundSymbol.mWaveOpacity [i] = 1.0f; - mSoundSymbol.mWaveExpansion [i] = 1.0f; - } - - mSoundSymbol.mTexture[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); - - // The first instance loads the initial state from prefs. - if (!sPrefsInitialized) - { - setPreferences(); - - // Set up our listener to get updates on all prefs values we care about. - gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - - sPrefsInitialized = true; - } - -}//--------------------------------------------------- - -//--------------------------------------------------- -void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m ) -{ - mMinGesticulationAmplitude = m; - -}//--------------------------------------------------- - -//--------------------------------------------------- -void LLVoiceVisualizer::setMaxGesticulationAmplitude( F32 m ) -{ - mMaxGesticulationAmplitude = m; - -}//--------------------------------------------------- - -//--------------------------------------------------- -void LLVoiceVisualizer::setVoiceEnabled( bool v ) -{ - mVoiceEnabled = v; - -}//--------------------------------------------------- - -//--------------------------------------------------- -void LLVoiceVisualizer::setStartSpeaking() -{ - mStartTime = mTimer.getTotalSeconds(); - mCurrentlySpeaking = true; - mSoundSymbol.mActive = true; - -}//--------------------------------------------------- - - -//--------------------------------------------------- -bool LLVoiceVisualizer::getCurrentlySpeaking() -{ - return mCurrentlySpeaking; - -}//--------------------------------------------------- - - -//--------------------------------------------------- -void LLVoiceVisualizer::setStopSpeaking() -{ - mCurrentlySpeaking = false; - mSpeakingAmplitude = 0.0f; - -}//--------------------------------------------------- - - -//--------------------------------------------------- -void LLVoiceVisualizer::setSpeakingAmplitude( F32 a ) -{ - mSpeakingAmplitude = a; - -}//--------------------------------------------------- - - -//--------------------------------------------------- -void LLVoiceVisualizer::setPreferences( ) -{ - sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled"); - sOohAahRate = gSavedSettings.getF32("LipSyncOohAahRate"); - - std::string oohString = gSavedSettings.getString("LipSyncOoh"); - lipStringToF32s (oohString, sOoh, sOohs); - - std::string aahString = gSavedSettings.getString("LipSyncAah"); - lipStringToF32s (aahString, sAah, sAahs); - - std::string oohPowerString = gSavedSettings.getString("LipSyncOohPowerTransfer"); - lipStringToF32s (oohPowerString, sOohPowerTransfer, sOohPowerTransfers); - sOohPowerTransfersf = (F32) sOohPowerTransfers; - - std::string aahPowerString = gSavedSettings.getString("LipSyncAahPowerTransfer"); - lipStringToF32s (aahPowerString, sAahPowerTransfer, sAahPowerTransfers); - sAahPowerTransfersf = (F32) sAahPowerTransfers; - -}//--------------------------------------------------- - - -//--------------------------------------------------- -// convert a string of digits to an array of floats. -// the result for each digit is the value of the -// digit multiplied by 0.11 -//--------------------------------------------------- -void LLVoiceVisualizer::lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ) -{ - delete[] out_F32s; // get rid of the current array - - count_F32s = in_string.length(); - if (count_F32s == 0) - { - // we don't like zero length arrays - - count_F32s = 1; - out_F32s = new F32[1]; - out_F32s[0] = 0.0f; - } - else - { - out_F32s = new F32[count_F32s]; - - for (U32 i=0; i<count_F32s; i++) - { - // we convert the characters 0 to 9 to their numeric value - // anything else we take the low order four bits with a ceiling of 9 - - U8 digit = in_string[i]; - U8 four_bits = digit % 16; - if (four_bits > 9) - { - four_bits = 9; - } - out_F32s[i] = 0.11f * (F32) four_bits; - } - } - -}//--------------------------------------------------- - - -//-------------------------------------------------------------------------- -// find the amount to blend the ooh and aah mouth morphs -//-------------------------------------------------------------------------- -void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) -{ - if( ( sLipSyncEnabled == TRUE ) && mCurrentlySpeaking ) - { - U32 transfer_index = (U32) (sOohPowerTransfersf * mSpeakingAmplitude); - if (transfer_index >= sOohPowerTransfers) - { - transfer_index = sOohPowerTransfers - 1; - } - F32 transfer_ooh = sOohPowerTransfer[transfer_index]; - - transfer_index = (U32) (sAahPowerTransfersf * mSpeakingAmplitude); - if (transfer_index >= sAahPowerTransfers) - { - transfer_index = sAahPowerTransfers - 1; - } - F32 transfer_aah = sAahPowerTransfer[transfer_index]; - - F64 current_time = mTimer.getTotalSeconds(); - F64 elapsed_time = current_time - mStartTime; - U32 elapsed_frames = (U32) (elapsed_time * sOohAahRate); - U32 elapsed_oohs = elapsed_frames % sOohs; - U32 elapsed_aahs = elapsed_frames % sAahs; - - ooh = transfer_ooh * sOoh[elapsed_oohs]; - aah = transfer_aah * sAah[elapsed_aahs]; - - /* - llinfos << " elapsed frames " << elapsed_frames - << " ooh " << ooh - << " aah " << aah - << " transfer ooh" << transfer_ooh - << " transfer aah" << transfer_aah - << " start time " << mStartTime - << " current time " << current_time - << " elapsed time " << elapsed_time - << " elapsed oohs " << elapsed_oohs - << " elapsed aahs " << elapsed_aahs - << llendl; - */ - } - else - { - ooh = 0.0f; - aah = 0.0f; - } - -}//--------------------------------------------------- - - -//--------------------------------------------------- -// this method is inherited from HUD Effect -//--------------------------------------------------- -void LLVoiceVisualizer::render() -{ - if ( ! mVoiceEnabled ) - { - return; - } - - if ( mSoundSymbol.mActive ) - { - mPreviousTime = mCurrentTime; - mCurrentTime = mTimer.getTotalSeconds(); - - //--------------------------------------------------------------- - // set the sound symbol position over the source (avatar's head) - //--------------------------------------------------------------- - mSoundSymbol.mPosition = mVoiceSourceWorldPosition + WORLD_UPWARD_DIRECTION * HEIGHT_ABOVE_HEAD; - - //--------------------------------------------------------------- - // some gl state - //--------------------------------------------------------------- - LLGLSPipelineAlpha alpha_blend; - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - - //------------------------------------------------------------- - // create coordinates of the geometry for the dot - //------------------------------------------------------------- - LLViewerCamera* camera = LLViewerCamera::getInstance(); - LLVector3 l = camera->getLeftAxis() * DOT_SIZE; - LLVector3 u = camera->getUpAxis() * DOT_SIZE; - - LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u; - LLVector3 bottomRight = mSoundSymbol.mPosition - l - u; - LLVector3 topLeft = mSoundSymbol.mPosition + l + u; - LLVector3 topRight = mSoundSymbol.mPosition - l + u; - - //----------------------------- - // bind texture 0 (the dot) - //----------------------------- - gGL.getTexUnit(0)->bind(mSoundSymbol.mTexture[0]); - - //------------------------------------------------------------- - // now render the dot - //------------------------------------------------------------- - gGL.color4fv( LLColor4( 1.0f, 1.0f, 1.0f, DOT_OPACITY ).mV ); - - gGL.begin( LLRender::TRIANGLE_STRIP ); - gGL.texCoord2i( 0, 0 ); gGL.vertex3fv( bottomLeft.mV ); - gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV ); - gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV ); - gGL.end(); - - gGL.begin( LLRender::TRIANGLE_STRIP ); - gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV ); - gGL.texCoord2i( 1, 1 ); gGL.vertex3fv( topRight.mV ); - gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV ); - gGL.end(); - - - - //-------------------------------------------------------------------------------------- - // if currently speaking, trigger waves (1 through 6) based on speaking amplitude - //-------------------------------------------------------------------------------------- - if ( mCurrentlySpeaking ) - { - F32 min = 0.2f; - F32 max = 0.7f; - F32 fraction = ( mSpeakingAmplitude - min ) / ( max - min ); - - // in case mSpeakingAmplitude > max.... - if ( fraction > 1.0f ) - { - fraction = 1.0f; - } - - S32 level = 1 + (int)( fraction * ( NUM_VOICE_SYMBOL_WAVES - 2 ) ); - - for (int i=0; i<level+1; i++) - { - mSoundSymbol.mWaveActive [i] = true; - mSoundSymbol.mWaveOpacity [i] = 1.0f; - mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime; - } - - } // if currently speaking - - //--------------------------------------------------- - // determine color - //--------------------------------------------------- - F32 red = 0.0f; - F32 green = 0.0f; - F32 blue = 0.0f; - if ( mSpeakingAmplitude < RED_THRESHOLD ) - { - if ( mSpeakingAmplitude < GREEN_THRESHOLD ) - { - red = BASE_BRIGHTNESS; - green = BASE_BRIGHTNESS; - blue = BASE_BRIGHTNESS; - } - else - { - //--------------------------------------------------- - // fade from gray to bright green - //--------------------------------------------------- - F32 fraction = ( mSpeakingAmplitude - GREEN_THRESHOLD ) / ( 1.0f - GREEN_THRESHOLD ); - red = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS ); - green = BASE_BRIGHTNESS + fraction * ( 1.0f - BASE_BRIGHTNESS ); - blue = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS ); - } - } - else - { - //--------------------------------------------------- - // redish - //--------------------------------------------------- - red = 1.0f; - green = 0.2f; - blue = 0.2f; - } - - for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++) - { - if ( mSoundSymbol.mWaveActive[i] ) - { - F32 fadeOutFraction = (F32)( mCurrentTime - mSoundSymbol.mWaveFadeOutStartTime[i] ) / FADE_OUT_DURATION; - - mSoundSymbol.mWaveOpacity[i] = 1.0f - fadeOutFraction; - - if ( mSoundSymbol.mWaveOpacity[i] < 0.0f ) - { - mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime; - mSoundSymbol.mWaveOpacity [i] = 0.0f; - mSoundSymbol.mWaveActive [i] = false; - } - - //---------------------------------------------------------------------------------- - // This is where we calculate the expansion of the waves - that is, the - // rate at which they are scaled greater than 1.0 so that they grow over time. - //---------------------------------------------------------------------------------- - F32 timeSlice = (F32)( mCurrentTime - mPreviousTime ); - F32 waveSpeed = mSpeakingAmplitude * WAVE_MOTION_RATE; - mSoundSymbol.mWaveExpansion[i] *= ( 1.0f + EXPANSION_RATE * timeSlice * waveSpeed ); - - if ( mSoundSymbol.mWaveExpansion[i] > EXPANSION_MAX ) - { - mSoundSymbol.mWaveExpansion[i] = 1.0f; - } - - //---------------------------------------------------------------------------------- - // create geometry for the wave billboard textures - //---------------------------------------------------------------------------------- - F32 width = i * WAVE_WIDTH_SCALE * mSoundSymbol.mWaveExpansion[i]; - F32 height = i * WAVE_HEIGHT_SCALE * mSoundSymbol.mWaveExpansion[i]; - - LLVector3 l = camera->getLeftAxis() * width; - LLVector3 u = camera->getUpAxis() * height; - - LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u; - LLVector3 bottomRight = mSoundSymbol.mPosition - l - u; - LLVector3 topLeft = mSoundSymbol.mPosition + l + u; - LLVector3 topRight = mSoundSymbol.mPosition - l + u; - - gGL.color4fv( LLColor4( red, green, blue, mSoundSymbol.mWaveOpacity[i] ).mV ); - gGL.getTexUnit(0)->bind(mSoundSymbol.mTexture[i]); - - - //--------------------------------------------------- - // now, render the mofo - //--------------------------------------------------- - gGL.begin( LLRender::TRIANGLE_STRIP ); - gGL.texCoord2i( 0, 0 ); gGL.vertex3fv( bottomLeft.mV ); - gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV ); - gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV ); - gGL.end(); - - gGL.begin( LLRender::TRIANGLE_STRIP ); - gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV ); - gGL.texCoord2i( 1, 1 ); gGL.vertex3fv( topRight.mV ); - gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV ); - gGL.end(); - - } //if ( mSoundSymbol.mWaveActive[i] ) - - }// for loop - - }//if ( mSoundSymbol.mActive ) - -}//--------------------------------------------------- - - - - - -//--------------------------------------------------- -void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p ) -{ - mVoiceSourceWorldPosition = p; - -}//--------------------------------------------------- - -//--------------------------------------------------- -VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel() -{ - VoiceGesticulationLevel gesticulationLevel = VOICE_GESTICULATION_LEVEL_OFF; //default - - //----------------------------------------------------------------------------------------- - // Within the range of gesticulation amplitudes, the sound signal is split into - // three equal amplitude regimes, each specifying one of three gesticulation levels. - //----------------------------------------------------------------------------------------- - F32 range = mMaxGesticulationAmplitude - mMinGesticulationAmplitude; - - if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.5f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_HIGH; } - else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.25f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_MEDIUM; } - else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.00000f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_LOW; } - - return gesticulationLevel; - -}//--------------------------------------------------- - - - -//------------------------------------ -// Destructor -//------------------------------------ -LLVoiceVisualizer::~LLVoiceVisualizer() -{ -}//---------------------------------------------- - - -//--------------------------------------------------- -// "packData" is inherited from HUDEffect -//--------------------------------------------------- -void LLVoiceVisualizer::packData(LLMessageSystem *mesgsys) -{ - // Pack the default data - LLHUDEffect::packData(mesgsys); - - // TODO -- pack the relevant data for voice effects - // we'll come up with some cool configurations....TBD - //U8 packed_data[41]; - //mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41); - U8 packed_data = 0; - mesgsys->addBinaryDataFast(_PREHASH_TypeData, &packed_data, 1); -} - - -//--------------------------------------------------- -// "unpackData" is inherited from HUDEffect -//--------------------------------------------------- -void LLVoiceVisualizer::unpackData(LLMessageSystem *mesgsys, S32 blocknum) -{ - // TODO -- find the speaker, unpack binary data, set the properties of this effect - /* - LLHUDEffect::unpackData(mesgsys, blocknum); - LLUUID source_id; - LLUUID target_id; - S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData); - if (size != 1) - { - llwarns << "Voice effect with bad size " << size << llendl; - return; - } - mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 1, blocknum); - */ -} - - -//------------------------------------------------------------------ -// this method is inherited from HUD Effect -//------------------------------------------------------------------ -void LLVoiceVisualizer::markDead() -{ - mCurrentlySpeaking = false; - mVoiceEnabled = false; - mSoundSymbol.mActive = false; - - LLHUDEffect::markDead(); -}//------------------------------------------------------------------ - - - - - - - - +/**
+ * @file llvoicevisualizer.cpp
+ * @brief Draws in-world speaking indicators.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//----------------------------------------------------------------------
+// Voice Visualizer
+// author: JJ Ventrella
+// (information about this stuff can be found in "llvoicevisualizer.h")
+//----------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+#include "llviewercontrol.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llvoicevisualizer.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewertexture.h"
+#include "llviewertexturelist.h"
+#include "llvoiceclient.h"
+#include "llrender.h"
+
+//brent's wave image
+//29de489d-0491-fb00-7dab-f9e686d31e83
+
+
+#ifdef XXX_STINSON_CHUI_REWORK
+//--------------------------------------------------------------------------------------
+// sound symbol constants
+//--------------------------------------------------------------------------------------
+const F32 HEIGHT_ABOVE_HEAD = 0.3f; // how many meters vertically above the av's head the voice symbol will appear
+const F32 RED_THRESHOLD = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; // value above which speaking amplitude causes the voice symbol to turn red
+const F32 GREEN_THRESHOLD = 0.2f; // value above which speaking amplitude causes the voice symbol to turn green
+const F32 FADE_OUT_DURATION = 0.4f; // how many seconds it takes for a pair of waves to fade away
+const F32 EXPANSION_RATE = 1.0f; // how many seconds it takes for the waves to expand to twice their original size
+const F32 EXPANSION_MAX = 1.5f; // maximum size scale to which the waves can expand before popping back to 1.0
+const F32 WAVE_WIDTH_SCALE = 0.03f; // base width of the waves
+const F32 WAVE_HEIGHT_SCALE = 0.02f; // base height of the waves
+const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when quiet (below green threshold)
+const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture
+const F32 DOT_OPACITY = 0.7f; // how opaque the dot is
+const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude
+#endif // XXX_STINSON_CHUI_REWORK
+
+//--------------------------------------------------------------------------------------
+// gesticulation constants
+//--------------------------------------------------------------------------------------
+const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f;
+const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f;
+
+#ifdef XXX_STINSON_CHUI_REWORK
+//--------------------------------------------------------------------------------------
+// other constants
+//--------------------------------------------------------------------------------------
+const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code.
+const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL
+#endif // XXX_STINSON_CHUI_REWORK
+
+//------------------------------------------------------------------
+// Initialize the statics
+//------------------------------------------------------------------
+bool LLVoiceVisualizer::sPrefsInitialized = false;
+BOOL LLVoiceVisualizer::sLipSyncEnabled = FALSE;
+F32* LLVoiceVisualizer::sOoh = NULL;
+F32* LLVoiceVisualizer::sAah = NULL;
+U32 LLVoiceVisualizer::sOohs = 0;
+U32 LLVoiceVisualizer::sAahs = 0;
+F32 LLVoiceVisualizer::sOohAahRate = 0.0f;
+F32* LLVoiceVisualizer::sOohPowerTransfer = NULL;
+U32 LLVoiceVisualizer::sOohPowerTransfers = 0;
+F32 LLVoiceVisualizer::sOohPowerTransfersf = 0.0f;
+F32* LLVoiceVisualizer::sAahPowerTransfer = NULL;
+U32 LLVoiceVisualizer::sAahPowerTransfers = 0;
+F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f;
+
+
+//-----------------------------------------------
+// constructor
+//-----------------------------------------------
+#ifdef XXX_STINSON_CHUI_REWORK
+LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
+#else // XXX_STINSON_CHUI_REWORK
+LLVoiceVisualizer::LLVoiceVisualizer()
+ : LLRefCount(),
+ mTimer(),
+ mStartTime(0.0),
+ mCurrentlySpeaking(false),
+ mSpeakingAmplitude(0.0f),
+ mMaxGesticulationAmplitude(DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE),
+ mMinGesticulationAmplitude(DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE)
+#endif // XXX_STINSON_CHUI_REWORK
+{
+#ifdef XXX_STINSON_CHUI_REWORK
+ mCurrentTime = mTimer.getTotalSeconds();
+ mPreviousTime = mCurrentTime;
+ mStartTime = mCurrentTime;
+#else // XXX_STINSON_CHUI_REWORK
+ mStartTime = mTimer.getTotalSeconds();
+#endif // XXX_STINSON_CHUI_REWORK
+#ifdef XXX_STINSON_CHUI_REWORK
+ mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+ mSpeakingAmplitude = 0.0f;
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mMinGesticulationAmplitude = DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE;
+ mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE;
+ mSoundSymbol.mActive = true;
+ mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+#endif // XXX_STINSON_CHUI_REWORK
+
+ mTimer.reset();
+
+#ifdef XXX_STINSON_CHUI_REWORK
+ const char* sound_level_img[] =
+ {
+ "voice_meter_dot.j2c",
+ "voice_meter_rings.j2c",
+ "voice_meter_rings.j2c",
+ "voice_meter_rings.j2c",
+ "voice_meter_rings.j2c",
+ "voice_meter_rings.j2c",
+ "voice_meter_rings.j2c"
+ };
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mTexture [i] = LLViewerTextureManager::getFetchedTextureFromFile(sound_level_img[i], FALSE, LLViewerTexture::BOOST_UI);
+ mSoundSymbol.mWaveActive [i] = false;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveExpansion [i] = 1.0f;
+ }
+
+ mSoundSymbol.mTexture[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+#endif // XXX_STINSON_CHUI_REWORK
+
+ // The first instance loads the initial state from prefs.
+ if (!sPrefsInitialized)
+ {
+ setPreferences();
+
+ // Set up our listener to get updates on all prefs values we care about.
+ gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+ gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+ gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+ gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+ gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+ gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2));
+
+ sPrefsInitialized = true;
+ }
+
+}//---------------------------------------------------
+
+#ifdef XXX_STINSON_CHUI_REWORK
+//---------------------------------------------------
+void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m )
+{
+ mMinGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setMaxGesticulationAmplitude( F32 m )
+{
+ mMaxGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceEnabled( bool v )
+{
+ mVoiceEnabled = v;
+
+}//---------------------------------------------------
+#endif // XXX_STINSON_CHUI_REWORK
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStartSpeaking()
+{
+ mStartTime = mTimer.getTotalSeconds();
+ mCurrentlySpeaking = true;
+#ifdef XXX_STINSON_CHUI_REWORK
+ mSoundSymbol.mActive = true;
+#endif // XXX_STINSON_CHUI_REWORK
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+bool LLVoiceVisualizer::getCurrentlySpeaking()
+{
+ return mCurrentlySpeaking;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStopSpeaking()
+{
+ mCurrentlySpeaking = false;
+ mSpeakingAmplitude = 0.0f;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setSpeakingAmplitude( F32 a )
+{
+ mSpeakingAmplitude = a;
+
+}//---------------------------------------------------
+
+//------------------------------------------------------------------
+// handles parameter updates
+//------------------------------------------------------------------
+bool LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged(const LLSD& newvalue)
+{
+ // Note: Ignore the specific event value, we look up the ones we want
+ LLVoiceVisualizer::setPreferences();
+ return true;
+}
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setPreferences( )
+{
+ sLipSyncEnabled = gSavedSettings.getBOOL("LipSyncEnabled");
+ sOohAahRate = gSavedSettings.getF32("LipSyncOohAahRate");
+
+ std::string oohString = gSavedSettings.getString("LipSyncOoh");
+ lipStringToF32s (oohString, sOoh, sOohs);
+
+ std::string aahString = gSavedSettings.getString("LipSyncAah");
+ lipStringToF32s (aahString, sAah, sAahs);
+
+ std::string oohPowerString = gSavedSettings.getString("LipSyncOohPowerTransfer");
+ lipStringToF32s (oohPowerString, sOohPowerTransfer, sOohPowerTransfers);
+ sOohPowerTransfersf = (F32) sOohPowerTransfers;
+
+ std::string aahPowerString = gSavedSettings.getString("LipSyncAahPowerTransfer");
+ lipStringToF32s (aahPowerString, sAahPowerTransfer, sAahPowerTransfers);
+ sAahPowerTransfersf = (F32) sAahPowerTransfers;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+// convert a string of digits to an array of floats.
+// the result for each digit is the value of the
+// digit multiplied by 0.11
+//---------------------------------------------------
+void LLVoiceVisualizer::lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s )
+{
+ delete[] out_F32s; // get rid of the current array
+
+ count_F32s = in_string.length();
+ if (count_F32s == 0)
+ {
+ // we don't like zero length arrays
+
+ count_F32s = 1;
+ out_F32s = new F32[1];
+ out_F32s[0] = 0.0f;
+ }
+ else
+ {
+ out_F32s = new F32[count_F32s];
+
+ for (U32 i=0; i<count_F32s; i++)
+ {
+ // we convert the characters 0 to 9 to their numeric value
+ // anything else we take the low order four bits with a ceiling of 9
+
+ U8 digit = in_string[i];
+ U8 four_bits = digit % 16;
+ if (four_bits > 9)
+ {
+ four_bits = 9;
+ }
+ out_F32s[i] = 0.11f * (F32) four_bits;
+ }
+ }
+
+}//---------------------------------------------------
+
+
+//--------------------------------------------------------------------------
+// find the amount to blend the ooh and aah mouth morphs
+//--------------------------------------------------------------------------
+void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah )
+{
+ if( ( sLipSyncEnabled == TRUE ) && mCurrentlySpeaking )
+ {
+ U32 transfer_index = (U32) (sOohPowerTransfersf * mSpeakingAmplitude);
+ if (transfer_index >= sOohPowerTransfers)
+ {
+ transfer_index = sOohPowerTransfers - 1;
+ }
+ F32 transfer_ooh = sOohPowerTransfer[transfer_index];
+
+ transfer_index = (U32) (sAahPowerTransfersf * mSpeakingAmplitude);
+ if (transfer_index >= sAahPowerTransfers)
+ {
+ transfer_index = sAahPowerTransfers - 1;
+ }
+ F32 transfer_aah = sAahPowerTransfer[transfer_index];
+
+ F64 current_time = mTimer.getTotalSeconds();
+ F64 elapsed_time = current_time - mStartTime;
+ U32 elapsed_frames = (U32) (elapsed_time * sOohAahRate);
+ U32 elapsed_oohs = elapsed_frames % sOohs;
+ U32 elapsed_aahs = elapsed_frames % sAahs;
+
+ ooh = transfer_ooh * sOoh[elapsed_oohs];
+ aah = transfer_aah * sAah[elapsed_aahs];
+
+ /*
+ llinfos << " elapsed frames " << elapsed_frames
+ << " ooh " << ooh
+ << " aah " << aah
+ << " transfer ooh" << transfer_ooh
+ << " transfer aah" << transfer_aah
+ << " start time " << mStartTime
+ << " current time " << current_time
+ << " elapsed time " << elapsed_time
+ << " elapsed oohs " << elapsed_oohs
+ << " elapsed aahs " << elapsed_aahs
+ << llendl;
+ */
+ }
+ else
+ {
+ ooh = 0.0f;
+ aah = 0.0f;
+ }
+
+}//---------------------------------------------------
+
+
+#ifdef XXX_STINSON_CHUI_REWORK
+//---------------------------------------------------
+// this method is inherited from HUD Effect
+//---------------------------------------------------
+void LLVoiceVisualizer::render()
+{
+ if ( ! mVoiceEnabled )
+ {
+ return;
+ }
+
+ if ( mSoundSymbol.mActive )
+ {
+ mPreviousTime = mCurrentTime;
+ mCurrentTime = mTimer.getTotalSeconds();
+
+ //---------------------------------------------------------------
+ // set the sound symbol position over the source (avatar's head)
+ //---------------------------------------------------------------
+ mSoundSymbol.mPosition = mVoiceSourceWorldPosition + WORLD_UPWARD_DIRECTION * HEIGHT_ABOVE_HEAD;
+
+ //---------------------------------------------------------------
+ // some gl state
+ //---------------------------------------------------------------
+ LLGLSPipelineAlpha alpha_blend;
+ LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+
+ //-------------------------------------------------------------
+ // create coordinates of the geometry for the dot
+ //-------------------------------------------------------------
+ LLViewerCamera* camera = LLViewerCamera::getInstance();
+ LLVector3 l = camera->getLeftAxis() * DOT_SIZE;
+ LLVector3 u = camera->getUpAxis() * DOT_SIZE;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ //-----------------------------
+ // bind texture 0 (the dot)
+ //-----------------------------
+ gGL.getTexUnit(0)->bind(mSoundSymbol.mTexture[0]);
+
+ //-------------------------------------------------------------
+ // now render the dot
+ //-------------------------------------------------------------
+ gGL.color4fv( LLColor4( 1.0f, 1.0f, 1.0f, DOT_OPACITY ).mV );
+
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ gGL.texCoord2i( 0, 0 ); gGL.vertex3fv( bottomLeft.mV );
+ gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV );
+ gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV );
+ gGL.end();
+
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV );
+ gGL.texCoord2i( 1, 1 ); gGL.vertex3fv( topRight.mV );
+ gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV );
+ gGL.end();
+
+
+
+ //--------------------------------------------------------------------------------------
+ // if currently speaking, trigger waves (1 through 6) based on speaking amplitude
+ //--------------------------------------------------------------------------------------
+ if ( mCurrentlySpeaking )
+ {
+ F32 min = 0.2f;
+ F32 max = 0.7f;
+ F32 fraction = ( mSpeakingAmplitude - min ) / ( max - min );
+
+ // in case mSpeakingAmplitude > max....
+ if ( fraction > 1.0f )
+ {
+ fraction = 1.0f;
+ }
+
+ S32 level = 1 + (int)( fraction * ( NUM_VOICE_SYMBOL_WAVES - 2 ) );
+
+ for (int i=0; i<level+1; i++)
+ {
+ mSoundSymbol.mWaveActive [i] = true;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ }
+
+ } // if currently speaking
+
+ //---------------------------------------------------
+ // determine color
+ //---------------------------------------------------
+ F32 red = 0.0f;
+ F32 green = 0.0f;
+ F32 blue = 0.0f;
+ if ( mSpeakingAmplitude < RED_THRESHOLD )
+ {
+ if ( mSpeakingAmplitude < GREEN_THRESHOLD )
+ {
+ red = BASE_BRIGHTNESS;
+ green = BASE_BRIGHTNESS;
+ blue = BASE_BRIGHTNESS;
+ }
+ else
+ {
+ //---------------------------------------------------
+ // fade from gray to bright green
+ //---------------------------------------------------
+ F32 fraction = ( mSpeakingAmplitude - GREEN_THRESHOLD ) / ( 1.0f - GREEN_THRESHOLD );
+ red = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ green = BASE_BRIGHTNESS + fraction * ( 1.0f - BASE_BRIGHTNESS );
+ blue = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ }
+ }
+ else
+ {
+ //---------------------------------------------------
+ // redish
+ //---------------------------------------------------
+ red = 1.0f;
+ green = 0.2f;
+ blue = 0.2f;
+ }
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ if ( mSoundSymbol.mWaveActive[i] )
+ {
+ F32 fadeOutFraction = (F32)( mCurrentTime - mSoundSymbol.mWaveFadeOutStartTime[i] ) / FADE_OUT_DURATION;
+
+ mSoundSymbol.mWaveOpacity[i] = 1.0f - fadeOutFraction;
+
+ if ( mSoundSymbol.mWaveOpacity[i] < 0.0f )
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mWaveOpacity [i] = 0.0f;
+ mSoundSymbol.mWaveActive [i] = false;
+ }
+
+ //----------------------------------------------------------------------------------
+ // This is where we calculate the expansion of the waves - that is, the
+ // rate at which they are scaled greater than 1.0 so that they grow over time.
+ //----------------------------------------------------------------------------------
+ F32 timeSlice = (F32)( mCurrentTime - mPreviousTime );
+ F32 waveSpeed = mSpeakingAmplitude * WAVE_MOTION_RATE;
+ mSoundSymbol.mWaveExpansion[i] *= ( 1.0f + EXPANSION_RATE * timeSlice * waveSpeed );
+
+ if ( mSoundSymbol.mWaveExpansion[i] > EXPANSION_MAX )
+ {
+ mSoundSymbol.mWaveExpansion[i] = 1.0f;
+ }
+
+ //----------------------------------------------------------------------------------
+ // create geometry for the wave billboard textures
+ //----------------------------------------------------------------------------------
+ F32 width = i * WAVE_WIDTH_SCALE * mSoundSymbol.mWaveExpansion[i];
+ F32 height = i * WAVE_HEIGHT_SCALE * mSoundSymbol.mWaveExpansion[i];
+
+ LLVector3 l = camera->getLeftAxis() * width;
+ LLVector3 u = camera->getUpAxis() * height;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ gGL.color4fv( LLColor4( red, green, blue, mSoundSymbol.mWaveOpacity[i] ).mV );
+ gGL.getTexUnit(0)->bind(mSoundSymbol.mTexture[i]);
+
+
+ //---------------------------------------------------
+ // now, render the mofo
+ //---------------------------------------------------
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ gGL.texCoord2i( 0, 0 ); gGL.vertex3fv( bottomLeft.mV );
+ gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV );
+ gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV );
+ gGL.end();
+
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ gGL.texCoord2i( 1, 0 ); gGL.vertex3fv( bottomRight.mV );
+ gGL.texCoord2i( 1, 1 ); gGL.vertex3fv( topRight.mV );
+ gGL.texCoord2i( 0, 1 ); gGL.vertex3fv( topLeft.mV );
+ gGL.end();
+
+ } //if ( mSoundSymbol.mWaveActive[i] )
+
+ }// for loop
+
+ }//if ( mSoundSymbol.mActive )
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p )
+{
+ mVoiceSourceWorldPosition = p;
+
+}//---------------------------------------------------
+#endif // XXX_STINSON_CHUI_REWORK
+
+//---------------------------------------------------
+VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel()
+{
+ VoiceGesticulationLevel gesticulationLevel = VOICE_GESTICULATION_LEVEL_OFF; //default
+
+ //-----------------------------------------------------------------------------------------
+ // Within the range of gesticulation amplitudes, the sound signal is split into
+ // three equal amplitude regimes, each specifying one of three gesticulation levels.
+ //-----------------------------------------------------------------------------------------
+ F32 range = mMaxGesticulationAmplitude - mMinGesticulationAmplitude;
+
+ if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.5f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_HIGH; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.25f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_MEDIUM; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.00000f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_LOW; }
+
+ return gesticulationLevel;
+
+}//---------------------------------------------------
+
+
+
+//------------------------------------
+// Destructor
+//------------------------------------
+LLVoiceVisualizer::~LLVoiceVisualizer()
+{
+}//----------------------------------------------
+
+
+#ifdef XXX_STINSON_CHUI_REWORK
+//---------------------------------------------------
+// "packData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // TODO -- pack the relevant data for voice effects
+ // we'll come up with some cool configurations....TBD
+ //U8 packed_data[41];
+ //mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41);
+ U8 packed_data = 0;
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, &packed_data, 1);
+}
+
+
+//---------------------------------------------------
+// "unpackData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ // TODO -- find the speaker, unpack binary data, set the properties of this effect
+ /*
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 1)
+ {
+ llwarns << "Voice effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 1, blocknum);
+ */
+}
+
+
+//------------------------------------------------------------------
+// this method is inherited from HUD Effect
+//------------------------------------------------------------------
+void LLVoiceVisualizer::markDead()
+{
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mSoundSymbol.mActive = false;
+
+ LLHUDEffect::markDead();
+}//------------------------------------------------------------------
+
+#endif // XXX_STINSON_CHUI_REWORK
diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h index e434c7f3f1..6258de163d 100644 --- a/indra/newview/llvoicevisualizer.h +++ b/indra/newview/llvoicevisualizer.h @@ -42,7 +42,11 @@ #ifndef LL_VOICE_VISUALIZER_H #define LL_VOICE_VISUALIZER_H +#ifdef XXX_STINSON_CHUI_REWORK #include "llhudeffect.h" +#else // XXX_STINSON_CHUI_REWORK +#include "llpointer.h" +#endif // XXX_STINSON_CHUI_REWORK //----------------------------------------------------------------------------------------------- // The values of voice gesticulation represent energy levels for avatar animation, based on @@ -60,34 +64,45 @@ enum VoiceGesticulationLevel NUM_VOICE_GESTICULATION_LEVELS }; +#ifdef XXX_STINSON_CHUI_REWORK const static int NUM_VOICE_SYMBOL_WAVES = 7; +#endif // XXX_STINSON_CHUI_REWORK //---------------------------------------------------- // LLVoiceVisualizer class //---------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK class LLVoiceVisualizer : public LLHUDEffect +#else // XXX_STINSON_CHUI_REWORK +class LLVoiceVisualizer : public LLRefCount +#endif // XXX_STINSON_CHUI_REWORK { //--------------------------------------------------- // public methods //--------------------------------------------------- public: - LLVoiceVisualizer ( const U8 type ); //constructor +#ifdef XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer( const U8 type ); //constructor +#else // XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer(); //constructor +#endif // XXX_STINSON_CHUI_REWORK ~LLVoiceVisualizer(); //destructor - - friend class LLHUDObject; +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level +#endif // XXX_STINSON_CHUI_REWORK void setStartSpeaking(); // tell me when the av starts speaking +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled +#endif // XXX_STINSON_CHUI_REWORK void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1) void setStopSpeaking(); // tell me when the av stops speaking bool getCurrentlySpeaking(); // the get for the above set VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech - static void setPreferences( ); - static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats void lipSyncOohAah( F32& ooh, F32& aah ); +#ifdef XXX_STINSON_CHUI_REWORK void render(); // inherited from HUD Effect void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect @@ -103,12 +118,17 @@ class LLVoiceVisualizer : public LLHUDEffect //---------------------------------------------------------------------------------------------- void setMaxGesticulationAmplitude(); void setMinGesticulationAmplitude(); +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // private members //--------------------------------------------------- private: - + static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue); + static void setPreferences( ); + static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats + +#ifdef XXX_STINSON_CHUI_REWORK struct SoundSymbol { F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ]; @@ -119,15 +139,20 @@ class LLVoiceVisualizer : public LLHUDEffect bool mActive; LLVector3 mPosition; }; +#endif // XXX_STINSON_CHUI_REWORK LLFrameTimer mTimer; // so I can ask the current time in seconds F64 mStartTime; // time in seconds when speaking started +#ifdef XXX_STINSON_CHUI_REWORK F64 mCurrentTime; // current time in seconds, captured every step F64 mPreviousTime; // copy of "current time" from last frame SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head bool mVoiceEnabled; // if off, no rendering should happen +#endif // XXX_STINSON_CHUI_REWORK bool mCurrentlySpeaking; // is the user currently speaking? +#ifdef XXX_STINSON_CHUI_REWORK LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol +#endif // XXX_STINSON_CHUI_REWORK F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 49c4f37871..7c744f3c3f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4831,11 +4831,6 @@ void LLPipeline::rebuildPools() } max_count--; } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->rebuildHUD(); - } } void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) @@ -6354,7 +6349,7 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) } void LLPipeline::resetVertexBuffers() -{ +{ mResetVertexBuffers = true; } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index b616e2327b..5205988d5d 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -766,7 +766,7 @@ reference="LtGray" /> <color name="UserChatColor" - reference="White" /> + reference="Yellow" /> <color name="llOwnerSayChatColor" reference="LtYellow" /> diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png Binary files differnew file mode 100755 index 0000000000..f024c733f3 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png Binary files differnew file mode 100755 index 0000000000..a19e720d42 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png Binary files differnew file mode 100755 index 0000000000..7f3f42639d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png Binary files differnew file mode 100755 index 0000000000..2880eb766a --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png Binary files differnew file mode 100755 index 0000000000..25a939d7f5 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png Binary files differnew file mode 100755 index 0000000000..82baabde47 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png Binary files differnew file mode 100755 index 0000000000..7d64abb042 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png Binary files differnew file mode 100755 index 0000000000..f0da962c2d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png Binary files differnew file mode 100755 index 0000000000..0db001dcdb --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png Binary files differnew file mode 100755 index 0000000000..0cf7edc2d4 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png Binary files differnew file mode 100755 index 0000000000..a0c15a6d3e --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 7ca6820318..0404fdae48 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -162,7 +162,21 @@ with the same filename but different name <texture name="ComboButton_On" file_name="widgets/ComboButton_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_Off" file_name="widgets/ComboButton_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> + <texture name="Container" file_name="containers/Container.png" preload="false" /> + + <texture name="Conv_toolbar_add_person" file_name="icons/Conv_toolbar_add_person.png" preload="false" /> + <texture name="Conv_toolbar_arrow_ne" file_name="icons/Conv_toolbar_arrow_ne.png" preload="false" /> + <texture name="Conv_toolbar_arrow_sw" file_name="icons/Conv_toolbar_arrow_sw.png" preload="false" /> + <texture name="Conv_toolbar_call_log" file_name="icons/Conv_toolbar_call_log.png" preload="false" /> + <texture name="Conv_toolbar_close" file_name="icons/Conv_toolbar_close.png" preload="false" /> + <texture name="Conv_toolbar_collapse" file_name="icons/Conv_toolbar_collapse.png" preload="false" /> + <texture name="Conv_toolbar_expand" file_name="icons/Conv_toolbar_expand.png" preload="false" /> + <texture name="Conv_toolbar_hang_up" file_name="icons/Conv_toolbar_hang_up.png" preload="false" /> + <texture name="Conv_toolbar_open_call" file_name="icons/Conv_toolbar_open_call.png" preload="false" /> + <texture name="Conv_toolbar_plus" file_name="icons/Conv_toolbar_plus.png" preload="false" /> + <texture name="Conv_toolbar_sort" file_name="icons/Conv_toolbar_sort.png" preload="false" /> + <texture name="Copy" file_name="icons/Copy.png" preload="false" /> <texture name="DisclosureArrow_Opened_Off" file_name="widgets/DisclosureArrow_Opened_Off.png" preload="true" /> diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml deleted file mode 100644 index 405557242f..0000000000 --- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater - positioning="specified" - left="10" - bottom="-10" - height="60" - layout="topleft" - legacy_header_height="25" - single_instance="true" - title="NEARBY CHAT" - save_rect="true" - save_visibility="true" - can_close="true" - can_minimize="true" - help_topic="chat_bar" - min_height="60" - min_width="150" - can_resize="true" - default_tab_group="1" - name="chat_bar" - width="300"> - <panel - top="20" - class="panel_nearby_chat" - follow="all" - width="300" - height="0" - visible="false" - filename="panel_nearby_chat.xml" - name="nearby_chat" /> - <panel width="300" - height="31" - left="0" - name="bottom_panel" - bottom="-1" - follows="left|right|bottom" - tab_group="1"> - <line_editor - border_style="line" - border_thickness="1" - follows="left|right" - height="23" - label="Click here to chat." - layout="topleft" - left_delta="7" - left="0" - max_length_bytes="1023" - name="chat_box" - spellcheck="true" - text_pad_left="5" - text_pad_right="25" - tool_tip="Press Enter to say, Ctrl+Enter to shout" - top="2" - width="255" /> - <output_monitor - auto_update="true" - follows="right" - draw_border="false" - height="16" - layout="topleft" - left_pad="-24" - mouse_opaque="true" - name="chat_zone_indicator" - top="6" - visible="true" - width="20" /> - <button - follows="right" - is_toggle="true" - width="20" - top="2" - layout="topleft" - left_pad="12" - image_disabled="ComboButton_UpOff" - image_unselected="ComboButton_UpOff" - image_selected="ComboButton_On" - image_pressed="ComboButton_UpSelected" - image_pressed_selected="ComboButton_Selected" - height="23" - chrome="true" - name="show_nearby_chat" - tool_tip="Shows/hides nearby chat log"> - </button> - </panel> -</floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index e123de46c2..e5ef80e352 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,49 +1,122 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <multi_floater - can_close="false" + can_close="true" can_minimize="true" can_resize="true" - height="390" + height="430" layout="topleft" + min_height="50" name="floater_im_box" help_topic="floater_im_box" save_rect="true" save_visibility="true" single_instance="true" title="CONVERSATIONS" - width="396"> - <tab_container - follows="left|right|top|bottom" - height="390" + width="680"> + <string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <string + name="expand_icon" + value="Conv_toolbar_expand"/> + <layout_stack + animate="true" + follows="all" + height="430" layout="topleft" - left="1" - name="im_box_tab_container" - tab_position="bottom" - tab_width="64" - tab_max_width = "134" - tab_height="16" - use_custom_icon_ctrl="true" - tab_icon_ctrl_pad="2" - halign="left" - use_ellipses="true" + left="0" + name="conversations_stack" + orientation="horizontal" top="0" - width="394"> - <first_tab - tab_bottom_image_flash="Toolbar_Left_Flash"/> - <middle_tab - tab_bottom_image_flash="Toolbar_Middle_Flash"/> - <last_tab - tab_bottom_image_flash="Toolbar_Right_Flash"/> - </tab_container> - <icon - color="DefaultShadowLight" - enabled="false" - follows="left|right|bottom" - height="17" - image_name="tabarea.tga" - layout="bottomleft" - left="1" - name="im_box_tab_container_icon" - bottom="10" - width="394" /> + width="680"> + <layout_panel + auto_resize="true" + height="430" + name="conversations_layout_panel" + min_dim="51" + width="268"> + <layout_stack + animate="false" + follows="left|top|right" + height="35" + layout="topleft" + left="0" + name="conversations_pane_buttons_stack" + orientation="horizontal" + top="0" + width="268"> + <layout_panel + auto_resize="true" + height="35" + name="conversations_pane_buttons_expanded"> + <menu_button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left="10" + name="sort_btn" + top="5" + width="31" /> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_plus" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + tool_tip="Add button on the left panel" + width="31"/> + </layout_panel> + <layout_panel + auto_resize="false" + height="35" + name="conversations_pane_buttons_collapsed" + width="41"> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="5" + name="expand_collapse_btn" + width="31" /> + </layout_panel> + </layout_stack> + <panel + follows="all" + layout="topleft" + name="conversations_list_panel" + opaque="true" + top_pad="0" + left="5" + height="390" + width="263"/> + </layout_panel> + <layout_panel + auto_resize="true" + height="430" + name="messages_layout_panel" + width="412"> + <panel_container + follows="all" + height="430" + layout="topleft" + left="10" + name="im_box_tab_container" + top="0" + width="402"/> + </layout_panel> + </layout_stack> </multi_floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 040b66623e..09c1510004 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater - legacy_header_height="18" background_visible="true" default_tab_group="1" height="355" @@ -10,84 +9,251 @@ can_dock="false" can_minimize="true" can_close="true" + save_rect="true" visible="false" width="394" can_resize="true" + can_tear_off="false" min_width="250" min_height="190"> + <floater.string + name="NearbyChatTitle" + value="Nearby Chat"/> + <floater.string name="call_btn_start">Conv_toolbar_open_call</floater.string> + <floater.string name="call_btn_stop">Conv_toolbar_hang_up</floater.string> + <floater.string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <floater.string + name="expand_icon" + value="Conv_toolbar_expand"/> + <floater.string + name="tear_off_icon" + value="Conv_toolbar_arrow_ne"/> + <floater.string + name="return_icon" + value="Conv_toolbar_arrow_sw"/> + <view + follows="all" + layout="topleft" + name="contents_view" + top="0" + left="0" + height="355" + width="394"> + <panel + follows="left|top|right" + layout="topleft" + name="toolbar_panel" + top="0" + left="0" + height="35" + width="394"> + <menu_button + menu_filename="menu_im_session_showmodes.xml" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left="5" + name="view_options_btn" + top="5" + width="31" /> + <button + enabled="false" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_add_person" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + width="31"/> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_open_call" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="voice_call_btn" + width="31"/> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_close" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="283" + name="close_btn" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="expand_collapse_btn" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_arrow_ne" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="tear_off_btn" + width="31" /> + </panel> <layout_stack animate="true" default_tab_group="2" follows="all" - height="320" + height="310" width="394" layout="topleft" orientation="horizontal" name="im_panels" tab_group="1" - top="20" + top_pad="0" left="0"> <layout_panel - name="im_control_panel_holder" + name="speakers_list_panel" + follows="all" min_width="115" width="150" - height="320" + height="310" auto_resize="false"> - <panel - name="panel_im_control_panel" + <avatar_list + color="DkGray2" + follows="all" + height="310" + ignore_online_status="true" layout="topleft" - height="320" - width="150" - follows="all"/> + name="speakers_list" + opaque="false" + show_info_btn="true" + show_profile_btn="false" + show_speaking_indicator="false" + width="150" /> </layout_panel> <layout_panel default_tab_group="3" left="0" tab_group="2" + follows="all" top="0" - height="200" - width="244" - user_resize="true"> - <button - height="20" - follows="left|top" - top="0" - left="2" - image_overlay="TabIcon_Open_Off" + height="310" + width="244" + layout="topleft" + user_resize="true" + auto_resize="true" + visible="true" + name="left_part_holder"> + <panel + name="trnsAndChat_panel" + follows="all" + layout="topleft" + visible="true" + height="275" + width="244"> + <layout_stack + animate="true" + default_tab_group="2" + follows="all" + height="275" + width="244" layout="topleft" - width="25" - name="slide_left_btn" /> - <button - height="20" - follows="left|top" + visible="true" + orientation="vertical" + name="translate_and_chat_stack" + tab_group="1" + left_pad="0" top="0" - left="2" - image_overlay="TabIcon_Close_Off" - width="25" - name="slide_right_btn" /> + left="0"> + <layout_panel + auto_resize="false" + height="26" + layout="topleft" + left_delta="0" + name="translate_chat_checkbox_lp" + top_delta="0" + visible="true" + width="230"> + <check_box + top="10" + control_name="TranslateChat" + enabled="true" + height="16" + label="Translate chat" + layout="topleft" + left="5" + name="translate_chat_checkbox" + width="230" /> + </layout_panel> + <layout_panel + height="248" + width="234" + layout="topleft" + follows="all" + left_delta="0" + top_delta="0" + bottom="0" + visible="true" + user_resize="true" + auto_resize="true" + name="chat_holder"> <chat_history font="SansSerifSmall" - follows="left|right|top|bottom" - height="150" + follows="all" + visible="true" + height="240" name="chat_history" parse_highlights="true" parse_urls="true" left="1" - width="238"> + width="229"> </chat_history> - <line_editor + </layout_panel> + </layout_stack> + </panel> + <chat_editor bottom="0" - left="3" + expand_lines_count="5" follows="left|right|bottom" font="SansSerifSmall" + visible="true" height="20" + is_expandable="true" label="To" layout="bottomleft" name="chat_editor" + max_length="1023" spellcheck="true" tab_group="3" - width="236"> - </line_editor> + width="240" + wrap="true"> + </chat_editor> </layout_panel> </layout_stack> + </view> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_people.xml b/indra/newview/skins/default/xui/en/floater_people.xml index 08d0b00a83..8e143623ab 100644 --- a/indra/newview/skins/default/xui/en/floater_people.xml +++ b/indra/newview/skins/default/xui/en/floater_people.xml @@ -7,20 +7,20 @@ height="570" help_topic="sidebar_people" min_height="440" - min_width="333" + min_width="390" layout="topleft" name="floater_people" save_rect="true" single_instance="true" reuse_instance="true" title="PEOPLE" - width="333"> + width="390"> <panel_container default_panel_name="panel_people" follows="all" height="570" name="main_panel" - width="333"> + width="390"> <panel class="panel_people" name="panel_people" @@ -31,11 +31,5 @@ filename="panel_group_info_sidetray.xml" label="Group Profile" font="SansSerifBold"/> - <panel - class="panel_block_list_sidetray" - name="panel_block_list_sidetray" - filename="panel_block_list_sidetray.xml" - label="Blocked Residents & Objects" - font="SansSerifBold"/> </panel_container> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_voice_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_volume.xml new file mode 100644 index 0000000000..9346295d5b --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_volume.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + Not can_close / no title to avoid window chrome + Single instance - only have one at a time, recycle it each spawn +--> +<floater + legacy_header_height="25" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="90" + layout="topleft" + name="floater_voice_volume" + single_instance="true" + sound_flags="0" + title="VOICE VOLUME" + visible="true" + width="245"> + <text + follows="top|left|right" + font="SansSerifSmall" + height="21" + left="10" + name="avatar_name" + parse_urls="false" + top="35" + text_color="White" + translate="false" + use_ellipses="true" + value="TestString PleaseIgnore" + width="225" /> + <slider + follows="top|left" + height="23" + increment="0.01" + left="1" + max_val="0.95" + min_val="0.05" + name="volume_slider" + show_text="false" + tool_tip="Voice volume" + top_pad="0" + value="0.5" + width="200" /> + <button + follows="top|left" + height="16" + image_disabled="Audio_Off" + image_disabled_selected="AudioMute_Off" + image_hover_selected="AudioMute_Over" + image_selected="AudioMute_Off" + image_unselected="Audio_Off" + is_toggle="true" + left_pad="0" + top_delta="4" + name="mute_btn" + width="16" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/inspect_avatar.xml b/indra/newview/skins/default/xui/en/inspect_avatar.xml index bc3bcd331b..c3481e6d4c 100644 --- a/indra/newview/skins/default/xui/en/inspect_avatar.xml +++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml @@ -9,7 +9,7 @@ bg_opaque_image="Inspector_Background" can_close="false" can_minimize="false" - height="164" + height="130" layout="topleft" name="inspect_avatar" single_instance="true" @@ -94,32 +94,17 @@ use_ellipses="true" width="220">This is my second life description and I really think it is great. But for some reason my description is super extra long because I like to talk a whole lot </text> - <slider - follows="top|left" - height="23" - increment="0.01" - left="1" - max_val="0.95" - min_val="0.05" - name="volume_slider" - show_text="false" - tool_tip="Voice volume" - top_pad="0" - value="0.5" - width="200" /> - <button + <text follows="top|left" height="16" - image_disabled="Audio_Off" - image_disabled_selected="AudioMute_Off" - image_hover_selected="AudioMute_Over" - image_selected="AudioMute_Off" - image_unselected="Audio_Off" - is_toggle="true" - left_pad="0" - top_delta="4" - name="mute_btn" - width="16" /> + left="8" + name="avatar_profile_link" + font="SansSerifSmall" + text_color="White" + top_pad="5" + translate="false" + value="[[LINK] View full profile]" + width="175" /> <avatar_icon follows="top|left" height="38" @@ -130,83 +115,4 @@ name="avatar_icon" top="10" width="38" /> -<!-- Overlapping buttons for default actions - llinspectavatar.cpp makes visible the most likely default action ---> - <button - follows="top|left" - height="20" - label="Add Friend" - left="8" - top="135" - name="add_friend_btn" - width="90" /> - <button - follows="top|left" - height="20" - label="IM" - left_delta="0" - top_delta="0" - name="im_btn" - width="80" - commit_callback.function="InspectAvatar.IM"/> - <button - follows="top|left" - height="20" - label="Profile" - layout="topleft" - name="view_profile_btn" - left_delta="96" - top_delta="0" - tab_stop="false" - width="80" /> - <!-- gear buttons here --> - <menu_button - follows="top|left" - height="20" - layout="topleft" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_avatar_gear.xml" - name="gear_btn" - right="-5" - top_delta="0" - width="35" /> - <menu_button - follows="top|left" - height="20" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_self_gear.xml" - name="gear_self_btn" - right="-5" - top_delta="0" - width="35" /> - <panel - follows="top|left" - top="164" - left="0" - height="60" - width="228" - visible="false" - background_visible="true" - name="moderator_panel" - background_opaque="true" - bg_opaque_color="MouseGray"> - <button - name="disable_voice" - label="Disable Voice" - top="20" - width="95" - height="20" - left="10" - commit_callback.function="InspectAvatar.DisableVoice"/> - <button - name="enable_voice" - label="Enable Voice" - top="20" - width="95" - height="20" - left="10" - visible="false" - commit_callback.function="InspectAvatar.EnableVoice"/> - </panel> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_group_plus.xml b/indra/newview/skins/default/xui/en/menu_group_plus.xml index fce7414d80..eca9e7f3c9 100644 --- a/indra/newview/skins/default/xui/en/menu_group_plus.xml +++ b/indra/newview/skins/default/xui/en/menu_group_plus.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false"> <menu_item_call name="item_join" label="Join Group..."> @@ -8,4 +8,4 @@ <menu_item_call name="item_new" label="New Group..."> <menu_item_call.on_click function="People.Group.Plus.Action" userdata="new_group" /> </menu_item_call> -</menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml new file mode 100644 index 0000000000..483f24afd0 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_modes" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Compact view" + name="compact_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="compact_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="compact_view"/> + </menu_item_check> + <menu_item_check + label="Expanded view" + name="expanded_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="expanded_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="expanded_view"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="IMShowTime" label="Show time"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowTime" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowTime" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowTime" /> + </menu_item_check> + <menu_item_check name="IMShowNamesForP2PConv" label="Show names in one-to-one conversations"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowNamesForP2PConv" /> + + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml deleted file mode 100644 index 76b188220d..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<toggleable_menu - create_jump_keys="true" - layout="topleft" - mouse_opaque="false" - visible="false" - name="Gear Menu"> - <menu_item_call - label="View Profile" - enabled="true" - name="view_profile"> - <menu_item_call.on_click - function="InspectAvatar.ViewProfile"/> - </menu_item_call> - <menu_item_call - label="Add Friend" - name="add_friend"> - <menu_item_call.on_click - function="InspectAvatar.AddFriend"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.Enable"/> - </menu_item_call> - <menu_item_call - label="IM" - name="im"> - <menu_item_call.on_click - function="InspectAvatar.IM"/> - </menu_item_call> - <menu_item_call - label="Call" - enabled="true" - name="call"> - <menu_item_call.on_click - function="InspectAvatar.Call"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableCall"/> - </menu_item_call> - <menu_item_call - label="Teleport" - name="teleport"> - <menu_item_call.on_click - function="InspectAvatar.Teleport"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableTeleportOffer"/> - </menu_item_call> - <menu_item_call - label="Invite to Group" - name="invite_to_group"> - <menu_item_call.on_click - function="InspectAvatar.InviteToGroup"/> - </menu_item_call> - <menu_item_separator /> - <menu_item_call - label="Block" - name="block"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableMute" /> - </menu_item_call> - <menu_item_call - label="Unblock" - name="unblock"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableUnmute" /> - </menu_item_call> - <menu_item_call - label="Report" - name="report"> - <menu_item_call.on_click - function="InspectAvatar.Report"/> - </menu_item_call> - <menu_item_call - label="Freeze" - name="freeze"> - <menu_item_call.on_click - function="InspectAvatar.Freeze"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFreeze"/> - </menu_item_call> - <menu_item_call - label="Eject" - name="eject"> - <menu_item_call.on_click - function="InspectAvatar.Eject"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleEject"/> - </menu_item_call> - <menu_item_call - label="Kick" - name="kick"> - <menu_item_call.on_click - function="InspectAvatar.Kick"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod"/> - </menu_item_call> - <menu_item_call - label="CSR" - name="csr"> - <menu_item_call.on_click - function="InspectAvatar.CSR" /> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="debug"> - <menu_item_call.on_click - function="Avatar.Debug"/> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> - <menu_item_call - label="Find On Map" - name="find_on_map"> - <menu_item_call.on_click - function="InspectAvatar.FindOnMap"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFindOnMap"/> - </menu_item_call> - <menu_item_call - label="Zoom In" - name="zoom_in"> - <menu_item_call.on_click - function="InspectAvatar.ZoomIn"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleZoomIn"/> - </menu_item_call> - <menu_item_call - label="Pay" - name="pay"> - <menu_item_call.on_click - function="InspectAvatar.Pay"/> - </menu_item_call> - <menu_item_call - label="Share" - name="share"> - <menu_item_call.on_click - function="InspectAvatar.Share"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml deleted file mode 100644 index 5e7b16ed4a..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml +++ /dev/null @@ -1,252 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - layout="topleft" - name="Self Pie"> - <menu_item_call - label="Sit Down" - layout="topleft" - name="Sit Down Here"> - <menu_item_call.on_click - function="Self.SitDown" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableSitDown" /> - </menu_item_call> - <menu_item_call - label="Stand Up" - layout="topleft" - name="Stand Up"> - <menu_item_call.on_click - function="Self.StandUp" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableStandUp" /> - </menu_item_call> - <context_menu - label="Take Off" - layout="topleft" - name="Take Off >"> - <context_menu - label="Clothes" - layout="topleft" - name="Clothes >"> - <menu_item_call - enabled="false" - label="Shirt" - layout="topleft" - name="Shirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Pants" - layout="topleft" - name="Pants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="pants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="pants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Skirt" - layout="topleft" - name="Skirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="skirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="skirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Shoes" - layout="topleft" - name="Shoes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shoes" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shoes" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Socks" - layout="topleft" - name="Socks"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="socks" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="socks" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Jacket" - layout="topleft" - name="Jacket"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="jacket" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="jacket" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Gloves" - layout="topleft" - name="Gloves"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="gloves" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="gloves" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Undershirt" - layout="topleft" - name="Self Undershirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="undershirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="undershirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Underpants" - layout="topleft" - name="Self Underpants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="underpants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="underpants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Tattoo" - layout="topleft" - name="Self Tattoo"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="tattoo" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="tattoo" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Alpha" - layout="topleft" - name="Self Alpha"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="alpha" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="alpha" /> - </menu_item_call> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="All Clothes" - layout="topleft" - name="All Clothes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="all" /> - </menu_item_call> - </context_menu> - <context_menu - label="HUD" - layout="topleft" - name="Object Detach HUD" /> - <context_menu - label="Detach" - layout="topleft" - name="Object Detach" /> - <menu_item_call - label="Detach All" - layout="topleft" - name="Detach All"> - <menu_item_call.on_click - function="Self.RemoveAllAttachments" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableRemoveAllAttachments" /> - </menu_item_call> - </context_menu> - <menu_item_call - label="Change Outfit" - layout="topleft" - name="Chenge Outfit"> - <menu_item_call.on_click - function="CustomizeAvatar" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Outfit" - layout="topleft" - name="Edit Outfit"> - <menu_item_call.on_click - function="EditOutfit" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Shape" - layout="topleft" - name="Edit My Shape"> - <menu_item_call.on_click - function="EditShape" /> - <menu_item_call.on_enable - function="Edit.EnableEditShape" /> - </menu_item_call> - <menu_item_call - label="My Friends" - layout="topleft" - name="Friends..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="friends_panel" /> - </menu_item_call> - <menu_item_call - label="My Groups" - layout="topleft" - name="Groups..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="groups_panel" /> - </menu_item_call> - <menu_item_call - label="My Profile" - layout="topleft" - name="Profile..."> - <menu_item_call.on_click - function="ShowAgentProfile" - parameter="agent" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="Debug..."> - <menu_item_call.on_click - function="Avatar.Debug" /> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml new file mode 100644 index 0000000000..63295ea27b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_gear" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Unblock" + name="unblock"> + <on_click + function="Block.Action" + parameter="unblock_item" /> + <on_enable + function="Block.Enable" + parameter="unblock_item" /> + </menu_item_call> + <menu_item_call + label="Profile..." + name="profile"> + <on_click + function="Block.Action" + parameter="profile_item"/> + <on_enable + function="Block.Enable" + parameter="profile_item" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml new file mode 100644 index 0000000000..0c7155667e --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Block Resident by name..." + name="block_resident_by_name"> + <on_click + function="Block.Action" + parameter="block_res_by_name"/> + </menu_item_call> + <menu_item_call + label="Block object by name" + name="block_object_by_name"> + <on_click + function="Block.Action" + parameter="block_obj_by_name"/> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml new file mode 100644 index 0000000000..2efb70ee37 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_view" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by name" + name="sort_by_name"> + <on_click + function="Block.Action" + parameter="sort_by_name"/> + <on_check + function="Block.Check" + parameter="sort_by_name"/> + </menu_item_check> + <menu_item_check + label="Sort by type" + name="sort_by_type"> + <on_click + function="Block.Action" + parameter="sort_by_type" /> + <on_check + function="Block.Check" + parameter="sort_by_type" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml index b452f96e7a..eab7b8c085 100644 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml @@ -40,8 +40,4 @@ function="CheckControl" parameter="FriendsListShowPermissions" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Friends.ViewSort.Action" parameter="panel_block_list_sidetray" /> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups.xml b/indra/newview/skins/default/xui/en/menu_people_groups.xml index 8f89d37dbb..1e0364b84e 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups.xml @@ -1,8 +1,18 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false" opaque="true" color="MenuDefaultBgColor"> <menu_item_call + label="Activate" + name="Activate"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="activate" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="activate" /> + </menu_item_call> + <menu_item_call label="View Info" name="View Info"> <menu_item_call.on_click @@ -23,7 +33,7 @@ parameter="chat" /> </menu_item_call> <menu_item_call - label="Call" + label="Voice call" name="Call"> <menu_item_call.on_click function="People.Groups.Action" @@ -34,17 +44,6 @@ </menu_item_call> <menu_item_separator /> <menu_item_call - label="Activate" - name="Activate"> - <menu_item_call.on_click - function="People.Groups.Action" - parameter="activate" /> - <menu_item_call.on_enable - function="People.Groups.Enable" - parameter="activate" /> - </menu_item_call> - <menu_item_separator /> - <menu_item_call label="Leave" name="Leave"> <menu_item_call.on_click @@ -54,4 +53,4 @@ function="People.Groups.Enable" parameter="leave" /> </menu_item_call> -</menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_groups_view.xml index c710fe3b9b..73f79f1e70 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups_view.xml @@ -14,13 +14,4 @@ function="CheckControl" parameter="GroupListShowIcons" /> </menu_item_check> - <menu_item_call - label="Leave Selected Group" - layout="topleft" - name="Leave Selected Group"> - <menu_item_call.on_click - function="People.Group.Minus.Action"/> - <menu_item_call.on_enable - function="People.Group.Minus.Enable"/> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml index d2e35e4cc0..b7c9ab1fe3 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -10,12 +10,39 @@ function="Avatar.Profile" /> </menu_item_call> <menu_item_call + label="IM" + layout="topleft" + name="IM"> + <menu_item_call.on_click + function="Avatar.IM" /> + </menu_item_call> + <menu_item_call + label="Offer Teleport" + name="teleport"> + <menu_item_call.on_click + function="Avatar.OfferTeleport"/> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call + label="Voice call" + layout="topleft" + name="Call"> + <menu_item_call.on_click + function="Avatar.Call" /> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_call" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call label="Add Friend" layout="topleft" name="Add Friend"> <menu_item_call.on_click function="Avatar.AddFriend" /> - <menu_item_call.on_enable + <menu_item_call.on_visible function="Avatar.EnableItem" parameter="can_add" /> </menu_item_call> @@ -30,22 +57,13 @@ parameter="can_delete" /> </menu_item_call> <menu_item_call - label="IM" + label="Invite to group..." layout="topleft" - name="IM"> + name="Invite"> <menu_item_call.on_click - function="Avatar.IM" /> - </menu_item_call> - <menu_item_call - label="Call" - layout="topleft" - name="Call"> - <menu_item_call.on_click - function="Avatar.Call" /> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_call" /> + function="Avatar.InviteToGroup" /> </menu_item_call> + <menu_item_separator /> <menu_item_call label="Map" layout="topleft" @@ -83,13 +101,5 @@ function="Avatar.EnableItem" parameter="can_block" /> </menu_item_check> - <menu_item_call - label="Offer Teleport" - name="teleport"> - <menu_item_call.on_click - function="Avatar.OfferTeleport"/> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_offer_teleport"/> - </menu_item_call> + </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml new file mode 100644 index 0000000000..da88ca9f4d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by Recent Speakers" + name="sort_by_recent_speakers"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_by_recent_speakers"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_by_recent_speakers"/> + </menu_item_check> + <menu_item_check + label="Sort by Name" + name="sort_name"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_name"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_name"/> + </menu_item_check> + <menu_item_check + label="Sort by Distance" + name="sort_distance"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_distance"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_distance"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowIcons" /> + </menu_item_check> + <menu_item_check name ="view_map" label="View Map"> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowMap" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="NearbyListShowMap" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml deleted file mode 100644 index 614dd693c5..0000000000 --- a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - name="menu_group_plus" - left="0" bottom="0" visible="false" - mouse_opaque="false"> - <menu_item_check - label="Sort by Recent Speakers" - name="sort_by_recent_speakers"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_by_recent_speakers"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_by_recent_speakers"/> - </menu_item_check> - <menu_item_check - label="Sort by Name" - name="sort_name"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_name"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_name"/> - </menu_item_check> - <menu_item_check - label="Sort by Distance" - name="sort_distance"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_distance"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_distance"/> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_check name="view_icons" label="View People Icons"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="view_icons" /> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowIcons" /> - </menu_item_check> - <menu_item_check name ="view_map" label="View Map"> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowMap" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="NearbyListShowMap" /> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view.xml index 485a5a658c..1dbc90dd2b 100644 --- a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view.xml @@ -32,8 +32,4 @@ function="CheckControl" parameter="RecentListShowIcons" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 91b4d38e97..3d057fa8f5 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3010,6 +3010,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="GrantedModifyRights" persist="true" + log_to_im="true" type="notify"> [NAME] has given you permission to edit their objects. </notification> @@ -3018,6 +3019,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="RevokedModifyRights" persist="true" + log_to_im="true" type="notify"> Your privilege to modify [NAME]'s objects has been revoked </notification> @@ -5426,6 +5428,8 @@ The string [STRING_NAME] is missing from strings.xml <notification icon="notifytip.tga" name="IMSystemMessageTip" + log_to_im="true" + log_to_chat="false" type="notifytip"> [MESSAGE] </notification> @@ -5469,18 +5473,14 @@ Topic: [SUBJECT], Message: [MESSAGE] <notification icon="notifytip.tga" - name="FriendOnline" + name="FriendOnlineOffline" + log_to_chat="false" type="notifytip"> <tag>friendship</tag> -<nolink>[NAME]</nolink> is Online - </notification> - - <notification - icon="notifytip.tga" - name="FriendOffline" - type="notifytip"> - <tag>friendship</tag> -<nolink>[NAME]</nolink> is Offline +<nolink>[NAME]</nolink> is [STATUS] + <unique combine="cancel_old"> + <context>NAME</context> + </unique> </notification> <notification @@ -5724,6 +5724,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryAccepted" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] received your inventory offer. </notification> @@ -5731,6 +5733,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryDeclined" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] declined your inventory offer. </notification> @@ -5812,6 +5816,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentReceived" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -5821,6 +5826,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentSent" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -5965,6 +5971,7 @@ The objects on the selected parcel that are NOT owned by you have been returned <notification icon="notify.tga" name="ServerObjectMessage" + log_to_im="true" persist="true" type="notify"> Message from [NAME]: @@ -6333,6 +6340,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="UserGiveItem" + log_to_im ="true" type="offer"> [NAME_SLURL] has given you this [OBJECTTYPE]: [ITEM_SLURL] @@ -6388,6 +6396,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="TeleportOffered" + log_to_im="true" type="offer"> [NAME_SLURL] has offered to teleport you to their location: @@ -6445,6 +6454,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="TeleportOfferSent" + log_to_im="true" + show_toast="false" type="offer"> Teleport offer sent to [TO_NAME] </notification> @@ -6472,6 +6483,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="OfferFriendship" + log_to_im="true" type="offer"> <tag>friendship</tag> <tag>confirm</tag> @@ -6495,6 +6507,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipOffered" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> You have offered friendship to [TO_NAME] @@ -6524,6 +6538,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAccepted" + log_to_im="true" type="offer"> <tag>friendship</tag> <nolink>[NAME]</nolink> accepted your friendship offer. @@ -6532,6 +6547,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipDeclined" + log_to_im="true" persist="true" type="notify"> <tag>friendship</tag> @@ -6541,6 +6557,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAcceptedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer accepted. @@ -6549,6 +6567,8 @@ Friendship offer accepted. <notification icon="notify.tga" name="FriendshipDeclinedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer declined. diff --git a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml deleted file mode 100644 index d68fa6ca6c..0000000000 --- a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="215" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="215" - layout="topleft" - left="3" - name="vertical_stack" - orientation="vertical" - top="0" - width="147"> - <layout_panel - auto_resize="true" - follows="top|left" - height="130" - layout="topleft" - left="0" - min_height="0" - mouse_opaque="false" - width="147" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="130" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="147" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Call" - name="call_btn" - width="130" - top="0" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Leave Call" - name="end_call_btn" - top="0"/> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Voice Controls" - name="voice_ctrls_btn" - top="0" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml index 7c67fd7f83..24f7d44cce 100644 --- a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml @@ -4,88 +4,95 @@ follows="left|top|right|bottom" height="305" layout="topleft" + left="0" name="block_list_panel" help_topic="blocked_list" min_height="350" min_width="240" - width="280"> - <button - follows="top|left" - height="24" - image_hover_unselected="BackButton_Over" - image_pressed="BackButton_Press" - image_unselected="BackButton_Off" - layout="topleft" - name="back" - left="4" - tab_stop="false" - top="1" - width="30"/> - <text - follows="top|left|right" - font="SansSerifLargeBold" - height="20" - layout="topleft" - left_pad="10" - name="title_text" - text_color="White" - top="5" - width="250"> - Block List - </text> - <scroll_list + width="323"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="blocked_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter" + max_length_chars="300" + name="blocked_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + menu_filename="menu_people_blocked_gear.xml" + menu_position="bottomleft" + name="blocked_gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_view.xml" + menu_position="bottomleft" + name="view_btn" + top_delta="0" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_plus.xml" + menu_position="bottomleft" + name="plus_btn" + top_delta="0" + width="31"/> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="unblock_btn" + top_delta="0" + width="31"/> + </panel> + <block_list follows="all" - height="190" + height="273" layout="topleft" - left="5" + left="3" name="blocked" tool_tip="List of currently blocked Residents" - top="30" - width="270"> - <scroll_list.columns - name="item_name" /> - <scroll_list.columns - name="item_type" - width="96" /> - </scroll_list> - <button - follows="left|bottom" - height="23" - label="Block person" - layout="topleft" - left_delta="0" - name="Block resident..." - tool_tip="Pick a Resident to block" - top_pad="4" - width="210"> - <button.commit_callback - function="Block.ClickPick" /> - </button> - <button - follows="left|bottom" - height="23" - label="Block object by name" - layout="topleft" - left_delta="0" - name="Block object by name..." - tool_tip="Pick an object to block by name" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickBlockByName" /> - </button> - <button - enabled="false" - follows="left|bottom" - height="23" - label="Unblock" - layout="topleft" - left_delta="0" - name="Unblock" - tool_tip="Remove Resident or object from blocked list" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickRemove" /> - </button> + top="31" + right="-1"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml new file mode 100644 index 0000000000..84e7e467b1 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="23" + layout="topleft" + left="0" + name="blocked_list_item" + top="0" + width="380"> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <avatar_icon + default_icon_name="Generic_Person" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <group_icon + default_icon_name="Generic_Group" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="16" + image_name="Inv_Object" + layout="topleft" + left="7" + name="object_icon" + top="4" + visible="false" + width="16" /> + <text + follows="left|right" + font="SansSerifSmall" + font.color="DkGray" + height="15" + layout="topleft" + left_pad="5" + name="item_name" + parse_urls="false" + top="6" + use_ellipses="true" + width="180" /> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml deleted file mode 100644 index ad10e53a4e..0000000000 --- a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="238" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="238" - layout="topleft" - left="5" - name="vertical_stack" - orientation="vertical" - top="0" - width="145"> - <layout_panel - auto_resize="true" - follows="top|left" - height="100" - layout="topleft" - min_height="0" - mouse_opaque="false" - width="145" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="100" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="145" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="group_info_btn_panel"> - <button - follows="left|right|bottom" - height="23" - label="Group Profile" - name="group_info_btn" - use_ellipses="true" - top="5" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="call_btn_panel"> - <button - follows="all" - height="23" - label="Call Group" - name="call_btn" - use_ellipses="true" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Leave Call" - name="end_call_btn" - use_ellipses="true" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Open Voice Controls" - name="voice_ctrls_btn" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml deleted file mode 100644 index 8fcd6ccbaf..0000000000 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - height="300" - name="panel_im_control_panel" - width="150"> - <avatar_icon - follows="left|top" - height="105" - left_delta="20" - name="avatar_icon" - top="-5" - width="114"/> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="183" - layout="topleft" - left="5" - name="button_stack" - orientation="vertical" - top_pad="5" - width="145"> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="20" - layout="topleft" - left="2" - min_height="20" - width="140" - name="view_profile_btn_panel" - top="0" > - <button - follows="left|top|right" - height="23" - label="Profile" - name="view_profile_btn" - top="0" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="add_friend_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Add Friend" - name="add_friend_btn" - top="5" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="teleport_btn_panel"> - <button - auto_resize="false" - follows="left|top|right" - height="23" - label="Teleport" - name="teleport_btn" - tool_tip = "Offer to teleport this person" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="share_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Share" - name="share_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="pay_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Pay" - name="pay_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="call_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Call" - name="call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="end_call_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="End Call" - name="end_call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="Voice Controls" - name="voice_ctrls_btn" - width="140" /> - </layout_panel> - <layout_panel - mouse_opaque="false" - auto_resize="true" - follows="top|left" - height="0" - layout="topleft" - min_height="0" - width="140" - name="spacer"/> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml index d683116eb8..4de56b424e 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml @@ -1,20 +1,22 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel follows="all" - height="300" + top="0" + bottom_delta="10" help_topic="nearby_chat" layout="topleft" name="nearby_chat" - width="320"> + width="242" + height="169"> <layout_stack follows="all" - height="295" + height="164" layout="topleft" left="0" name="stack" top="5" orientation="vertical" - width="320"> + width="242"> <layout_panel auto_resize="false" height="26" @@ -23,7 +25,7 @@ name="translate_chat_checkbox_lp" top_delta="0" visible="true" - width="313"> + width="230"> <check_box top="10" control_name="TranslateChat" @@ -33,15 +35,15 @@ layout="topleft" left="5" name="translate_chat_checkbox" - width="300" /> + width="230" /> </layout_panel> <layout_panel auto_resize="true" - height="277" + height="138" left_delta="0" layout="topleft" name="chat_history_lp" - width="318"> + width="242"> <chat_history bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" @@ -49,7 +51,7 @@ layout="topleft" left="5" left_widget_pad="0" - height="272" + height="138" name="chat_history" parse_highlights="true" parse_urls="true" @@ -57,7 +59,7 @@ text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" top="0" - width="313" /> + width="237" /> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 98c7c49ff4..09156b41b5 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -38,12 +38,6 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M name="no_filtered_friends_msg"> Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]. </string> - <string - name="people_filter_label" - value="Filter People" /> - <string - name="groups_filter_label" - value="Filter Groups" /> <!-- *WORKAROUND: for group_list.no_items_msg & group_list.no_filtered_items_msg attributes. They are not defined as translatable in VLT. See EXT-5931 @@ -60,21 +54,9 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M <string name="AltMiniMapToolTipMsg" value="[REGION](Double-click to teleport, shift-drag to pan)"/> - <filter_editor - follows="left|top|right" - height="23" - layout="topleft" - left="10" - label="Filter" - max_length_chars="300" - name="filter_input" - text_color="Black" - text_pad_left="10" - top="3" - width="303" /> <tab_container + bottom="-10" follows="all" - height="383" layout="topleft" left="3" name="tabs" @@ -82,31 +64,116 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M tab_min_width="70" tab_height="30" tab_position="top" - top_pad="10" + top="0" halign="center" - width="319"> - <panel + right="-5"> + +<!-- ================================= NEARBY tab =========================== --> + + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="NEARBY" layout="topleft" left="0" help_topic="people_nearby_tab" name="nearby_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="nearby_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="nearby_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="178" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="7" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_nearby_view.xml" + menu_position="bottomleft" + name="nearby_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="add_friend_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="nearby_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <layout_stack clip="false" follows="all" - height="355" + height="410" layout="topleft" + left="0" mouse_opaque="false" orientation="vertical" - width="313"> + right="-1" + top_pad="0"> <layout_panel height="142" layout="topleft" @@ -123,16 +190,16 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="3" mouse_opaque="false" name="Net Map" - top="4" - width="305"/> + right="-1" + top="4" /> </layout_panel> <layout_panel height="213" layout="topleft" min_dim="100" mouse_opaque="false" - user_resize="true" - width="313"> + right="-1" + user_resize="true"> <avatar_list allow_select="true" follows="all" @@ -143,84 +210,118 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M keep_one_selected="false" multi_select="true" name="avatar_list" - top="2" - width="306" /> + right="-1" + top="2" /> </layout_panel> </layout_stack> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="nearby_view_sort_btn" - tool_tip="Options" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" - width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="243" - /> - </panel> </panel> + +<!-- ================================= FRIENDS tab ========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY FRIENDS" layout="topleft" left="0" help_topic="people_friends_tab" name="friends_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="friends_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="friends_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_friends_view.xml" + menu_position="bottomleft" + name="friends_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="friends_add_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriendWizard" /> + </button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="friends_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <accordion background_visible="true" bg_alpha_color="DkGray2" bg_opaque_color="DkGray2" follows="all" - height="356" + height="408" layout="topleft" left="3" name="friends_accordion" - top="0" - width="307"> + right="-2" + top_pad="2"> <accordion_tab layout="topleft" height="172" @@ -257,247 +358,129 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M width="307" /> </accordion_tab> </accordion> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - - <layout_stack - animate="false" - border_size="0" - follows="left|right|bottom" - height="25" - layout="topleft" - orientation="horizontal" - top_pad="1" - left="0" - name="bottom_panel" - width="308"> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="options_gear_btn_panel" - width="32"> - <menu_button - follows="bottom|left" - tool_tip="Show additional options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="add_btn_panel" - width="32"> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left="0" - name="add_btn" - tool_tip="Offer friendship to a Resident" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="true" - height="25" - layout="topleft" - name="dummy_panel" - width="210"> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left="0" - top="0" - name="dummy_icon" - width="210" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="trash_btn_panel" - width="31"> - <dnd_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - left="0" - layout="topleft" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - top="0" - width="31"/> - </layout_panel> - </layout_stack><!-- - - <button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_btn" - tool_tip="Offer friendship to a Resident" - width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="209" - /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - width="31" /> - --></panel> <text follows="all" height="450" left="13" name="no_friends_help_text" - top="10" - width="293" + right="-13" + top="37" wrap="true" /> </panel> + +<!-- ================================= GROUPS tab =========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY GROUPS" layout="topleft" left="0" help_topic="people_groups_tab" name="groups_panel" - top="0" - width="313"> + right="-1" + top="0"> <!-- *NOTE: no_groups_msg & group_list attributes are not defined as translatable in VLT. See EXT-5931 Values are set from appropriate strings at the top of file via LLPeoplePanel::postBuild() --> - <group_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - name="group_list" - top="0" - width="307" /> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" left="0" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="3" - name="groups_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + name="groups_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter Groups" + max_length_chars="300" + name="groups_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" + image_overlay="OptionsMenu_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" - name="plus_btn" - tool_tip="Join group/Create new group" + left_pad="8" + name="groups_gear_btn" + top="3" width="31" /> - <button - follows="bottom|left" + <menu_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" - image_overlay="Activate_Checkmark" + image_overlay="Conv_toolbar_sort" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" - name="activate_btn" - tool_tip="Activate selected group" + left_pad="2" + menu_filename="menu_people_groups_view.xml" + menu_position="bottomleft" + name="groups_view_btn" + top_delta="0" width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="212" - /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_group_plus.xml" + menu_position="bottomleft" + name="plus_btn" + top_delta="0" + width="31"> + <validate_callback + function="People.Group.Plus.Validate" /> + </menu_button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="minus_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.Group.Minus" /> + </dnd_button> </panel> + <group_list + allow_select="true" + follows="all" + height="406" + layout="topleft" + left="3" + name="group_list" + right="-2" + top_pad="4" /> </panel> + +<!-- ================================= RECENT tab =========================== --> + <panel background_opaque="true" background_visible="true" @@ -510,265 +493,129 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="0" help_topic="people_recent_tab" name="recent_panel" - top="0" - width="313"> - <avatar_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - multi_select="true" - name="avatar_list" - show_last_interaction_time="true" - top="0" - width="307" /> + right="-1" + top="0"> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - name="recent_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + left="0" + name="recent_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="recent_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_recent_view.xml" + menu_position="bottomleft" + name="recent_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" image_overlay="AddItem_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" + left_pad="2" name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" + top_delta="0" width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="244" - /> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="recent_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> </panel> + <avatar_list + allow_select="true" + follows="all" + height="351" + layout="topleft" + left="3" + multi_select="true" + name="avatar_list" + show_last_interaction_time="true" + right="-2" + top_pad="4" /> </panel> - </tab_container> - <panel - follows="bottom|left|right" - height="23" - layout="topleft" - left="8" - top_pad="4" - name="button_bar" - width="313"> -<!--********************************Profile; IM; Call, Share, Teleport********************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - name="bottom_bar_ls" - left="0" - orientation="horizontal" - top_pad="0" - width="313"> +<!-- ================================= BLOCKED tab ========================== --> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - name="view_profile_btn_lp" - auto_resize="true" - width="68"> - <button - follows="bottom|left|right" - height="23" - label="Profile" - layout="topleft" - left="1" - name="view_profile_btn" - tool_tip="Show picture, groups, and other Residents information" - top="0" - width="67" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="im_btn_lp" - auto_resize="true" - width="41"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="IM" - layout="topleft" - name="im_btn" - tool_tip="Open instant message session" - top="0" - width="40" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="call_btn_lp" - auto_resize="true" - width="52"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Call" - layout="topleft" - name="call_btn" - tool_tip="Call this Resident" - top="0" - width="51" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="share_btn_lp" - auto_resize="true" - width="66"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Share" - layout="topleft" - name="share_btn" - tool_tip="Share an inventory item" - top="0" - width="65" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="teleport_btn_lp" - auto_resize="true" - width="77"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Teleport" - layout="topleft" - name="teleport_btn" - tool_tip="Offer teleport" - top="0" - width="76" /> - </layout_panel> - </layout_stack> - -<!--********************************Group Profile; Group Chat; Group Call buttons************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - mouse_opaque="false" - name="bottom_bar_ls1" - left="0" - orientation="horizontal" - top="0" - width="313"> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - mouse_opaque="false" - name="group_info_btn_lp" - auto_resize="true" - width="108"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Profile" - layout="topleft" - mouse_opaque="false" - name="group_info_btn" - tool_tip="Show group information" - top="0" - width="107" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="chat_btn_lp" - auto_resize="true" - width="101"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Chat" - layout="topleft" - mouse_opaque="false" - name="chat_btn" - tool_tip="Open chat session" - top="0" - width="100" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="group_call_btn_lp" - auto_resize="true" - width="96"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Call" - layout="topleft" - mouse_opaque="false" - name="group_call_btn" - tool_tip="Call this group" - top="0" - width="95" /> - </layout_panel> - </layout_stack> - </panel> + <panel + background_opaque="true" + background_visible="true" + bg_alpha_color="DkGray" + bg_opaque_color="DkGray" + follows="all" + height="383" + label="BLOCKED" + layout="topleft" + left="0" + help_topic="people_blocked_tab" + name="blocked_panel" + right="-1" + top="0"> + <panel + class="panel_block_list_sidetray" + height="383" + name="panel_block_list_sidetray" + filename="panel_block_list_sidetray.xml" + follows="all" + label="Blocked Residents & Objects" + layout="topleft" + left="0" + font="SansSerifBold" + top="0" + right="-1" /> + </panel> + </tab_container> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index 27193a984f..c76a3cfaaf 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -76,15 +76,6 @@ top_pad="5" width="400" /> <check_box - enabled="false" - height="16" - label="Enable plain text IM and chat history" - layout="topleft" - left_delta="0" - name="plain_text_chat_history" - top_pad="5" - width="400" /> - <check_box control_name="UseChatBubbles" follows="left|top" height="16" @@ -95,55 +86,6 @@ name="bubble_text_chat" width="150" /> <text - name="show_ims_in_label" - follows="left|top" - layout="topleft" - left="30" - height="20" - width="170" - top_pad="15"> - Show IMs in: - </text> - <text - name="requires_restart_label" - follows="left|top" - layout="topleft" - top_delta="0" - left="170" - height="20" - width="130" - text_color="White_25"> - (requires restart) - </text> - <radio_group - follows="left|top" - height="30" - left="40" - control_name="ChatWindow" - name="chat_window" - top_pad="0" - tool_tip="Show your Instant Messages in separate floaters, or in one floater with many tabs (Requires restart)" - width="150"> - <radio_item - height="16" - label="Separate Windows" - layout="topleft" - left="0" - name="radio" - value="0" - top="0" - width="150" /> - <radio_item - height="16" - label="Tabs" - layout="topleft" - left_delta="0" - name="radio2" - value="1" - top_pad="5" - width="150" /> - </radio_group> - <text name="disable_toast_label" follows="left|top" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 6dd80dc11a..9ccd6b5cbe 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -404,9 +404,10 @@ Please try logging in again in a minute.</string> <string name="AddAndRemoveJoints">Add and remove joints with other objects</string> <string name="ChangePermissions">Change its permissions</string> <string name="TrackYourCamera">Track your camera</string> - <string name="ControlYourCamera">Control your camera</string> - <string name="TeleportYourAgent">Teleport you</string> - <string name="NotConnected">Not Connected</string> + <string name="ControlYourCamera">Control your camera</string> + <string name="NotConnected">Not Connected</string> + <string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name --> + <string name="TeleportYourAgent">Teleport you</string> <!-- Sim Access labels --> <string name="SIM_ACCESS_PG">General</string> @@ -2448,9 +2449,6 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <string name="GroupMoneyDebits">Debits</string> <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string> - <!-- viewer object --> - <string name="ViewerObjectContents">Contents</string> - <!-- Viewer menu --> <string name="AcquiredItems">Acquired Items</string> <string name="Cancel">Cancel</string> @@ -3245,6 +3243,8 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. <string name="IM_moderator_label">(Moderator)</string> <string name="Saved_message">(Saved [LONG_TIMESTAMP])</string> <string name="IM_unblock_only_groups_friends">To see this message, you must uncheck 'Only friends and groups can call or IM me' in Preferences/Privacy.</string> + <string name="OnlineStatus">Online</string> + <string name="OfflineStatus">Offline</string> <!-- voice calls --> <string name="answered_call">Your call has been answered</string> @@ -3691,6 +3691,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Label">Avatar</string> <string name="Command_Build_Label">Build</string> <string name="Command_Chat_Label">Chat</string> + <string name="Command_Conversations_Label">Conversations</string> <string name="Command_Compass_Label">Compass</string> <string name="Command_Destinations_Label">Destinations</string> <string name="Command_Gestures_Label">Gestures</string> @@ -3717,6 +3718,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Tooltip">Choose a complete avatar</string> <string name="Command_Build_Tooltip">Building objects and reshaping terrain</string> <string name="Command_Chat_Tooltip">Chat with people nearby using text</string> + <string name="Command_Conversations_Tooltip">Converse with everyone</string> <string name="Command_Compass_Tooltip">Compass</string> <string name="Command_Destinations_Tooltip">Destinations of interest</string> <string name="Command_Gestures_Tooltip">Gestures for your avatar</string> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml new file mode 100644 index 0000000000..f9facb593a --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<chat_editor + name="chat_editor" + show_context_menu="true"/> diff --git a/indra/newview/skins/default/xui/en/widgets/text.xml b/indra/newview/skins/default/xui/en/widgets/text.xml index 134f2d7522..2102074674 100644 --- a/indra/newview/skins/default/xui/en/widgets/text.xml +++ b/indra/newview/skins/default/xui/en/widgets/text.xml @@ -9,6 +9,7 @@ h_pad="0" allow_scroll="false" text_readonly_color="LabelTextColor" + text_tentative_color="TextFgTentativeColor" bg_writeable_color="FloaterDefaultBackgroundColor" use_ellipses="false" bg_visible="false" |