diff options
Diffstat (limited to 'indra')
144 files changed, 4847 insertions, 3149 deletions
diff --git a/indra/llcharacter/llhandmotion.h b/indra/llcharacter/llhandmotion.h index 39c61b2154..662800784b 100644 --- a/indra/llcharacter/llhandmotion.h +++ b/indra/llcharacter/llhandmotion.h @@ -124,6 +124,8 @@ public: // called when a motion is deactivated virtual void onDeactivate(); + virtual BOOL canDeprecate() { return FALSE; } + static LLString getHandPoseName(eHandPose pose); static eHandPose getHandPose(LLString posename); diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h index cd8c79a84d..96b2ab169b 100644 --- a/indra/llcommon/lldefs.h +++ b/indra/llcommon/lldefs.h @@ -183,7 +183,15 @@ template <class LLDATATYPE> inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3) { LLDATATYPE r = llmax(d1,d2); - return (r > d3 ? r : d3); + return llmax(r, d3); +} + +template <class LLDATATYPE> +inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3, const LLDATATYPE& d4) +{ + LLDATATYPE r1 = llmax(d1,d2); + LLDATATYPE r2 = llmax(d3,d4); + return llmax(r1, r2); } template <class LLDATATYPE> @@ -200,6 +208,14 @@ inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATA } template <class LLDATATYPE> +inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3, const LLDATATYPE& d4) +{ + LLDATATYPE r1 = llmin(d1,d2); + LLDATATYPE r2 = llmin(d3,d4); + return llmin(r1, r2); +} + +template <class LLDATATYPE> inline LLDATATYPE llclamp(const LLDATATYPE& a, const LLDATATYPE& minval, const LLDATATYPE& maxval) { return llmin(llmax(a, minval), maxval); diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h index 3018cc671b..8ba883a0ee 100644 --- a/indra/llcommon/llevent.h +++ b/indra/llcommon/llevent.h @@ -179,7 +179,7 @@ public: if (mDispatcher.notNull()) mDispatcher->removeListener(listener); } // Notifies the dispatcher of an event being fired. - void fireEvent(LLPointer<LLEvent> event, LLSD filter); + void fireEvent(LLPointer<LLEvent> event, LLSD filter = LLSD()); protected: LLPointer<LLEventDispatcher> mDispatcher; diff --git a/indra/llcommon/llmortician.cpp b/indra/llcommon/llmortician.cpp index cab3a7f973..b3e25104cb 100644 --- a/indra/llcommon/llmortician.cpp +++ b/indra/llcommon/llmortician.cpp @@ -33,20 +33,20 @@ #include <list> -std::list<LLMortician*> gGraveyard; +std::list<LLMortician*> LLMortician::sGraveyard; BOOL LLMortician::sDestroyImmediate = FALSE; LLMortician::~LLMortician() { - gGraveyard.remove(this); + sGraveyard.remove(this); } void LLMortician::updateClass() { - while (!gGraveyard.empty()) + while (!sGraveyard.empty()) { - LLMortician* dead = gGraveyard.front(); + LLMortician* dead = sGraveyard.front(); delete dead; } } @@ -56,7 +56,7 @@ void LLMortician::die() // It is valid to call die() more than once on something that hasn't died yet if (sDestroyImmediate) { - // *NOTE: This is a hack to ensure destruction order on shutdown. + // *NOTE: This is a hack to ensure destruction order on shutdown (relative to non-mortician controlled classes). mIsDead = TRUE; delete this; return; @@ -64,7 +64,7 @@ void LLMortician::die() else if (!mIsDead) { mIsDead = TRUE; - gGraveyard.push_back(this); + sGraveyard.push_back(this); } } diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h index 24a38520e9..606ac0dc39 100644 --- a/indra/llcommon/llmortician.h +++ b/indra/llcommon/llmortician.h @@ -50,6 +50,8 @@ private: static BOOL sDestroyImmediate; BOOL mIsDead; + + static std::list<LLMortician*> sGraveyard; }; #endif diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index a0f337c2de..992c883a7e 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -301,3 +301,123 @@ char* ll_pretty_print_sd(const LLSD& sd) buffer[bufferSize - 1] = '\0'; return buffer; } + +//compares the structure of an LLSD to a template LLSD and stores the +//"valid" values in a 3rd LLSD. Default values are stored in the template +// +//If the llsd to test has a specific key to a map and the values +//are not of the same type, false is returned or if the LLSDs are not +//of the same value. Ordering of arrays matters +//Otherwise, returns true +BOOL compare_llsd_with_template( + const LLSD& llsd_to_test, + const LLSD& template_llsd, + LLSD& resultant_llsd) +{ + if ( + llsd_to_test.isUndefined() && + template_llsd.isDefined() ) + { + resultant_llsd = template_llsd; + return TRUE; + } + else if ( llsd_to_test.type() != template_llsd.type() ) + { + resultant_llsd = LLSD(); + return FALSE; + } + + if ( llsd_to_test.isArray() ) + { + //they are both arrays + //we loop over all the items in the template + //verifying that the to_test has a subset (in the same order) + //any shortcoming in the testing_llsd are just taken + //to be the rest of the template + LLSD data; + LLSD::array_const_iterator test_iter; + LLSD::array_const_iterator template_iter; + + resultant_llsd = LLSD::emptyArray(); + test_iter = llsd_to_test.beginArray(); + + for ( + template_iter = template_llsd.beginArray(); + (template_iter != template_llsd.endArray() && + test_iter != llsd_to_test.endArray()); + ++template_iter) + { + if ( !compare_llsd_with_template( + *test_iter, + *template_iter, + data) ) + { + resultant_llsd = LLSD(); + return FALSE; + } + else + { + resultant_llsd.append(data); + } + + ++test_iter; + } + + //so either the test or the template ended + //we do another loop now to the end of the template + //grabbing the default values + for (; + template_iter != template_llsd.endArray(); + ++template_iter) + { + resultant_llsd.append(*template_iter); + } + } + else if ( llsd_to_test.isMap() ) + { + //now we loop over the keys of the two maps + //any excess is taken from the template + //excess is ignored in the test + LLSD value; + LLSD::map_const_iterator template_iter; + + resultant_llsd = LLSD::emptyMap(); + for ( + template_iter = template_llsd.beginMap(); + template_iter != template_llsd.endMap(); + ++template_iter) + { + if ( llsd_to_test.has(template_iter->first) ) + { + //the test LLSD has the same key + if ( !compare_llsd_with_template( + llsd_to_test[template_iter->first], + template_iter->second, + value) ) + { + resultant_llsd = LLSD(); + return FALSE; + } + else + { + resultant_llsd[template_iter->first] = value; + } + } + else + { + //test llsd doesn't have it...take the + //template as default value + resultant_llsd[template_iter->first] = + template_iter->second; + } + } + } + else + { + //of same type...take the test llsd's value + resultant_llsd = llsd_to_test; + } + + + return TRUE; +} diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 699d5093e1..17a881d9cb 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -91,4 +91,14 @@ char* ll_print_sd(const LLSD& sd); // Serializes sd to static buffer and returns pointer, using "pretty printing" mode. char* ll_pretty_print_sd(const LLSD& sd); +//compares the structure of an LLSD to a template LLSD and stores the +//"valid" values in a 3rd LLSD. Default values +//are pulled from the template. Ordering of arrays matters +//Returns false if the test is of same type but values differ in type +//Otherwise, returns true +BOOL compare_llsd_with_template( + const LLSD& llsd_to_test, + const LLSD& template_llsd, + LLSD& resultant_llsd); + #endif // LL_LLSDUTIL_H diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index ec4ff335c9..d4925d8bee 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -299,6 +299,10 @@ public: // a.k.a. strdictcmp() static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b); + // Case *in*sensitive comparison with good handling of numbers. Does not use current locale. + // a.k.a. strdictcmp() + static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b); + // Puts compareDict() in a form appropriate for LL container classes to use for sorting. static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b ); @@ -310,7 +314,7 @@ public: // Copies src into dst at a given offset. static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset); -#ifdef _DEBUG +#ifdef _DEBUG static void testHarness(); #endif @@ -678,6 +682,39 @@ S32 LLStringBase<T>::compareDict(const std::basic_string<T>& astr, const std::ba return ca-cb; } +template<class T> +S32 LLStringBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr) +{ + const T* a = astr.c_str(); + const T* b = bstr.c_str(); + T ca, cb; + S32 ai, bi, cnt = 0; + + ca = *(a++); + cb = *(b++); + while( ca && cb ){ + if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); } + if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); } + if( LLStringOps::isDigit(ca) ){ + if( cnt-->0 ){ + if( cb!=ca ) break; + }else{ + if( !LLStringOps::isDigit(cb) ) break; + for(ai=0; LLStringOps::isDigit(a[ai]); ai++); + for(bi=0; LLStringOps::isDigit(b[bi]); bi++); + if( ai<bi ){ ca=0; break; } + if( bi<ai ){ cb=0; break; } + if( ca!=cb ) break; + cnt = ai; + } + }else if( ca!=cb ){ break; + } + ca = *(a++); + cb = *(b++); + } + return ca-cb; +} + // Puts compareDict() in a form appropriate for LL container classes to use for sorting. // static template<class T> diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h index 1a7c977f21..12bd21ec20 100644 --- a/indra/llcommon/roles_constants.h +++ b/indra/llcommon/roles_constants.h @@ -56,12 +56,12 @@ enum LLRoleChangeType // Powers // -// KNOWN HOLES: -// bit 0x1 << 37 (GP_OBJECT_RETURN) +// KNOWN HOLES: use these for any single bit powers you need +// bit 0x1 << 41 +// bit 0x1 << 46 +// bit 0x1 << 49 and above // These powers were removed to make group roles simpler -// bit 0x1 << 27 (GP_LAND_ALLOW_SCRIPTS) -// bit 0x1 << 16 (GP_LAND_VIEW_OWNED) // bit 0x1 << 41 (GP_ACCOUNTING_VIEW) // bit 0x1 << 46 (GP_PROPOSAL_VIEW) @@ -116,18 +116,19 @@ const U64 GP_LAND_MANAGE_PASSES = 0x1LL << 31; // Change Sell Pass Settings const U64 GP_LAND_ADMIN = 0x1LL << 32; // Eject and Freeze Users on the land // Parcel Content -const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48; // Return objects on parcel that are owned by the group const U64 GP_LAND_RETURN_GROUP_SET = 0x1LL << 33; // Return objects on parcel that are set to group const U64 GP_LAND_RETURN_NON_GROUP = 0x1LL << 34; // Return objects on parcel that are not set to group +const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48; // Return objects on parcel that are owned by the group + // Select a power-bit based on an object's relationship to a parcel. const U64 GP_LAND_RETURN = GP_LAND_RETURN_GROUP_OWNED | GP_LAND_RETURN_GROUP_SET | GP_LAND_RETURN_NON_GROUP; + const U64 GP_LAND_GARDENING = 0x1LL << 35; // Parcel Gardening - plant and move linden trees // Object Management const U64 GP_OBJECT_DEED = 0x1LL << 36; // Deed Object -// HOLE -- 0x1LL << 37 const U64 GP_OBJECT_MANIPULATE = 0x1LL << 38; // Manipulate Group Owned Objects (Move, Copy, Mod) const U64 GP_OBJECT_SET_SALE = 0x1LL << 39; // Set Group Owned Object for Sale @@ -142,9 +143,10 @@ const U64 GP_NOTICES_RECEIVE = 0x1LL << 43; // Receive Notices and View Notice const U64 GP_PROPOSAL_START = 0x1LL << 44; // Start Proposal const U64 GP_PROPOSAL_VOTE = 0x1LL << 45; // Vote on Proposal -const U64 GP_SESSION_JOIN = 0x1LL << 46; //can join session -const U64 GP_SESSION_VOICE = 0x1LL << 47; //can hear/talk -const U64 GP_SESSION_MODERATOR = 0x1LL << 49; //can mute people's session +// Group chat moderation related +const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session +const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk +const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE | GP_LAND_ALLOW_SET_HOME diff --git a/indra/llinventory/lluserrelations.cpp b/indra/llinventory/lluserrelations.cpp index 0f1b48ecd1..e96cb1e1c1 100644 --- a/indra/llinventory/lluserrelations.cpp +++ b/indra/llinventory/lluserrelations.cpp @@ -41,6 +41,7 @@ const LLRelationship LLRelationship::DEFAULT_RELATIONSHIP = LLRelationship(GRANT LLRelationship::LLRelationship() : mGrantToAgent(0), mGrantFromAgent(0), + mChangeSerialNum(0), mIsOnline(false) { } @@ -48,6 +49,7 @@ LLRelationship::LLRelationship() : LLRelationship::LLRelationship(S32 grant_to, S32 grant_from, bool is_online) : mGrantToAgent(grant_to), mGrantFromAgent(grant_from), + mChangeSerialNum(0), mIsOnline(is_online) { } @@ -60,6 +62,7 @@ bool LLRelationship::isOnline() const void LLRelationship::online(bool is_online) { mIsOnline = is_online; + mChangeSerialNum++; } bool LLRelationship::isRightGrantedTo(S32 rights) const @@ -86,12 +89,14 @@ void LLRelationship::grantRights(S32 to_agent, S32 from_agent) { mGrantToAgent |= to_agent; mGrantFromAgent |= from_agent; + mChangeSerialNum++; } void LLRelationship::revokeRights(S32 to_agent, S32 from_agent) { mGrantToAgent &= ~to_agent; mGrantFromAgent &= ~from_agent; + mChangeSerialNum++; } diff --git a/indra/llinventory/lluserrelations.h b/indra/llinventory/lluserrelations.h index 448301383e..36472215f6 100644 --- a/indra/llinventory/lluserrelations.h +++ b/indra/llinventory/lluserrelations.h @@ -142,8 +142,18 @@ public: */ S32 getRightsGrantedFrom() const; - void setRightsTo(S32 to_agent) { mGrantToAgent = to_agent; } - void setRightsFrom(S32 from_agent) { mGrantFromAgent = from_agent; } + void setRightsTo(S32 to_agent) { mGrantToAgent = to_agent; mChangeSerialNum++; } + void setRightsFrom(S32 from_agent) { mGrantFromAgent = from_agent; mChangeSerialNum++;} + + /** + * @brief Get the change count for this agent + * + * Every change to rights will increment the serial number + * allowing listeners to determine when a relationship value is actually new + * + * @return change serial number for relationship + */ + S32 getChangeSerialNum() const { return mChangeSerialNum; } /** * @brief Grant a set of rights. @@ -171,6 +181,7 @@ public: protected: S32 mGrantToAgent; S32 mGrantFromAgent; + S32 mChangeSerialNum; bool mIsOnline; }; diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h index 00a1b03335..e7b15dcc58 100644 --- a/indra/llmath/llrect.h +++ b/indra/llmath/llrect.h @@ -215,7 +215,12 @@ public: mLeft = llmin(mLeft, mRight); mBottom = llmin(mBottom, mTop); } - + + bool isNull() const + { + return mLeft == mRight || mBottom == mTop; + } + void unionWith(const LLRectBase &other) { mLeft = llmin(mLeft, other.mLeft); diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index b80fa069c7..d09af7a8c4 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -163,6 +163,7 @@ inline LLColor3::LLColor3(F32 r, F32 g, F32 b) mV[VZ] = b; } + inline LLColor3::LLColor3(const F32 *vec) { mV[VX] = vec[VX]; diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index 1e6232fc9d..0514870ef6 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -260,6 +260,7 @@ inline LLColor4::LLColor4(U32 clr) mV[VW] = (clr>>24) * (1.0f/255.0f); } + inline LLColor4::LLColor4(const F32 *vec) { mV[VX] = vec[VX]; diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index 473eaea76d..85a1e229ab 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -723,7 +723,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( const LLHTTPNode* node = mRootNode.traverse(mPath, context); if(node) { - llinfos << "LLHTTPResponder::process_impl found node for " + lldebugs << "LLHTTPResponder::process_impl found node for " << mAbsPathAndQuery << llendl; // Copy everything after mLast read to the out. diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index a1b63ead75..aca7a60bf1 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -705,21 +705,28 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count ) // But we don't want to acknowledge UseCircuitCode until the circuit is // available, which is why the acknowledgement test is done above. JC - valid_packet = mTemplateMessageReader->validateMessage(buffer, - receive_size, - host); + valid_packet = mTemplateMessageReader->validateMessage( + buffer, + receive_size, + host); // UseCircuitCode is allowed in even from an invalid circuit, so that // we can toss circuits around. - if(valid_packet && !cdp && - (mTemplateMessageReader->getMessageName() != _PREHASH_UseCircuitCode)) + if( + valid_packet && + !cdp && + (mTemplateMessageReader->getMessageName() != + _PREHASH_UseCircuitCode)) { logMsgFromInvalidCircuit( host, recv_reliable ); clearReceiveState(); valid_packet = FALSE; } - if(valid_packet && cdp && !cdp->getTrusted() && + if( + valid_packet && + cdp && + !cdp->getTrusted() && mTemplateMessageReader->isTrusted()) { logTrustedMsgFromUntrustedCircuit( host ); @@ -729,8 +736,9 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count ) valid_packet = FALSE; } - if (valid_packet - && mTemplateMessageReader->isBanned(cdp && cdp->getTrusted())) + if ( + valid_packet && + mTemplateMessageReader->isBanned(cdp && cdp->getTrusted())) { llwarns << "LLMessageSystem::checkMessages " << "received banned message " @@ -1159,9 +1167,10 @@ LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& n { if(mSendReliable) { - return new LLFnPtrResponder(mReliablePacketParams.mCallback, - mReliablePacketParams.mCallbackData, - name); + return new LLFnPtrResponder( + mReliablePacketParams.mCallback, + mReliablePacketParams.mCallbackData, + name); } else { @@ -1170,8 +1179,10 @@ LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& n // llwarns << "LLMessageSystem::sendMessage: Sending unreliable " // << mMessageBuilder->getMessageName() << " message via HTTP" // << llendl; - return new LLFnPtrResponder(NULL, NULL, - mMessageBuilder->getMessageName()); + return new LLFnPtrResponder( + NULL, + NULL, + mMessageBuilder->getMessageName()); } } @@ -1241,8 +1252,11 @@ S32 LLMessageSystem::sendMessage(const LLHost &host) LLSD message = mLLSDMessageBuilder->getMessage(); const LLHTTPSender& sender = LLHTTPSender::getSender(host); - sender.send(host, mLLSDMessageBuilder->getMessageName(), - message, createResponder(mLLSDMessageBuilder->getMessageName())); + sender.send( + host, + mLLSDMessageBuilder->getMessageName(), + message, + createResponder(mLLSDMessageBuilder->getMessageName())); mSendReliable = FALSE; mReliablePacketParams.clear(); @@ -1423,8 +1437,10 @@ void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, BOOL recv_re } } -S32 LLMessageSystem::sendMessage(const LLHost &host, const char* name, - const LLSD& message) +S32 LLMessageSystem::sendMessage( + const LLHost &host, + const char* name, + const LLSD& message) { if (!(host.isOk())) { diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 491fbb586a..cbfa3c2202 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -196,14 +196,4 @@ public: #endif }; -//RN: maybe this needs to moved elsewhere? -class LLImageProviderInterface -{ -public: - LLImageProviderInterface() {}; - virtual ~LLImageProviderInterface() {}; - - virtual LLImageGL* getUIImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0; -}; - #endif // LL_LLIMAGEGL_H diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 26ce473e08..ce3a4b64c7 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -59,9 +59,6 @@ S32 BTN_HEIGHT = 0; S32 BTN_GRID = 12; S32 BORDER_SIZE = 1; -// static -LLFrameTimer LLButton::sFlashingTimer; - LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& control_name, void (*click_callback)(void*), void *callback_data) : LLUICtrl(name, rect, TRUE, NULL, NULL), mClickedCallback( click_callback ), @@ -79,6 +76,7 @@ LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& co mImageDisabled( NULL ), mImageDisabledSelected( NULL ), mToggleState( FALSE ), + mIsToggle( FALSE ), mScaleImage( TRUE ), mDropShadowedText( TRUE ), mBorderEnabled( FALSE ), @@ -86,8 +84,6 @@ LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& co mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), - mFixedWidth( 16 ), - mFixedHeight( 16 ), mHoverGlowStrength(0.15f), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), @@ -134,6 +130,7 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, mImageDisabled( NULL ), mImageDisabledSelected( NULL ), mToggleState( FALSE ), + mIsToggle( FALSE ), mScaleImage( TRUE ), mDropShadowedText( TRUE ), mBorderEnabled( FALSE ), @@ -141,8 +138,6 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), - mFixedWidth( 16 ), - mFixedHeight( 16 ), mHoverGlowStrength(0.25f), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), @@ -158,15 +153,11 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, if( unselected_image_name != "" ) { + // user-specified image - don't use fixed borders unless requested setImageUnselected(unselected_image_name); setImageDisabled(unselected_image_name); mDisabledImageColor.mV[VALPHA] = 0.5f; - mImageDisabled = mImageUnselected; - mDisabledImageColor.mV[VALPHA] = 0.5f; - // user-specified image - don't use fixed borders unless requested - mFixedWidth = 0; - mFixedHeight = 0; mScaleImage = FALSE; } else @@ -177,13 +168,11 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, if( selected_image_name != "" ) { + // user-specified image - don't use fixed borders unless requested setImageSelected(selected_image_name); setImageDisabledSelected(selected_image_name); mDisabledImageColor.mV[VALPHA] = 0.5f; - // user-specified image - don't use fixed borders unless requested - mFixedWidth = 0; - mFixedHeight = 0; mScaleImage = FALSE; } else @@ -273,6 +262,12 @@ void LLButton::onCommit() make_ui_sound("UISndClickRelease"); } + if (mIsToggle) + { + toggleState(); + } + + // do this last, as it can result in destroying this button if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -286,6 +281,11 @@ BOOL LLButton::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent) BOOL handled = FALSE; if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char && !gKeyboard->getKeyRepeated(' ')) { + if (mIsToggle) + { + toggleState(); + } + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -302,11 +302,17 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent ) { if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { + if (mIsToggle) + { + toggleState(); + } + + handled = TRUE; + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); } - handled = TRUE; } } return handled; @@ -354,6 +360,9 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) (*mMouseUpCallback)(mCallbackUserData); } + mMouseDownTimer.stop(); + mMouseDownTimer.reset(); + // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked. // If mouseup in the widget, it's been clicked if (pointInView(x, y)) @@ -363,6 +372,11 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) make_ui_sound("UISndClickRelease"); } + if (mIsToggle) + { + toggleState(); + } + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -422,8 +436,10 @@ void LLButton::draw() BOOL flash = FALSE; if( mFlashing ) { - F32 elapsed = LLButton::sFlashingTimer.getElapsedTimeF32(); - flash = S32(elapsed * 2) & 1; + F32 elapsed = mFlashingTimer.getElapsedTimeF32(); + S32 flash_count = S32(elapsed * LLUI::sConfigGroup->getF32("ButtonFlashRate") * 2.f); + // flash on or off? + flash = (flash_count % 2 == 0) || flash_count > (F32)LLUI::sConfigGroup->getS32("ButtonFlashCount"); } BOOL pressed_by_keyboard = FALSE; @@ -443,24 +459,14 @@ void LLButton::draw() cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]); screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); - BOOL pressed = pressed_by_keyboard || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)); - - BOOL display_state = FALSE; - if( pressed ) - { - mImagep = mImageSelected; - // show the resulting state after releasing the mouse button while it is down - display_state = mToggleState ? FALSE : TRUE; - } - else - { - display_state = mToggleState || flash; - } + BOOL pressed = pressed_by_keyboard + || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) + || mToggleState; BOOL use_glow_effect = FALSE; - if ( mNeedsHighlight ) + if ( mNeedsHighlight || flash ) { - if (display_state) + if (pressed) { if (mImageHoverSelected) { @@ -485,7 +491,7 @@ void LLButton::draw() } } } - else if ( display_state ) + else if ( pressed ) { mImagep = mImageSelected; } @@ -499,11 +505,11 @@ void LLButton::draw() // enabled and tentative // or // disabled but checked - if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && display_state ) ) ) + if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && pressed ) ) ) { mImagep = mImageDisabledSelected; } - else if (!mImageDisabled.isNull() && !mEnabled && !display_state) + else if (!mImageDisabled.isNull() && !mEnabled && !pressed) { mImagep = mImageDisabled; } @@ -516,33 +522,34 @@ void LLButton::draw() // Figure out appropriate color for the text LLColor4 label_color; + // label changes when button state changes, not when pressed if ( mEnabled ) { - if ( !display_state ) + if ( mToggleState ) { - label_color = mUnselectedLabelColor; + label_color = mSelectedLabelColor; } else { - label_color = mSelectedLabelColor; + label_color = mUnselectedLabelColor; } } else { - if ( !display_state ) + if ( mToggleState ) { - label_color = mDisabledLabelColor; + label_color = mDisabledSelectedLabelColor; } else { - label_color = mDisabledSelectedLabelColor; + label_color = mDisabledLabelColor; } } // Unselected label assignments LLWString label; - if( display_state ) + if( mToggleState ) { if( mEnabled || mDisabledSelectedLabel.empty() ) { @@ -591,24 +598,22 @@ void LLButton::draw() // Otherwise draw basic rectangular button. if( mImagep.notNull() && !mScaleImage) { - gl_draw_image( 0, 0, mImagep, mEnabled ? mImageColor : mDisabledImageColor ); + mImagep->draw(0, 0, mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); - gl_draw_scaled_image_with_border(0, 0, 0, 0, mImagep->getWidth(), mImagep->getHeight(), mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE); + mImagep->drawSolid(0, 0, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } else if ( mImagep.notNull() && mScaleImage) { - gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(), - mImagep, mEnabled ? mImageColor : mDisabledImageColor ); + mImagep->draw(0, 0, mRect.getWidth(), mRect.getHeight(), mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); - gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(), - mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE); + mImagep->drawSolid(0, 0, mRect.getWidth(), mRect.getHeight(), LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } @@ -620,13 +625,17 @@ void LLButton::draw() gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE); } + // let overlay image and text play well together + S32 text_left = mLeftHPad; + S32 text_right = mRect.getWidth() - mRightHPad; + S32 text_width = mRect.getWidth() - mLeftHPad - mRightHPad; + // draw overlay image if (mImageOverlay.notNull()) { - const S32 IMG_PAD = 5; // get max width and height (discard level 0) - S32 overlay_width = mImageOverlay->getWidth(0); - S32 overlay_height = mImageOverlay->getHeight(0); + S32 overlay_width = mImageOverlay->getWidth(); + S32 overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)mRect.getWidth() / (F32)overlay_width, (F32)mRect.getHeight() / (F32)overlay_height, 1.f); overlay_width = llround((F32)overlay_width * scale_factor); @@ -635,34 +644,49 @@ void LLButton::draw() S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); + //FUGLY HACK FOR "DEPRESSED" BUTTONS + if (pressed) + { + center_y--; + center_x++; + } + + // fade out overlay images on disabled buttons + LLColor4 overlay_color = mImageOverlayColor; + if (!getEnabled()) + { + overlay_color.mV[VALPHA] = 0.5f; + } + switch(mImageOverlayAlignment) { case LLFontGL::LEFT: - gl_draw_scaled_image( - IMG_PAD, + text_left += overlay_width + 1; + text_width -= overlay_width + 1; + mImageOverlay->draw( + mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; case LLFontGL::HCENTER: - gl_draw_scaled_image( + mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; case LLFontGL::RIGHT: - gl_draw_scaled_image( - mRect.getWidth() - IMG_PAD - overlay_width, + text_right -= overlay_width + 1; + text_width -= overlay_width + 1; + mImageOverlay->draw( + mRect.getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; default: // draw nothing @@ -673,28 +697,26 @@ void LLButton::draw() // Draw label if( !label.empty() ) { - S32 drawable_width = mRect.getWidth() - mLeftHPad - mRightHPad; - LLWString::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: - x = mRect.getWidth() - mRightHPad; + x = text_right; break; case LLFontGL::HCENTER: x = mRect.getWidth() / 2; break; case LLFontGL::LEFT: default: - x = mLeftHPad; + x = text_left; break; } S32 y_offset = 2 + (mRect.getHeight() - 20)/2; - if (pressed || display_state) + if (pressed) { y_offset--; x++; @@ -704,7 +726,7 @@ void LLButton::draw() label_color, mHAlign, LLFontGL::BOTTOM, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NORMAL, - U32_MAX, drawable_width, + U32_MAX, text_width, NULL, FALSE, FALSE); } @@ -733,13 +755,11 @@ void LLButton::drawBorder(const LLColor4& color, S32 size) if (mScaleImage) { - gl_draw_scaled_image_with_border(left, bottom, mFixedWidth, mFixedHeight, right-left, top-bottom, - mImagep, color, TRUE ); + mImagep->drawSolid(left, bottom, right-left, top-bottom, color); } else { - gl_draw_scaled_image_with_border(left, bottom, 0, 0, mImagep->getWidth() + size * 2, - mImagep->getHeight() + size * 2, mImagep, color, TRUE ); + mImagep->drawSolid(left, bottom, mImagep->getWidth() + size * 2, mImagep->getHeight() + size * 2, color); } } @@ -763,6 +783,22 @@ void LLButton::setToggleState(BOOL b) } } +void LLButton::setFlashing( BOOL b ) +{ + if (b != mFlashing) + { + mFlashing = b; + mFlashingTimer.reset(); + } +} + + +BOOL LLButton::toggleState() +{ + setToggleState( !mToggleState ); + return mToggleState; +} + void LLButton::setValue(const LLSD& value ) { mToggleState = value.asBoolean(); @@ -770,7 +806,7 @@ void LLButton::setValue(const LLSD& value ) LLSD LLButton::getValue() const { - return mToggleState; + return mToggleState == TRUE; } void LLButton::setLabel( const LLStringExplicit& label ) @@ -807,10 +843,9 @@ void LLButton::setDisabledSelectedLabel( const LLStringExplicit& label ) mDisabledSelectedLabel = label; } -void LLButton::setImageUnselectedID( const LLUUID &image_id ) -{ - mImageUnselectedName = ""; - mImageUnselected = LLUI::sImageProvider->getUIImageByID(image_id); +void LLButton::setImageUnselected(LLPointer<LLUIImage> image) +{ + mImageUnselected = image; } void LLButton::setImages( const LLString &image_name, const LLString &selected_name ) @@ -820,10 +855,9 @@ void LLButton::setImages( const LLString &image_name, const LLString &selected_n } -void LLButton::setImageSelectedID( const LLUUID &image_id ) +void LLButton::setImageSelected(LLPointer<LLUIImage> image) { - mImageSelectedName = ""; - mImageSelected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageSelected = image; } void LLButton::setImageColor(const LLColor4& c) @@ -831,19 +865,22 @@ void LLButton::setImageColor(const LLColor4& c) mImageColor = c; } +void LLButton::setColor(const LLColor4& color) +{ + setImageColor(color); +} + -void LLButton::setImageDisabledID( const LLUUID &image_id ) +void LLButton::setImageDisabled(LLPointer<LLUIImage> image) { - mImageDisabledName = ""; - mImageDisabled = LLUI::sImageProvider->getUIImageByID(image_id); + mImageDisabled = image; mDisabledImageColor = mImageColor; mDisabledImageColor.mV[VALPHA] *= 0.5f; } -void LLButton::setImageDisabledSelectedID( const LLUUID &image_id ) -{ - mImageDisabledSelectedName = ""; - mImageDisabledSelected = LLUI::sImageProvider->getUIImageByID(image_id); +void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image) +{ + mImageDisabledSelected = image; mDisabledImageColor = mImageColor; mDisabledImageColor.mV[VALPHA] *= 0.5f; } @@ -855,11 +892,9 @@ void LLButton::setDisabledImages( const LLString &image_name, const LLString &se mDisabledImageColor = c; } - -void LLButton::setImageHoverSelectedID( const LLUUID& image_id ) +void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image) { - mImageHoverSelectedName = ""; - mImageHoverSelected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageHoverSelected = image; } void LLButton::setDisabledImages( const LLString &image_name, const LLString &selected_name) @@ -869,10 +904,9 @@ void LLButton::setDisabledImages( const LLString &image_name, const LLString &se setDisabledImages( image_name, selected_name, clr ); } -void LLButton::setImageHoverUnselectedID( const LLUUID& image_id ) +void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image) { - mImageHoverUnselectedName = ""; - mImageHoverUnselected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageHoverUnselected = image; } void LLButton::setHoverImages( const LLString& image_name, const LLString& selected_name ) @@ -889,8 +923,7 @@ void LLButton::setImageOverlay(const LLString &image_name, LLFontGL::HAlign alig } else { - LLUUID overlay_image_id = LLUI::findAssetUUIDByName(image_name); - mImageOverlay = LLUI::sImageProvider->getUIImageByID(overlay_image_id); + mImageOverlay = LLUI::getUIImageByName(image_name); mImageOverlayAlignment = alignment; mImageOverlayColor = color; } @@ -904,34 +937,6 @@ void LLButton::onMouseCaptureLost() } //------------------------------------------------------------------------- -// LLSquareButton -//------------------------------------------------------------------------- -LLSquareButton::LLSquareButton(const LLString& name, const LLRect& rect, - const LLString& label, - const LLFontGL *font, - const LLString& control_name, - void (*click_callback)(void*), - void *callback_data, - const LLString& selected_label ) -: LLButton(name, rect, "","", - control_name, - click_callback, callback_data, - font, - label, - (selected_label.empty() ? label : selected_label) ) -{ - setImageUnselected("square_btn_32x128.tga"); - // mImageUnselected = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga"))); - setImageSelected("square_btn_selected_32x128.tga"); - // mImageSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga"))); - setImageDisabled("square_btn_32x128.tga"); - //mDisabledImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga"))); - setImageDisabledSelected("square_btn_selected_32x128.tga"); - //mDisabledSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga"))); - mImageColor = LLUI::sColorsGroup->getColor("ButtonColor"); -} - -//------------------------------------------------------------------------- // Utilities //------------------------------------------------------------------------- S32 round_up(S32 grid, S32 value) @@ -951,37 +956,37 @@ S32 round_up(S32 grid, S32 value) void LLButton::setImageUnselected(const LLString &image_name) { - setImageUnselectedID(LLUI::findAssetUUIDByName(image_name)); + setImageUnselected(LLUI::getUIImageByName(image_name)); mImageUnselectedName = image_name; } void LLButton::setImageSelected(const LLString &image_name) { - setImageSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageSelected(LLUI::getUIImageByName(image_name)); mImageSelectedName = image_name; } void LLButton::setImageHoverSelected(const LLString &image_name) { - setImageHoverSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageHoverSelected(LLUI::getUIImageByName(image_name)); mImageHoverSelectedName = image_name; } void LLButton::setImageHoverUnselected(const LLString &image_name) { - setImageHoverUnselectedID(LLUI::findAssetUUIDByName(image_name)); + setImageHoverUnselected(LLUI::getUIImageByName(image_name)); mImageHoverUnselectedName = image_name; } void LLButton::setImageDisabled(const LLString &image_name) { - setImageDisabledID(LLUI::findAssetUUIDByName(image_name)); + setImageDisabled(LLUI::getUIImageByName(image_name)); mImageDisabledName = image_name; } void LLButton::setImageDisabledSelected(const LLString &image_name) { - setImageDisabledSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageDisabledSelected(LLUI::getUIImageByName(image_name)); mImageDisabledSelectedName = image_name; } @@ -1009,8 +1014,6 @@ LLXMLNodePtr LLButton::getXML(bool save_children) const node->createChild("label_selected", TRUE)->setStringValue(getLabelSelected()); node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont)); node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign)); - node->createChild("border_width", TRUE)->setIntValue(mFixedWidth); - node->createChild("border_height", TRUE)->setIntValue(mFixedHeight); addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,"image_unselected"); addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,"image_selected"); @@ -1092,8 +1095,12 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa label, label_selected); - node->getAttributeS32("border_width", button->mFixedWidth); - node->getAttributeS32("border_height", button->mFixedHeight); + node->getAttributeS32("pad_right", button->mRightHPad); + node->getAttributeS32("pad_left", button->mLeftHPad); + + BOOL is_toggle = button->getIsToggle(); + node->getAttributeBOOL("toggle", is_toggle); + button->setIsToggle(is_toggle); if(image_hover_selected != LLString::null) button->setImageHoverSelected(image_hover_selected); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 5f7d917b4e..0e140a45a6 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -114,11 +114,13 @@ public: F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); } - BOOL toggleState() { setToggleState( !mToggleState ); return mToggleState; } + BOOL getIsToggle() const { return mIsToggle; } + void setIsToggle(BOOL is_toggle) { mIsToggle = is_toggle; } + BOOL toggleState(); BOOL getToggleState() const { return mToggleState; } void setToggleState(BOOL b); - void setFlashing( BOOL b ) { mFlashing = b; } + void setFlashing( BOOL b ); BOOL getFlashing() const { return mFlashing; } void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } @@ -128,14 +130,11 @@ public: const LLString getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); } const LLString getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); } - - // HACK to allow images to be freed when the caller knows he's done with it. - LLImageGL* getImageUnselected() const { return mImageUnselected; } - void setImageColor(const LLString& color_control); - void setImages(const LLString &image_name, const LLString &selected_name); void setImageColor(const LLColor4& c); - + virtual void setColor(const LLColor4& c); + + void setImages(const LLString &image_name, const LLString &selected_name); void setDisabledImages(const LLString &image_name, const LLString &selected_name); void setDisabledImages(const LLString &image_name, const LLString &selected_name, const LLColor4& c); @@ -146,7 +145,7 @@ public: void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; } void setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); - LLPointer<LLImageGL> getImageOverlay() { return mImageOverlay; } + LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; } virtual void setValue(const LLSD& value ); @@ -170,16 +169,8 @@ public: static void onHeldDown(void *userdata); // to be called by gIdleCallbacks - void setFixedBorder(S32 width, S32 height) { mFixedWidth = width; mFixedHeight = height; } void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } -private: - void setImageUnselectedID(const LLUUID &image_id); - void setImageSelectedID(const LLUUID &image_id); - void setImageHoverSelectedID(const LLUUID &image_id); - void setImageHoverUnselectedID(const LLUUID &image_id); - void setImageDisabledID(const LLUUID &image_id); - void setImageDisabledSelectedID(const LLUUID &image_id); public: void setImageUnselected(const LLString &image_name); void setImageSelected(const LLString &image_name); @@ -187,6 +178,14 @@ public: void setImageHoverUnselected(const LLString &image_name); void setImageDisabled(const LLString &image_name); void setImageDisabledSelected(const LLString &image_name); + + void setImageUnselected(LLPointer<LLUIImage> image); + void setImageSelected(LLPointer<LLUIImage> image); + void setImageHoverSelected(LLPointer<LLUIImage> image); + void setImageHoverUnselected(LLPointer<LLUIImage> image); + void setImageDisabled(LLPointer<LLUIImage> image); + void setImageDisabledSelected(LLPointer<LLUIImage> image); + void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } BOOL getCommitOnReturn() { return mCommitOnReturn; } @@ -209,27 +208,27 @@ protected: F32 mHeldDownDelay; // seconds, after which held-down callbacks get called S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called - LLPointer<LLImageGL> mImageOverlay; + LLPointer<LLUIImage> mImageOverlay; LLFontGL::HAlign mImageOverlayAlignment; LLColor4 mImageOverlayColor; - LLPointer<LLImageGL> mImageUnselected; + LLPointer<LLUIImage> mImageUnselected; LLUIString mUnselectedLabel; LLColor4 mUnselectedLabelColor; - LLPointer<LLImageGL> mImageSelected; + LLPointer<LLUIImage> mImageSelected; LLUIString mSelectedLabel; LLColor4 mSelectedLabelColor; - LLPointer<LLImageGL> mImageHoverSelected; + LLPointer<LLUIImage> mImageHoverSelected; - LLPointer<LLImageGL> mImageHoverUnselected; + LLPointer<LLUIImage> mImageHoverUnselected; - LLPointer<LLImageGL> mImageDisabled; + LLPointer<LLUIImage> mImageDisabled; LLUIString mDisabledLabel; LLColor4 mDisabledLabelColor; - LLPointer<LLImageGL> mImageDisabledSelected; + LLPointer<LLUIImage> mImageDisabledSelected; LLUIString mDisabledSelectedLabel; LLColor4 mDisabledSelectedLabelColor; @@ -254,6 +253,7 @@ protected: LLColor4 mImageColor; LLColor4 mDisabledImageColor; + BOOL mIsToggle; BOOL mToggleState; BOOL mScaleImage; @@ -267,9 +267,6 @@ protected: S32 mLeftHPad; S32 mRightHPad; - S32 mFixedWidth; - S32 mFixedHeight; - F32 mHoverGlowStrength; F32 mCurGlowStrength; @@ -278,22 +275,9 @@ protected: LLString mHelpURL; - LLPointer<LLImageGL> mImagep; - - static LLFrameTimer sFlashingTimer; -}; + LLPointer<LLUIImage> mImagep; -class LLSquareButton -: public LLButton -{ -public: - LLSquareButton(const LLString& name, const LLRect& rect, - const LLString& label, - const LLFontGL *font = NULL, - const LLString& control_name = LLString(), - void (*click_callback)(void*) = NULL, - void *callback_data = NULL, - const LLString& selected_label = LLString::null ); + LLFrameTimer mFlashingTimer; }; // Helpful functions diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 504b342003..b0a7e9d27f 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -75,7 +75,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLString& name, const LLRect& rect, } // must be big enough to hold all children - setSpanChildren(TRUE); + setUseBoundingRect(TRUE); mKeyboardFocusOnClick = TRUE; @@ -130,6 +130,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLString& name, const LLRect& rect, mButton->setDisabledImages( inactive_false_id, inactive_true_id ); mButton->setHoverGlowStrength(0.35f); } + mButton->setIsToggle(TRUE); mButton->setToggleState( initial_value ); mButton->setFollowsLeft(); mButton->setFollowsBottom(); @@ -150,16 +151,11 @@ void LLCheckBoxCtrl::onButtonPress( void *userdata ) if (self->mRadioStyle) { - if (!self->getValue()) - { - self->setValue(TRUE); - } - } - else - { - self->toggle(); + self->setValue(TRUE); } + self->setControlValue(self->getValue()); + // HACK: because buttons don't normally commit self->onCommit(); if (self->mKeyboardFocusOnClick) @@ -232,14 +228,13 @@ void LLCheckBoxCtrl::draw() //virtual void LLCheckBoxCtrl::setValue(const LLSD& value ) { - mSetValue = value.asBoolean(); - mButton->setToggleState( mSetValue ); + mButton->setValue( value ); } //virtual LLSD LLCheckBoxCtrl::getValue() const { - return mButton->getToggleState(); + return mButton->getValue(); } void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label ) diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 5f76cfc94b..6063fc155a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -55,17 +55,16 @@ // Globals S32 LLCOMBOBOX_HEIGHT = 0; S32 LLCOMBOBOX_WIDTH = 0; - +S32 MAX_COMBO_WIDTH = 500; + LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label, void (*commit_callback)(LLUICtrl*,void*), void *callback_userdata ) : LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, FOLLOWS_LEFT | FOLLOWS_TOP), - mDrawArrow(TRUE), mTextEntry(NULL), mArrowImage(NULL), - mArrowImageWidth(8), mAllowTextEntry(FALSE), mMaxChars(20), mTextEntryTentative(TRUE), @@ -73,55 +72,43 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString mPrearrangeCallback( NULL ), mTextEntryCallback( NULL ) { - // For now, all comboboxes don't take keyboard focus when clicked. - // This might change if it is part of a modal dialog. - // mKeyboardFocusOnClick = FALSE; - - // Revert to standard behavior. When this control's parent is hidden, it needs to - // hide this ctrl--which won't just happen automatically since when LLComboBox is - // showing its list, it's also set to TopCtrl. When keyboard focus is cleared all - // controls (including this one) know that they are no longer editing. - mKeyboardFocusOnClick = TRUE; - - LLRect r; - r.setOriginAndSize(0, 0, rect.getWidth(), rect.getHeight()); - // Always use text box // Text label button - mButton = new LLSquareButton("comboxbox button", - r, label, NULL, LLString::null, + mButton = new LLButton("comboxbox button", + LLRect(), label, NULL, LLString::null, NULL, this); + mButton->setImageUnselected("square_btn_32x128.tga"); + mButton->setImageSelected("square_btn_selected_32x128.tga"); + mButton->setImageDisabled("square_btn_32x128.tga"); + mButton->setImageDisabledSelected("square_btn_selected_32x128.tga"); + mButton->setScaleImage(TRUE); + mButton->setMouseDownCallback(onButtonDown); mButton->setFont(LLFontGL::sSansSerifSmall); mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); mButton->setHAlign( LLFontGL::LEFT ); - - const S32 ARROW_WIDTH = 16; - mButton->setRightHPad( ARROW_WIDTH ); + mButton->setRightHPad(2); addChild(mButton); - // Default size, will be set by arrange() call in button callback. - S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE; - r.setOriginAndSize(0, 16, list_width, 220); - // disallow multiple selection mList = new LLScrollListCtrl( - "ComboBox", r, + "ComboBox", LLRect(), &LLComboBox::onItemSelected, this, FALSE); mList->setVisible(FALSE); mList->setBgWriteableColor( LLColor4(1,1,1,1) ); mList->setCommitOnKeyboardMovement(FALSE); - mList->setFocusChangedCallback(onListFocusChanged); addChild(mList); LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0); mBorder = new LLViewBorder( "combo border", border_rect ); addChild( mBorder ); - mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM); + mBorder->setFollowsAll(); LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") ); - mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id); - mArrowImageWidth = llmax(8,mArrowImage->getWidth(0)); // In case image hasn't loaded yet + mArrowImage = LLUI::sImageProvider->getImageByID(arrow_image_id); + mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT); + + updateLayout(); } @@ -155,7 +142,7 @@ LLXMLNodePtr LLComboBox::getXML(bool save_children) const LLSD value = item->getValue(); item_node->createChild("value", TRUE)->setStringValue(value.asString()); item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled()); - item_node->setStringValue(cell->getText()); + item_node->setStringValue(cell->getValue().asString()); } } @@ -272,34 +259,42 @@ void LLComboBox::resetDirty() // add item "name" to menu -void LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) +LLScrollListItem* LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) { - mList->addSimpleItem(name, pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled); mList->selectFirstItem(); + return item; } // add item "name" with a unique id to menu -void LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) { - mList->addSimpleItem(name, LLSD(id), pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, LLSD(id), pos, enabled); mList->selectFirstItem(); + return item; } // add item "name" with attached userdata -void LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) { LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled); item->setUserdata( userdata ); mList->selectFirstItem(); + return item; } // add item "name" with attached generic data -void LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) { - mList->addSimpleItem(name, value, pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, value, pos, enabled); mList->selectFirstItem(); + return item; } +LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) +{ + return mList->addSeparator(pos); +} void LLComboBox::sortByName() { @@ -335,9 +330,9 @@ void LLComboBox::setValue(const LLSD& value) } } -const LLString& LLComboBox::getSimple() const +const LLString LLComboBox::getSimple() const { - const LLString& res = mList->getSimpleSelectedItem(); + const LLString res = mList->getSimpleSelectedItem(); if (res.empty() && mAllowTextEntry) { return mTextEntry->getText(); @@ -348,7 +343,7 @@ const LLString& LLComboBox::getSimple() const } } -const LLString& LLComboBox::getSimpleSelectedItem(S32 column) const +const LLString LLComboBox::getSimpleSelectedItem(S32 column) const { return mList->getSimpleSelectedItem(column); } @@ -373,7 +368,7 @@ LLSD LLComboBox::getValue() const void LLComboBox::setLabel(const LLStringExplicit& name) { - if ( mAllowTextEntry ) + if ( mTextEntry ) { mTextEntry->setText(name); if (mList->selectSimpleItem(name, FALSE)) @@ -385,7 +380,8 @@ void LLComboBox::setLabel(const LLStringExplicit& name) mTextEntry->setTentative(mTextEntryTentative); } } - else + + if (!mAllowTextEntry) { mButton->setLabelUnselected(name); mButton->setLabelSelected(name); @@ -433,16 +429,21 @@ void LLComboBox::onFocusLost() LLUICtrl::onFocusLost(); } +void LLComboBox::onLostTop() +{ + hideList(); +} + + void LLComboBox::setButtonVisible(BOOL visible) { mButton->setVisible(visible); - mDrawArrow = visible; if (mTextEntry) { LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); if (visible) { - text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth(0)) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); } //mTextEntry->setRect(text_entry_rect); mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); @@ -457,22 +458,8 @@ void LLComboBox::draw() mButton->setEnabled(mEnabled /*&& !mList->isEmpty()*/); - // Draw children + // Draw children normally LLUICtrl::draw(); - - if (mDrawArrow) - { - // Paste the graphic on the right edge - if (!mArrowImage.isNull()) - { - S32 arrow_height = llmin(mRect.getHeight(), mArrowImage->getHeight()); - S32 arrow_width = llround((F32)mArrowImage->getWidth() * ((F32)arrow_height / (F32)mArrowImage->getHeight())); - - S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton"); - - gl_draw_scaled_image( left, 0, arrow_width, arrow_height, mArrowImage, LLColor4::white); - } - } } } @@ -497,6 +484,67 @@ S32 LLComboBox::getCurrentIndex() const } +void LLComboBox::updateLayout() +{ + LLRect rect = getLocalRect(); + if (mAllowTextEntry) + { + S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); + mButton->setRect(LLRect( mRect.getWidth() - llmax(8,mArrowImage->getWidth(0)) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + mButton->setTabStop(FALSE); + + if (!mTextEntry) + { + LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth(0)) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); + // clear label on button + LLString cur_label = mButton->getLabelSelected(); + mTextEntry = new LLLineEditor("combo_text_entry", + text_entry_rect, + "", + LLFontGL::sSansSerifSmall, + mMaxChars, + onTextCommit, + onTextEntry, + NULL, + this, + NULL, // prevalidate func + LLViewBorder::BEVEL_NONE, + LLViewBorder::STYLE_LINE, + 0); // no border + mTextEntry->setSelectAllonFocusReceived(TRUE); + mTextEntry->setHandleEditKeysDirectly(TRUE); + mTextEntry->setCommitOnFocusLost(FALSE); + mTextEntry->setText(cur_label); + mTextEntry->setIgnoreTab(TRUE); + mTextEntry->setFollowsAll(); + addChild(mTextEntry); + } + else + { + mTextEntry->setVisible(TRUE); + mTextEntry->setMaxTextLength(mMaxChars); + } + + // clear label on button + setLabel(LLString::null); + + mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); + } + else if (!mAllowTextEntry) + { + mButton->setRect(rect); + mButton->setTabStop(TRUE); + + if (mTextEntry) + { + mTextEntry->setVisible(FALSE); + } + mButton->setFollowsAll(); + } +} + void* LLComboBox::getCurrentUserdata() { LLScrollListItem* item = mList->getFirstSelected(); @@ -514,7 +562,7 @@ void LLComboBox::showList() LLCoordWindow window_size; getWindow()->getSize(&window_size); //HACK: shouldn't have to know about scale here - mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); + mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); // Make sure that we can see the whole list LLRect root_view_local; @@ -523,7 +571,9 @@ void LLComboBox::showList() LLRect rect = mList->getRect(); - S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE; + S32 min_width = mRect.getWidth(); + S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); + S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width); if (mListPosition == BELOW) { @@ -583,12 +633,6 @@ void LLComboBox::showList() mList->translate(0, -y); } - // pass mouse capture on to list if button is depressed - if (mButton->hasMouseCapture()) - { - gFocusMgr.setMouseCapture(mList); - } - // NB: this call will trigger the focuslost callback which will hide the list, so do it first // before finally showing the list @@ -604,24 +648,29 @@ void LLComboBox::showList() mButton->setToggleState(TRUE); mList->setVisible(TRUE); - gFocusMgr.setTopCtrl(mList); + setUseBoundingRect(TRUE); + gFocusMgr.setTopCtrl(this); } void LLComboBox::hideList() { + //*HACK: store the original value explicitly somewhere, not just in label + LLString orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected(); + + // assert selection in list + mList->selectSimpleItem(orig_selection, FALSE); + mButton->setToggleState(FALSE); mList->setVisible(FALSE); mList->highlightNthItem(-1); - if( gFocusMgr.getTopCtrl() == mList ) + setUseBoundingRect(FALSE); + if( gFocusMgr.getTopCtrl() == this ) { gFocusMgr.setTopCtrl(NULL); } - - //mList->setFocus(FALSE); } - //------------------------------------------------------------------ // static functions //------------------------------------------------------------------ @@ -650,21 +699,20 @@ void LLComboBox::onButtonDown(void *userdata) self->showList(); } - if (self->mKeyboardFocusOnClick && !self->hasFocus()) + self->setFocus( TRUE ); + + // pass mouse capture on to list if button is depressed + if (self->mButton->hasMouseCapture()) { - self->setFocus( TRUE ); + gFocusMgr.setMouseCapture(self->mList); } } else { - // hide and release keyboard focus self->hideList(); - - self->onCommit(); } -} - +} // static void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) @@ -672,7 +720,7 @@ void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) // Note: item is the LLScrollListCtrl LLComboBox *self = (LLComboBox *) userdata; - const LLString& name = self->mList->getSimpleSelectedItem(); + const LLString name = self->mList->getSimpleSelectedItem(); S32 cur_id = self->getCurrentIndex(); if (cur_id != -1) @@ -681,40 +729,24 @@ void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) if (self->mAllowTextEntry) { - gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL); + gFocusMgr.setKeyboardFocus(self->mTextEntry); self->mTextEntry->selectAll(); } } else { // invalid selection, just restore existing value - self->mList->selectSimpleItem(self->mButton->getLabelSelected()); + LLString orig_selection = self->mAllowTextEntry ? self->mTextEntry->getText() : self->mButton->getLabelSelected(); + + self->mList->selectSimpleItem(orig_selection); } self->onCommit(); self->hideList(); } -// static -void LLComboBox::onListFocusChanged(LLUICtrl* list, void* user_data) -{ - LLComboBox *self = (LLComboBox *) list->getParent(); - // user not manipulating list or clicking on drop down button - if (!self->mList->hasFocus() && !self->mButton->hasMouseCapture()) - { - //*HACK: store the original value explicitly somewhere, not just in label - LLString orig_selection = self->mAllowTextEntry ? self->mTextEntry->getText() : self->mButton->getLabelSelected(); - - self->hideList(); - - // reassert original selection - self->mList->selectSimpleItem(orig_selection, FALSE); - } -} - BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) { - LLString tool_tip; if (LLUI::sShowXUINames) @@ -726,23 +758,19 @@ BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_ tool_tip = mToolTipMsg; } - if( getVisible() && pointInView( x, y ) ) + if( !tool_tip.empty() ) { - if( !tool_tip.empty() ) - { - msg = tool_tip; - - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - mRect.getWidth(), mRect.getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); - } - return TRUE; + msg = tool_tip; + + // Convert rect local to screen coordinates + localPointToScreen( + 0, 0, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + mRect.getWidth(), mRect.getHeight(), + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); } - return FALSE; + return TRUE; } BOOL LLComboBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) @@ -793,63 +821,11 @@ BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative) { - LLRect rect( 0, mRect.getHeight(), mRect.getWidth(), 0); - if (allow && !mAllowTextEntry) - { - S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); - mButton->setRect(LLRect( mRect.getWidth() - mArrowImageWidth - 2 * shadow_size, - rect.mTop, rect.mRight, rect.mBottom)); - mButton->setTabStop(FALSE); - - // clear label on button - LLString cur_label = mButton->getLabelSelected(); - setLabel(LLString::null); - if (!mTextEntry) - { - LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); - text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); - mTextEntry = new LLLineEditor("combo_text_entry", - text_entry_rect, - "", - LLFontGL::sSansSerifSmall, - max_chars, - onTextCommit, - onTextEntry, - NULL, - this, - NULL, // prevalidate func - LLViewBorder::BEVEL_NONE, - LLViewBorder::STYLE_LINE, - 0); // no border - mTextEntry->setSelectAllonFocusReceived(TRUE); - mTextEntry->setHandleEditKeysDirectly(TRUE); - mTextEntry->setCommitOnFocusLost(FALSE); - mTextEntry->setText(cur_label); - mTextEntry->setIgnoreTab(TRUE); - mTextEntry->setFollowsAll(); - addChild(mTextEntry); - mMaxChars = max_chars; - } - else - { - mTextEntry->setVisible(TRUE); - } - - mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); - } - else if (!allow && mAllowTextEntry) - { - mButton->setRect(rect); - mButton->setTabStop(TRUE); - - if (mTextEntry) - { - mTextEntry->setVisible(FALSE); - } - mButton->setFollowsAll(); - } mAllowTextEntry = allow; - mTextEntryTentative = set_tentative; + mTextEntryTentative = set_tentative; + mMaxChars = max_chars; + + updateLayout(); } void LLComboBox::setTextEntry(const LLStringExplicit& text) @@ -993,6 +969,10 @@ void LLComboBox::setFocus(BOOL b) if (b) { mList->clearSearchString(); + if (mList->getVisible()) + { + mList->setFocus(TRUE); + } } } @@ -1097,3 +1077,155 @@ BOOL LLComboBox::operateOnAll(EOperation op) } return FALSE; } + + + +// +// LLFlyoutButton +// + +const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; + +LLFlyoutButton::LLFlyoutButton( + const LLString& name, + const LLRect &rect, + const LLString& label, + void (*commit_callback)(LLUICtrl*, void*) , + void *callback_userdata) +: LLComboBox(name, rect, LLString::null, commit_callback, callback_userdata), + mToggleState(FALSE), + mActionButton(NULL) +{ + // Always use text box + // Text label button + mActionButton = new LLButton("flyout_button_main", + LLRect(), label, NULL, LLString::null, + NULL, this); + mActionButton->setScaleImage(TRUE); + + mActionButton->setClickedCallback(onActionButtonClick); + mActionButton->setFollowsAll(); + mActionButton->setHAlign( LLFontGL::HCENTER ); + mActionButton->setLabel(label); + addChild(mActionButton); + + mActionButtonImage = LLUI::getUIImageByName("flyout_btn_left.tga"); + mExpanderButtonImage = LLUI::getUIImageByName("flyout_btn_right.tga"); + mActionButtonImageSelected = LLUI::getUIImageByName("flyout_btn_left_selected.tga"); + mExpanderButtonImageSelected = LLUI::getUIImageByName("flyout_btn_right_selected.tga"); + + mActionButton->setImageSelected(mActionButtonImageSelected); + mActionButton->setImageUnselected(mActionButtonImage); + mActionButton->setImageDisabled(LLPointer<LLUIImage>(NULL)); + mActionButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); + + mButton->setImageSelected(mExpanderButtonImageSelected); + mButton->setImageUnselected(mExpanderButtonImage); + mButton->setImageDisabled(LLPointer<LLUIImage>(NULL)); + mButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); + mButton->setRightHPad(6); + + mBorder->setVisible(FALSE); + + updateLayout(); +} + +//static +LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + LLString name = "flyout_button"; + node->getAttributeString("name", name); + + LLString label(""); + node->getAttributeString("label", label); + + LLRect rect; + createRect(node, rect, parent, LLRect()); + + LLUICtrlCallback callback = NULL; + + LLFlyoutButton* flyout_button = new LLFlyoutButton(name, + rect, + label, + callback, + NULL); + + LLString list_position; + node->getAttributeString("list_position", list_position); + if (list_position == "below") + { + flyout_button->mListPosition = BELOW; + } + else if (list_position == "above") + { + flyout_button->mListPosition = ABOVE; + } + + + flyout_button->initFromXML(node, parent); + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + if (child->hasName("flyout_button_item")) + { + LLString label = child->getTextContents(); + + LLString value = label; + child->getAttributeString("value", value); + + flyout_button->add(label, LLSD(value) ); + } + } + + flyout_button->updateLayout(); + + return flyout_button; +} + +void LLFlyoutButton::updateLayout() +{ + LLComboBox::updateLayout(); + + mButton->setOrigin(mRect.getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, 0); + mButton->reshape(FLYOUT_BUTTON_ARROW_WIDTH, mRect.getHeight()); + mButton->setFollows(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + mButton->setTabStop(FALSE); + mButton->setImageOverlay(mListPosition == BELOW ? "down_arrow.tga" : "up_arrow.tga", LLFontGL::RIGHT); + + mActionButton->setOrigin(0, 0); + mActionButton->reshape(mRect.getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, mRect.getHeight()); +} + +//static +void LLFlyoutButton::onActionButtonClick(void *user_data) +{ + LLFlyoutButton* buttonp = (LLFlyoutButton*)user_data; + // remember last list selection? + buttonp->mList->deselect(); + buttonp->onCommit(); +} + +void LLFlyoutButton::draw() +{ + mActionButton->setToggleState(mToggleState); + mButton->setToggleState(mToggleState); + + //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or + // the label reflects the last selected item, for now we have to manually remove the label + mButton->setLabel(LLString::null); + LLComboBox::draw(); +} + +void LLFlyoutButton::setEnabled(BOOL enabled) +{ + mActionButton->setEnabled(enabled); + LLComboBox::setEnabled(enabled); +} + + +void LLFlyoutButton::setToggleState(BOOL state) +{ + mToggleState = state; +} + diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index ff17d2874f..6e77007aef 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -80,6 +80,7 @@ public: virtual void draw(); virtual void onFocusLost(); + virtual void onLostTop(); virtual void setEnabled(BOOL enabled); @@ -107,10 +108,11 @@ public: void setAllowTextEntry(BOOL allow, S32 max_chars = 50, BOOL make_tentative = TRUE); void setTextEntry(const LLStringExplicit& text); - void add(const LLString& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu - void add(const LLString& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - void add(const LLString& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - void add(const LLString& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu + LLScrollListItem* add(const LLString& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed void removeall() { clearRows(); } @@ -119,9 +121,9 @@ public: // Select current item by name using selectSimpleItem. Returns FALSE if not found. BOOL setSimple(const LLStringExplicit& name); // Get name of current item. Returns an empty string if not found. - const LLString& getSimple() const; + const LLString getSimple() const; // Get contents of column x of selected row - const LLString& getSimpleSelectedItem(S32 column = 0) const; + const LLString getSimpleSelectedItem(S32 column = 0) const; // Sets the label, which doesn't have to exist in the label. // This is probably a UI abuse. @@ -132,6 +134,8 @@ public: BOOL setCurrentByIndex( S32 index ); S32 getCurrentIndex() const; + virtual void updateLayout(); + //======================================================================== LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }; @@ -172,7 +176,6 @@ public: static void onButtonDown(void *userdata); static void onItemSelected(LLUICtrl* item, void *userdata); - static void onListFocusChanged(LLUICtrl* item, void *userdata); static void onTextEntry(LLLineEditor* line_editor, void* user_data); static void onTextCommit(LLUICtrl* caller, void* user_data); @@ -183,12 +186,10 @@ public: protected: LLButton* mButton; LLScrollListCtrl* mList; + S32 mButtonPadding; LLViewBorder* mBorder; - BOOL mKeyboardFocusOnClick; - BOOL mDrawArrow; LLLineEditor* mTextEntry; LLPointer<LLImageGL> mArrowImage; - S32 mArrowImageWidth; BOOL mAllowTextEntry; S32 mMaxChars; BOOL mTextEntryTentative; @@ -197,4 +198,36 @@ protected: void (*mTextEntryCallback)(LLLineEditor*, void*); }; +class LLFlyoutButton : public LLComboBox +{ +public: + LLFlyoutButton( + const LLString& name, + const LLRect &rect, + const LLString& label, + void (*commit_callback)(LLUICtrl*, void*) = NULL, + void *callback_userdata = NULL); + + virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_FLYOUT_BUTTON; } + virtual LLString getWidgetTag() const { return LL_FLYOUT_BUTTON_TAG; } + + virtual void updateLayout(); + virtual void draw(); + virtual void setEnabled(BOOL enabled); + + void setToggleState(BOOL state); + + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + static void onActionButtonClick(void *userdata); + static void onSelectAction(LLUICtrl* ctrl, void *userdata); + +protected: + LLButton* mActionButton; + LLPointer<LLUIImage> mActionButtonImage; + LLPointer<LLUIImage> mExpanderButtonImage; + LLPointer<LLUIImage> mActionButtonImageSelected; + LLPointer<LLUIImage> mExpanderButtonImageSelected; + BOOL mToggleState; +}; + #endif diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index 50d199fac5..cd3ce04718 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -54,7 +54,6 @@ public: virtual void setTitle( const LLString& title ) = 0; virtual const LLString& getTitle() const = 0; - virtual void draw() = 0; virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) = 0; virtual BOOL handleHover(S32 x, S32 y, MASK mask); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 6f1c281eb2..59741a799a 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -128,7 +128,11 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; -LLFloater::LLFloater() +LLFloater::LLFloater() : + //FIXME: we should initialize *all* member variables here + mResizable(FALSE), + mDragOnLeft(FALSE) + { // automatically take focus when opened mAutoFocus = TRUE; @@ -215,9 +219,14 @@ void LLFloater::init(const LLString& title, } mButtonScale = 1.f; - LLPanel::deleteAllChildren(); + BOOL need_border = mBorder != NULL; + + // this will delete mBorder too + deleteAllChildren(); + // make sure we don't have a pointer to an old, deleted border + mBorder = NULL; //sjb: HACK! we had a border which was just deleted, so re-create it - if (mBorder != NULL) + if (need_border) { addBorder(); } @@ -609,7 +618,7 @@ void LLFloater::releaseFocus() if( gFocusMgr.childHasKeyboardFocus( this ) ) { - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } if( gFocusMgr.childHasMouseCapture( this ) ) @@ -1023,13 +1032,10 @@ void LLFloater::setHost(LLMultiFloater* host) { mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE; } - - mIsFocusRoot = FALSE; } else if (!mHostHandle.isDead() && !host) { mButtonScale = 1.f; - mIsFocusRoot = TRUE; //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; } updateButtons(); @@ -1257,6 +1263,7 @@ void LLFloater::show(LLFloater* floaterp) { if (floaterp) { + gFocusMgr.triggerFocusFlash(); floaterp->open(); if (floaterp->getHost()) { @@ -2594,9 +2601,9 @@ void LLMultiFloater::draw() for (S32 i = 0; i < mTabContainer->getTabCount(); i++) { LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i); - if (floaterp->getTitle() != mTabContainer->getPanelTitle(i)) + if (floaterp->getShortTitle() != mTabContainer->getPanelTitle(i)) { - mTabContainer->setPanelTitle(i, floaterp->getTitle()); + mTabContainer->setPanelTitle(i, floaterp->getShortTitle()); } } LLFloater::draw(); @@ -2714,7 +2721,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, if ( select_added_floater ) { - mTabContainer->selectLastTab(); + mTabContainer->selectTabPanel(floaterp); } floaterp->setHost(this); @@ -2959,8 +2966,9 @@ void LLMultiFloater::updateResizeLimits() // make sure upper left corner doesn't move translate(0, cur_height - mRect.getHeight()); - // Try to keep whole view onscreen, don't allow partial offscreen. - gFloaterView->adjustToFitScreen(this, FALSE); + // make sure this window is visible on screen when it has been modified + // (tab added, etc) + gFloaterView->adjustToFitScreen(this, TRUE); } } diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 6358ccbdd7..e3337eb588 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -42,11 +42,10 @@ LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() : mLockedView( NULL ), - mKeyboardLockedFocusLostCallback( NULL ), mMouseCaptor( NULL ), mKeyboardFocus( NULL ), + mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), - mKeyboardFocusLostCallback( NULL ), mTopCtrl( NULL ), mFocusWeight(0.f), mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true @@ -75,12 +74,11 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) if (view == mLockedView) { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; - setKeyboardFocus( NULL, NULL ); + setKeyboardFocus( NULL ); } else { - setKeyboardFocus( mLockedView, mKeyboardLockedFocusLostCallback ); + setKeyboardFocus( mLockedView ); } } @@ -91,7 +89,7 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) } -void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock) +void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock) { if (mLockedView && (new_focus == NULL || @@ -101,28 +99,27 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focu // or one of its descendants return; } - FocusLostCallback old_callback = mKeyboardFocusLostCallback; - mKeyboardFocusLostCallback = on_focus_lost; //llinfos << "Keyboard focus handled by " << (new_focus ? new_focus->getName() : "nothing") << llendl; if( new_focus != mKeyboardFocus ) { - LLUICtrl* old_focus = mKeyboardFocus; + mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; + if( mLastKeyboardFocus ) + { + mLastKeyboardFocus->onFocusLost(); + } + // clear out any existing flash if (new_focus) { mFocusWeight = 0.f; + new_focus->onFocusReceived(); } mFocusTimer.reset(); - if( old_callback ) - { - old_callback( old_focus ); - } - #ifdef _DEBUG mKeyboardFocusName = new_focus ? new_focus->getName() : "none"; #endif @@ -204,13 +201,11 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( LLView* focus ) if (focus == mLockedView) { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; } if( mKeyboardFocus == focus ) { mKeyboardFocus = NULL; - mKeyboardFocusLostCallback = NULL; #ifdef _DEBUG mKeyboardFocusName = "none"; #endif @@ -293,13 +288,19 @@ BOOL LLFocusMgr::childIsTopCtrl( LLView* parent ) // set new_top = NULL to release top_view. void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { - if( new_top != mTopCtrl ) + LLUICtrl* old_top = mTopCtrl; + if( new_top != old_top ) { mTopCtrl = new_top; #ifdef _DEBUG mTopCtrlName = new_top ? new_top->getName() : "none"; #endif + + if (old_top) + { + old_top->onLostTop(); + } } } @@ -317,13 +318,11 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( LLUICtrl* top_view ) void LLFocusMgr::lockFocus() { mLockedView = mKeyboardFocus; - mKeyboardLockedFocusLostCallback = mKeyboardFocusLostCallback; } void LLFocusMgr::unlockFocus() { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; } F32 LLFocusMgr::getFocusFlashAmt() @@ -356,9 +355,9 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) } // release focus from "top ctrl"s, which generally hides them - if (!focus && mTopCtrl && mTopCtrl->hasFocus()) + if (!focus && mTopCtrl) { - mTopCtrl->setFocus(FALSE); + setTopCtrl(NULL); } mAppHasFocus = focus; } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 1c25153fbe..20dc21fc3a 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -44,8 +44,6 @@ class LLMouseHandler; class LLFocusMgr { public: - typedef void (*FocusLostCallback)(LLUICtrl*); - LLFocusMgr(); ~LLFocusMgr(); @@ -56,11 +54,11 @@ public: BOOL childHasMouseCapture( LLView* parent ); // Keyboard Focus - void setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock = FALSE); // new_focus = NULL to release the focus. + void setKeyboardFocus(LLUICtrl* new_focus, BOOL lock = FALSE); // new_focus = NULL to release the focus. LLUICtrl* getKeyboardFocus() const { return mKeyboardFocus; } + LLUICtrl* getLastKeyboardFocus() const { return mLastKeyboardFocus; } BOOL childHasKeyboardFocus( const LLView* parent ) const; void removeKeyboardFocusWithoutCallback( LLView* focus ); - FocusLostCallback getFocusCallback() { return mKeyboardFocusLostCallback; } F32 getFocusTime() const { return mFocusTimer.getElapsedTimeF32(); } F32 getFocusFlashAmt(); LLColor4 getFocusColor(); @@ -90,15 +88,14 @@ public: protected: LLUICtrl* mLockedView; - FocusLostCallback mKeyboardLockedFocusLostCallback; // Mouse Captor LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object // Keyboard Focus LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object + LLUICtrl* mLastKeyboardFocus; // who last had focus LLUICtrl* mDefaultKeyboardFocus; - FocusLostCallback mKeyboardFocusLostCallback; // The object to which keyboard events are routed is called before another object takes its place // Top View LLUICtrl* mTopCtrl; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 1e49210565..a063ebcd25 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -88,18 +88,11 @@ void LLIconCtrl::draw() { if( getVisible() ) { - // Border - BOOL has_image = !mImageID.isNull(); - - if( has_image ) + if( mImagep.notNull() ) { - if( mImagep.notNull() ) - { - gl_draw_scaled_image(0, 0, - mRect.getWidth(), mRect.getHeight(), - mImagep, - mColor ); - } + mImagep->draw(0, 0, + mRect.getWidth(), mRect.getHeight(), + mColor ); } LLUICtrl::draw(); @@ -154,6 +147,7 @@ LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * LLUICtrlFactory::getAttributeColor(node,"color", color); LLIconCtrl* icon = new LLIconCtrl(name, rect, image_id); + icon->setColor(color); icon->initFromXML(node, parent); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index b789269558..1e474d0935 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -73,7 +73,7 @@ protected: LLColor4 mColor; LLString mImageName; LLUUID mImageID; - LLPointer<LLImageGL> mImagep; + LLPointer<LLUIImage> mImagep; }; #endif diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 3c7cd17b92..4297f5fef8 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -128,7 +128,7 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect, S32 max_length_bytes, void (*commit_callback)(LLUICtrl* caller, void* user_data ), void (*keystroke_callback)(LLLineEditor* caller, void* user_data ), - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data ), + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data ), void* userdata, LLLinePrevalidateFunc prevalidate_func, LLViewBorder::EBevel border_bevel, @@ -351,10 +351,14 @@ void LLLineEditor::setText(const LLStringExplicit &new_text) // Check to see if entire field is selected. S32 len = mText.length(); - BOOL allSelected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len ) - || ( mSelectionStart == len && mSelectionEnd == 0 )); + BOOL all_selected = (len > 0) + && (( mSelectionStart == 0 && mSelectionEnd == len ) + || ( mSelectionStart == len && mSelectionEnd == 0 )); // Do safe truncation so we don't split multi-byte characters + // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor + all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); + LLString truncated_utf8 = new_text; if (truncated_utf8.size() > (U32)mMaxLengthBytes) { @@ -362,7 +366,7 @@ void LLLineEditor::setText(const LLStringExplicit &new_text) } mText.assign(truncated_utf8); - if (allSelected) + if (all_selected) { // ...keep whole thing selected selectAll(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index a019353856..0739315c4d 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -77,7 +77,7 @@ public: S32 max_length_bytes = 254, void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL, void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL, - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data) = NULL, + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data) = NULL, void* userdata = NULL, LLLinePrevalidateFunc prevalidate_func = NULL, LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN, diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 46f9f515d7..19a5085a25 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -4514,7 +4514,7 @@ BOOL LLMenuHolderGL::hideMenus() } //if (gFocusMgr.childHasKeyboardFocus(this)) //{ - // gFocusMgr.setKeyboardFocus(NULL, NULL); + // gFocusMgr.setKeyboardFocus(NULL); //} return menu_visible; @@ -4599,6 +4599,7 @@ void LLTearOffMenu::onFocusReceived() break; } } + LLFloater::onFocusReceived(); } void LLTearOffMenu::onFocusLost() diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index b8b8bf9443..ca8020fe70 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -287,7 +287,7 @@ void LLModalDialog::onAppFocusLost() if( gFocusMgr.childHasKeyboardFocus( instance ) ) { - gFocusMgr.setKeyboardFocus( NULL, NULL ); + gFocusMgr.setKeyboardFocus( NULL ); } } } diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 6d000f3e7f..294ce5df18 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -64,7 +64,6 @@ LLPanel::panel_map_t LLPanel::sPanelMap; LLPanel::alert_queue_t LLPanel::sAlertQueue; const S32 RESIZE_BAR_OVERLAP = 1; -const S32 PANEL_STACK_GAP = RESIZE_BAR_HEIGHT; void LLPanel::init() { @@ -88,6 +87,7 @@ LLPanel::LLPanel() : mRectControl() { init(); + setName("panel"); } LLPanel::LLPanel(const LLString& name) @@ -124,6 +124,7 @@ LLPanel::LLPanel(const LLString& name, const LLString& rect_control, BOOL border void LLPanel::addBorder(LLViewBorder::EBevel border_bevel, LLViewBorder::EStyle border_style, S32 border_thickness) { + removeBorder(); mBorder = new LLViewBorder( "panel border", LLRect(0, mRect.getHeight(), mRect.getWidth(), 0), border_bevel, border_style, border_thickness ); @@ -361,12 +362,6 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) { BOOL handled = FALSE; - if( getVisible() && getEnabled() && gFocusMgr.childHasKeyboardFocus(this) && KEY_ESCAPE == key ) - { - gFocusMgr.setKeyboardFocus(NULL, NULL); - return TRUE; - } - if( getVisible() && getEnabled() && gFocusMgr.childHasKeyboardFocus(this) && !called_from_parent ) { @@ -472,7 +467,7 @@ void LLPanel::setFocus(BOOL b) { if( this == gFocusMgr.getKeyboardFocus() ) { - gFocusMgr.setKeyboardFocus( NULL, NULL ); + gFocusMgr.setKeyboardFocus( NULL ); } else { @@ -595,7 +590,8 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac { LLRect rect; createRect(node, rect, parent, LLRect()); - panelp = new LLPanel(name, rect); + // create a new panel without a border, by default + panelp = new LLPanel(name, rect, FALSE); panelp->initPanelXML(node, parent, factory); // preserve panel's width and height, but override the location const LLRect& panelrect = panelp->getRect(); @@ -608,12 +604,13 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac { panelp->initPanelXML(node, parent, factory); } + return panelp; } BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - LLString name("panel"); + LLString name = getName(); node->getAttributeString("name", name); setName(name); @@ -628,13 +625,15 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f if (!xml_filename.empty()) { - // Preserve postion of embedded panel but allow panel to dictate width/height - LLRect rect(getRect()); didPost = factory->buildPanel(this, xml_filename, NULL); - S32 w = getRect().getWidth(); - S32 h = getRect().getHeight(); - rect.setLeftTopAndSize(rect.mLeft, rect.mTop, w, h); - setRect(rect); + + LLRect new_rect = getRect(); + // override rectangle with embedding parameters as provided + createRect(node, new_rect, parent); + setOrigin(new_rect.mLeft, new_rect.mBottom); + reshape(new_rect.getWidth(), new_rect.getHeight()); + // optionally override follows flags from including nodes + parseFollowsFlags(node); } else { @@ -678,7 +677,7 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parent) initFromXML(node, parent); /////// Border attributes /////// - BOOL border = FALSE; + BOOL border = mBorder != NULL; node->getAttributeBOOL("border", border); if (border) { @@ -706,24 +705,24 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parent) } /////// Background attributes /////// - BOOL background_visible = FALSE; + BOOL background_visible = mBgVisible; node->getAttributeBOOL("background_visible", background_visible); setBackgroundVisible(background_visible); - BOOL background_opaque = FALSE; + BOOL background_opaque = mBgOpaque; node->getAttributeBOOL("background_opaque", background_opaque); setBackgroundOpaque(background_opaque); LLColor4 color; - color = LLUI::sColorsGroup->getColor( "FocusBackgroundColor" ); + color = mBgColorOpaque; LLUICtrlFactory::getAttributeColor(node,"bg_opaque_color", color); setBackgroundColor(color); - color = LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" ); + color = mBgColorAlpha; LLUICtrlFactory::getAttributeColor(node,"bg_alpha_color", color); setTransparentColor(color); - LLString label; + LLString label = getLabel(); node->getAttributeString("label", label); setLabel(label); } @@ -853,12 +852,12 @@ BOOL LLPanel::childHasFocus(const LLString& id) } -void LLPanel::childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*)) +void LLPanel::childSetFocusChangedCallback(const LLString& id, void (*cb)(LLFocusableElement*, void*), void* user_data) { LLUICtrl* child = (LLUICtrl*)getChildByName(id, true); if (child) { - child->setFocusChangedCallback(cb); + child->setFocusChangedCallback(cb, user_data); } } @@ -1165,11 +1164,12 @@ void LLPanel::storeRectControl() // struct LLLayoutStack::LLEmbeddedPanel { - LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize) : + LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp), mMinWidth(min_width), mMinHeight(min_height), mAutoResize(auto_resize), + mUserResize(user_resize), mOrientation(orientation), mVisibleAmt(1.f) // default to fully visible { @@ -1205,6 +1205,7 @@ struct LLLayoutStack::LLEmbeddedPanel S32 mMinWidth; S32 mMinHeight; BOOL mAutoResize; + BOOL mUserResize; LLResizeBar* mResizeBar; eLayoutOrientation mOrientation; F32 mVisibleAmt; @@ -1213,7 +1214,8 @@ struct LLLayoutStack::LLEmbeddedPanel LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : mOrientation(orientation), mMinWidth(0), - mMinHeight(0) + mMinHeight(0), + mPanelSpacing(RESIZE_BAR_HEIGHT) { } @@ -1226,24 +1228,26 @@ void LLLayoutStack::draw() { updateLayout(); { - // clip if outside nominal bounds - LLLocalClipRect clip(getLocalRect(), mRect.getWidth() > mMinWidth || mRect.getHeight() > mMinHeight); e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { + // clip to layout rectangle, not bounding rectangle LLRect clip_rect = (*panel_it)->mPanel->getRect(); // scale clipping rectangle by visible amount if (mOrientation == HORIZONTAL) { - clip_rect.mRight = clip_rect.mLeft + llround(clip_rect.getWidth() * (*panel_it)->mVisibleAmt); + clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->mVisibleAmt); } else { - clip_rect.mBottom = clip_rect.mTop - llround(clip_rect.getHeight() * (*panel_it)->mVisibleAmt); + clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->mVisibleAmt); } - LLLocalClipRect clip(clip_rect, (*panel_it)->mVisibleAmt < 1.f); + + LLPanel* panelp = (*panel_it)->mPanel; + + LLLocalClipRect clip(clip_rect); // only force drawing invisible children if visible amount is non-zero - drawChild((*panel_it)->mPanel, 0, 0, (*panel_it)->mVisibleAmt > 0.f); + drawChild(panelp, 0, 0, !clip_rect.isNull()); } } } @@ -1258,17 +1262,13 @@ void LLLayoutStack::removeCtrl(LLUICtrl* ctrl) delete embedded_panelp; } + // need to update resizebars + calcMinExtents(); LLView::removeCtrl(ctrl); } -void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLView::reshape(width, height, called_from_parent); - //updateLayout(); -} - LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const { LLXMLNodePtr node = LLView::getXML(); @@ -1298,6 +1298,14 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor LLLayoutStack* layout_stackp = new LLLayoutStack(orientation); + node->getAttributeS32("border_size", layout_stackp->mPanelSpacing); + // don't allow negative spacing values + layout_stackp->mPanelSpacing = llmax(layout_stackp->mPanelSpacing, 0); + + LLString name("stack"); + node->getAttributeString("name", name); + + layout_stackp->setName(name); layout_stackp->initFromXML(node, parent); LLXMLNodePtr child; @@ -1308,16 +1316,18 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor S32 min_width = 0; S32 min_height = 0; BOOL auto_resize = TRUE; + BOOL user_resize = TRUE; child->getAttributeS32("min_width", min_width); child->getAttributeS32("min_height", min_height); child->getAttributeBOOL("auto_resize", auto_resize); + child->getAttributeBOOL("user_resize", user_resize); LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory); if (panelp) { panelp->setFollowsNone(); - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize); + layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); } } } @@ -1335,11 +1345,36 @@ S32 LLLayoutStack::getMinHeight() return mMinHeight; } -void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index) +S32 LLLayoutStack::getDefaultHeight(S32 cur_height) { - LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize); + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == HORIZONTAL) + { + cur_height = llmax(mMinHeight, mRect.getHeight()); + } + + return cur_height; +} + +S32 LLLayoutStack::getDefaultWidth(S32 cur_width) +{ + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == VERTICAL) + { + cur_width = llmax(mMinWidth, mRect.getWidth()); + } + + return cur_width; +} + +void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, S32 index) +{ + LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); + addChild(panel); addChild(embedded_panel->mResizeBar); @@ -1347,29 +1382,15 @@ void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL // with a bit of overlap for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - e_panel_list_t::iterator next_it = panel_it; - ++next_it; - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; sendChildToFront(resize_barp); - // last resize bar is disabled, since its not between any two panels - if ( next_it == mPanels.end() ) - { - resize_barp->setEnabled(FALSE); - } - else - { - resize_barp->setEnabled(TRUE); - } } - //updateLayout(); } void LLLayoutStack::removePanel(LLPanel* panel) { removeChild(panel); - //updateLayout(); } void LLLayoutStack::updateLayout(BOOL force_resize) @@ -1377,11 +1398,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) calcMinExtents(); // calculate current extents - S32 cur_width = 0; - S32 cur_height = 0; + S32 total_width = 0; + S32 total_height = 0; const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.02f; + const F32 ANIM_CLOSE_TIME = 0.03f; e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) @@ -1403,23 +1424,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) (*panel_it)->mVisibleAmt = 0.f; } } + if (mOrientation == HORIZONTAL) { - // all panels get expanded to max of all the minimum dimensions - cur_height = llmax(mMinHeight, panelp->getRect().getHeight()); - cur_width += llround(panelp->getRect().getWidth() * (*panel_it)->mVisibleAmt); - if (panel_it != mPanels.end()) + total_width += llround(panelp->getRect().getWidth() * (*panel_it)->mVisibleAmt); + // want n-1 panel gaps for n panels + if (panel_it != mPanels.begin()) { - cur_width += PANEL_STACK_GAP; + total_width += mPanelSpacing; } } else //VERTICAL { - cur_width = llmax(mMinWidth, panelp->getRect().getWidth()); - cur_height += llround(panelp->getRect().getHeight() * (*panel_it)->mVisibleAmt); - if (panel_it != mPanels.end()) + total_height += llround(panelp->getRect().getHeight() * (*panel_it)->mVisibleAmt); + if (panel_it != mPanels.begin()) { - cur_height += PANEL_STACK_GAP; + total_height += mPanelSpacing; } } } @@ -1465,11 +1485,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 pixels_to_distribute; if (mOrientation == HORIZONTAL) { - pixels_to_distribute = mRect.getWidth() - cur_width; + pixels_to_distribute = mRect.getWidth() - total_width; } else //VERTICAL { - pixels_to_distribute = mRect.getHeight() - cur_height; + pixels_to_distribute = mRect.getHeight() - total_height; } S32 cur_x = 0; @@ -1482,7 +1502,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 cur_width = panelp->getRect().getWidth(); S32 cur_height = panelp->getRect().getHeight(); S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); - S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); + S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); S32 delta_size = 0; @@ -1502,11 +1522,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) // grow all elements equally delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); } - new_width = llmax((*panel_it)->mMinWidth, panelp->getRect().getWidth() + delta_size); + new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); } else { - new_width = llmax(mMinWidth, mRect.getWidth()); + new_width = getDefaultWidth(new_width); } if (mOrientation == VERTICAL) @@ -1520,22 +1540,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); } - new_height = llmax((*panel_it)->mMinHeight, panelp->getRect().getHeight() + delta_size); + new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); } else { - new_height = llmax(mMinHeight, mRect.getHeight()); + new_height = getDefaultHeight(new_height); } } - else // don't resize + else { if (mOrientation == HORIZONTAL) { - new_height = llmax(mMinHeight, mRect.getHeight()); + new_height = getDefaultHeight(new_height); } else // VERTICAL { - new_width = llmax(mMinWidth, mRect.getWidth()); + new_width = getDefaultWidth(new_width); } } @@ -1550,22 +1570,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if (mOrientation == HORIZONTAL) { resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP; - resize_bar_rect.mRight = panel_rect.mRight + PANEL_STACK_GAP + RESIZE_BAR_OVERLAP; + resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + RESIZE_BAR_OVERLAP; } else { resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP; - resize_bar_rect.mBottom = panel_rect.mBottom - PANEL_STACK_GAP - RESIZE_BAR_OVERLAP; + resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - RESIZE_BAR_OVERLAP; } (*panel_it)->mResizeBar->setRect(resize_bar_rect); if (mOrientation == HORIZONTAL) { - cur_x += llround(new_width * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP; + cur_x += llround(new_width * (*panel_it)->mVisibleAmt) + mPanelSpacing; } else //VERTICAL { - cur_y -= llround(new_height * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP; + cur_y -= llround(new_height * (*panel_it)->mVisibleAmt) + mPanelSpacing; } } @@ -1577,29 +1597,38 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if (mOrientation == HORIZONTAL) { - (*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinWidth, (*panel_it)->mMinWidth + shrink_headroom_total); + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinWidth, + (*panel_it)->mMinWidth + shrink_headroom_total); } else //VERTICAL { - (*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinHeight, (*panel_it)->mMinHeight + shrink_headroom_total); + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinHeight, + (*panel_it)->mMinHeight + shrink_headroom_total); } - // hide resize bars for invisible panels - (*panel_it)->mResizeBar->setVisible(panelp->getVisible()); - if (panelp->getVisible()) + + // toggle resize bars based on panel visibility, resizability, etc + BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; + (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); + + if (resize_bar_enabled) { last_resize_bar = (*panel_it)->mResizeBar; } } // hide last resize bar as there is nothing past it + // resize bars need to be in between two resizable panels if (last_resize_bar) { last_resize_bar->setVisible(FALSE); } // not enough room to fit existing contents - if (!force_resize && - ((cur_y != -PANEL_STACK_GAP) || (cur_x != mRect.getWidth() + PANEL_STACK_GAP))) + if (!force_resize + && ((cur_y != -mPanelSpacing) + || (cur_x != mRect.getWidth() + mPanelSpacing))) { // do another layout pass with all stacked elements contributing // even those that don't usually resize @@ -1631,20 +1660,22 @@ void LLLayoutStack::calcMinExtents() { if (mOrientation == HORIZONTAL) { - mMinHeight = llmax(mMinHeight, (*panel_it)->mMinHeight); + mMinHeight = llmax( mMinHeight, + (*panel_it)->mMinHeight); mMinWidth += (*panel_it)->mMinWidth; if (panel_it != mPanels.begin()) { - mMinWidth += PANEL_STACK_GAP; + mMinWidth += mPanelSpacing; } } else //VERTICAL { - mMinWidth = llmax(mMinWidth, (*panel_it)->mMinWidth); + mMinWidth = llmax( mMinWidth, + (*panel_it)->mMinWidth); mMinHeight += (*panel_it)->mMinHeight; if (panel_it != mPanels.begin()) { - mMinHeight += PANEL_STACK_GAP; + mMinHeight += mPanelSpacing; } } } diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 78aa7cfc21..88b4ecb76b 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -173,7 +173,7 @@ public: // LLUICtrl void childSetFocus(const LLString& id, BOOL focus = TRUE); BOOL childHasFocus(const LLString& id); - void childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*)); + void childSetFocusChangedCallback(const LLString& id, void (*cb)(LLFocusableElement*, void*), void* user_data = NULL); void childSetCommitCallback(const LLString& id, void (*cb)(LLUICtrl*, void*), void* userdata = NULL ); void childSetDoubleClickCallback(const LLString& id, void (*cb)(void*), void* userdata = NULL ); @@ -277,9 +277,9 @@ public: virtual ~LLLayoutStack(); /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; /*virtual*/ void removeCtrl(LLUICtrl* ctrl); + virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LAYOUT_STACK; } virtual LLString getWidgetTag() const { return LL_LAYOUT_STACK_TAG; } @@ -288,7 +288,7 @@ public: S32 getMinWidth(); S32 getMinHeight(); - void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index = S32_MAX); + void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, S32 index = S32_MAX); void removePanel(LLPanel* panel); void updateLayout(BOOL force_resize = FALSE); @@ -299,6 +299,8 @@ protected: void calcMinExtents(); S32 getMinStackSize(); S32 getCurStackSize(); + S32 getDefaultHeight(S32 cur_height); + S32 getDefaultWidth(S32 cur_width); protected: eLayoutOrientation mOrientation; @@ -308,6 +310,7 @@ protected: S32 mMinWidth; S32 mMinHeight; + S32 mPanelSpacing; }; #endif diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 17b76def71..120323e7d1 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -60,7 +60,7 @@ LLResizeHandle::LLResizeHandle( const LLString& name, const LLRect& rect, S32 mi if( RIGHT_BOTTOM == mCorner) { LLUUID image_id(LLUI::sConfigGroup->getString("UIImgResizeBottomRightUUID")); - mImage = LLUI::sImageProvider->getUIImageByID(image_id); + mImage = LLUI::sImageProvider->getImageByID(image_id); } switch( mCorner ) diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 99908a6bc0..b106bb570d 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -159,30 +159,50 @@ void LLScrollbar::setDocParams( S32 size, S32 pos ) void LLScrollbar::setDocPos(S32 pos) { - mDocPos = llclamp( pos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (pos != mDocPos) + { + mDocPos = llclamp( pos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } } void LLScrollbar::setDocSize(S32 size) { - mDocSize = size; - mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (size != mDocSize) + { + mDocSize = size; + mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } } void LLScrollbar::setPageSize( S32 page_size ) { - mPageSize = page_size; - mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (page_size != mPageSize) + { + mPageSize = page_size; + mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } +} + +BOOL LLScrollbar::isAtBeginning() +{ + return mDocPos == 0; +} + +BOOL LLScrollbar::isAtEnd() +{ + return mDocPos == getDocPosMax(); } + void LLScrollbar::updateThumbRect() { // llassert( 0 <= mDocSize ); @@ -479,7 +499,7 @@ void LLScrollbar::draw() // Draw background and thumb. LLUUID rounded_rect_image_id; rounded_rect_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga")); - LLImageGL* rounded_rect_imagep = LLUI::sImageProvider->getUIImageByID(rounded_rect_image_id); + LLImageGL* rounded_rect_imagep = LLUI::sImageProvider->getImageByID(rounded_rect_image_id); if (!rounded_rect_imagep) { diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 353935cfb8..50aa3cafe9 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -88,6 +88,9 @@ public: void setDocPos( S32 pos ); S32 getDocPos() { return mDocPos; } + BOOL isAtBeginning(); + BOOL isAtEnd(); + // How many "lines" of the "document" is can appear on a page. void setPageSize( S32 page_size ); S32 getPageSize() { return mPageSize; } diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 8b5d009b95..34a29cef51 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -394,33 +394,29 @@ BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect) { - if( getVisible() && pointInView(x,y) ) + S32 local_x, local_y; + for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) { - S32 local_x, local_y; - for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) + local_x = x - mScrollbar[i]->getRect().mLeft; + local_y = y - mScrollbar[i]->getRect().mBottom; + if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) ) { - local_x = x - mScrollbar[i]->getRect().mLeft; - local_y = y - mScrollbar[i]->getRect().mBottom; - if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) ) - { - return TRUE; - } + return TRUE; } - // Handle 'child' view. - if( mScrolledView ) + } + // Handle 'child' view. + if( mScrolledView ) + { + local_x = x - mScrolledView->getRect().mLeft; + local_y = y - mScrolledView->getRect().mBottom; + if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) ) { - local_x = x - mScrolledView->getRect().mLeft; - local_y = y - mScrolledView->getRect().mBottom; - if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) ) - { - return TRUE; - } + return TRUE; } - - // Opaque - return TRUE; } - return FALSE; + + // Opaque + return TRUE; } void LLScrollableContainerView::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 96a739418f..0c81b2da08 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -61,43 +61,55 @@ const S32 LIST_SNAP_PADDING = 5; // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const S32 sort_col, BOOL sort_ascending) - { - mSortCol = sort_col; - mSortAscending = sort_ascending; - } + SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders) + : mSortOrders(sort_orders) + {} bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) { - const LLScrollListCell *cell1; - const LLScrollListCell *cell2; - - cell1 = i1->getColumn(mSortCol); - cell2 = i2->getColumn(mSortCol); + if ( mSortOrders.empty() ) return true; + + const LLScrollListCell *cell1 = NULL; + const LLScrollListCell *cell2 = NULL; - S32 order = 1; - if (!mSortAscending) + sort_order_t::const_reverse_iterator end_it = mSortOrders.rend(); + sort_order_t::const_reverse_iterator it; + + // sort over all columns in order specified by mSortOrders + S32 sort_result = 0; + for (it = mSortOrders.rbegin(); it != end_it; ++it) { - order = -1; - } + S32 col_idx = it->first; + BOOL sort_ascending = it->second; - BOOL retval = FALSE; + cell1 = i1->getColumn(col_idx); + cell2 = i2->getColumn(col_idx); + // ascending or descending sort for this column? + S32 order = 1; + if (!sort_ascending) + { + order = -1; + } - if (cell1 && cell2) - { - retval = ((order * LLString::compareDict(cell1->getText(), cell2->getText())) < 0); + if (cell1 && cell2) + { + sort_result = (order * LLString::compareDict(cell1->getValue().asString(), cell2->getValue().asString())); + if (sort_result != 0) + { + // we have a sort order! + break; + } + } } - return (retval ? TRUE : FALSE); + return sort_result < 0; } -protected: - S32 mSortCol; - S32 mSortAscending; + typedef std::vector<std::pair<S32, BOOL> > sort_order_t; + const sort_order_t& mSortOrders; }; - // // LLScrollListIcon // @@ -120,6 +132,14 @@ LLScrollListIcon::~LLScrollListIcon() { } +void LLScrollListIcon::setValue(LLSD value) +{ + mImageUUID = value.asUUID(); + // don't use default image specified by LLUUID::null, use no image in that case + mIcon = mImageUUID.isNull() ? NULL : LLUI::sImageProvider->getImageByID(value.asUUID()); +} + + void LLScrollListIcon::setColor(const LLColor4& color) { mColor = color; @@ -127,7 +147,10 @@ void LLScrollListIcon::setColor(const LLColor4& color) void LLScrollListIcon::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const { - gl_draw_image(0, 0, mIcon, mColor); + if (mIcon) + { + gl_draw_image(0, 0, mIcon, mColor); + } } // @@ -158,15 +181,15 @@ LLScrollListCheck::~LLScrollListCheck() void LLScrollListCheck::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const { mCheckBox->draw(); - } BOOL LLScrollListCheck::handleClick() { - if ( mCheckBox->getEnabled() ) + if (mCheckBox->getEnabled()) { - LLCheckBoxCtrl::onButtonPress(mCheckBox); + mCheckBox->toggle(); } + // don't change selection when clicking on embedded checkbox return TRUE; } @@ -213,7 +236,7 @@ LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, // initialize rounded rect image if (!mRoundedRectImage) { - mRoundedRectImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); + mRoundedRectImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); } } @@ -223,6 +246,12 @@ LLScrollListText::~LLScrollListText() delete mColor; } +S32 LLScrollListText::getContentWidth() const +{ + return mFont->getWidth(mText.getString()); +} + + void LLScrollListText::setColor(const LLColor4& color) { if (!mColor) @@ -314,31 +343,6 @@ LLScrollListItem::~LLScrollListItem() std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); } -BOOL LLScrollListItem::handleClick(S32 x, S32 y, MASK mask) -{ - BOOL handled = FALSE; - - S32 left = 0; - S32 right = 0; - S32 width = 0; - - std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); - std::vector<LLScrollListCell *>::iterator end = mColumns.end(); - for ( ; iter != end; ++iter) - { - width = (*iter)->getWidth(); - right += width; - if (left <= x && x < right ) - { - handled = (*iter)->handleClick(); - break; - } - - left += width; - } - return handled; -} - void LLScrollListItem::setNumColumns(S32 columns) { S32 prev_columns = mColumns.size(); @@ -375,7 +379,7 @@ LLString LLScrollListItem::getContentsCSV() S32 count = getNumColumns(); for (S32 i=0; i<count; ++i) { - ret += getColumn(i)->getText(); + ret += getColumn(i)->getValue().asString(); if (i < count-1) { ret += ", "; @@ -387,16 +391,7 @@ LLString LLScrollListItem::getContentsCSV() void LLScrollListItem::setEnabled(BOOL b) { - if (b != mEnabled) - { - std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); - std::vector<LLScrollListCell *>::iterator end = mColumns.end(); - for ( ; iter != end; ++iter) - { - (*iter)->setEnabled(b); - } - mEnabled = b; - } + mEnabled = b; } //--------------------------------------------------------------------------- @@ -424,9 +419,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mCanSelect(TRUE), mDisplayColumnHeaders(FALSE), mCollapseEmptyColumns(FALSE), - mIsPopup(FALSE), mMaxItemCount(INT_MAX), - //mItemCount(0), + mMaxContentWidth(0), mBackgroundVisible( TRUE ), mDrawStripes(TRUE), mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ), @@ -443,12 +437,13 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mOnSortChangedCallback( NULL ), mHighlightedItem(-1), mBorder(NULL), - mDefaultColumn("SIMPLE"), + mDefaultColumnName("SIMPLE"), mSearchColumn(0), mNumDynamicWidthColumns(0), mTotalStaticColumnWidth(0), - mSortColumn(-1), mSortAscending(TRUE), + mSecondarySortColumn(-1), + mSecondarySortAscending(TRUE), mSorted(TRUE), mDirty(FALSE), mOriginalSelection(-1), @@ -457,7 +452,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, mBorderThickness + LIST_BORDER_PAD, - mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, + mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) ); updateLineHeight(); @@ -481,7 +476,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); mScrollbar->setEnabled( TRUE ); - mScrollbar->setVisible( TRUE ); + // scrollbar is visible only when needed + mScrollbar->setVisible(FALSE); addChild(mScrollbar); // Border @@ -539,7 +535,8 @@ void LLScrollListCtrl::clearRows() mScrollLines = 0; mLastSelected = NULL; - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); + updateLayout(); mDirty = FALSE; } @@ -620,38 +617,64 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const return ret; } +// returns first matching item +LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const +{ + LLString string_val = sd.asString(); + + item_list::const_iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // assumes string representation is good enough for comparison + if (item->getValue().asString() == string_val) + { + return item; + } + } + return NULL; +} + void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { - S32 old_height = mRect.getHeight(); LLUICtrl::reshape( width, height, called_from_parent ); - S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + updateLayout(); +} +void LLScrollListCtrl::updateLayout() +{ + // reserve room for column headers, if needed + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, mBorderThickness + LIST_BORDER_PAD, - mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, + mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size ); + // how many lines of content in a single "page" mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0; - if(old_height < height && getScrollPos() == mScrollbar->getDocPosMax()) + BOOL scrollbar_visible = getItemCount() > mPageLines; + if (scrollbar_visible) { - setScrollPos(mScrollbar->getDocPosMax()); + // provide space on the right for scrollbar + mItemListRect.mRight = mRect.getWidth() - ( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE; } - mScrollbar->setVisible(mPageLines < getItemCount()); + + mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); mScrollbar->setPageSize( mPageLines ); - + mScrollbar->setDocSize( getItemCount() ); + mScrollbar->setVisible(scrollbar_visible); + updateColumns(); } // Attempt to size the control to show all items. // Do not make larger than width or height. -void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) +void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) { - S32 height = mLineHeight * (getItemCount() + 1); - height = llmin( height, max_height ); - + S32 height = llmin( getRequiredRect().getHeight(), max_height ); S32 width = mRect.getWidth(); reshape( width, height ); @@ -660,7 +683,10 @@ void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) LLRect LLScrollListCtrl::getRequiredRect() { - S32 height = mLineHeight * (getItemCount() + 1); + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + S32 height = (mLineHeight * getItemCount()) + + (2 * ( mBorderThickness + LIST_BORDER_PAD )) + + heading_size; S32 width = mRect.getWidth(); return LLRect(0, height, width, 0); @@ -680,15 +706,22 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) break; case ADD_SORTED: - if (mSortColumn == -1) { - mSortColumn = 0; - mSortAscending = TRUE; - } - mItemList.push_back(item); - std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); - break; - + // sort by column 0, in ascending order + std::vector<sort_column_t> single_sort_column; + single_sort_column.push_back(std::make_pair(0, TRUE)); + + mItemList.push_back(item); + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(single_sort_column)); + + // ADD_SORTED just sorts by first column... + // this might not match user sort criteria, so flag list as being in unsorted state + setSorted(FALSE); + break; + } case ADD_BOTTOM: mItemList.push_back(item); setSorted(FALSE); @@ -702,33 +735,31 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) } updateLineHeightInsert(item); - mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; - BOOL scrollbar_visible = mPageLines < getItemCount(); - - if (scrollbar_visible != mScrollbar->getVisible()) - { - mScrollbar->setVisible(mPageLines < getItemCount()); - updateColumns(); - } - mScrollbar->setPageSize( mPageLines ); - - mScrollbar->setDocSize( getItemCount() ); + calcMaxContentWidth(item); - updateMaxContentWidth(item); + updateLayout(); } return not_too_big; } -void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) +void LLScrollListCtrl::calcMaxContentWidth(LLScrollListItem* added_item) { const S32 HEADING_TEXT_PADDING = 30; const S32 COLUMN_TEXT_PADDING = 20; - std::map<LLString, LLScrollListColumn>::iterator column_itor; - for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) + if (added_item == NULL) + { + mMaxContentWidth = 0; + } + + S32 item_content_width = 0; + + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) { - LLScrollListColumn* column = &column_itor->second; + LLScrollListColumn* column = *column_itor; + if (!column) continue; if (!added_item) { @@ -740,7 +771,7 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); if (!cellp) continue; - column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); } } else @@ -748,9 +779,13 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) LLScrollListCell* cellp = added_item->getColumn(column->mIndex); if (!cellp) continue; - column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); } + + item_content_width += column->mMaxContentWidth; } + + mMaxContentWidth = llmax(mMaxContentWidth, item_content_width); } const S32 SCROLL_LIST_ROW_PAD = 2; @@ -789,7 +824,6 @@ void LLScrollListCtrl::updateColumns() mColumnsIndexed.resize(mColumns.size()); std::map<LLString, LLScrollListColumn>::iterator column_itor; - bool first_dynamic = true; for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { LLScrollListColumn *column = &column_itor->second; @@ -801,11 +835,6 @@ void LLScrollListCtrl::updateColumns() else if (column->mDynamicWidth) { new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; - if(first_dynamic) - { - first_dynamic = false; - new_width += (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); - } } if (new_width != column->mWidth) @@ -854,43 +883,38 @@ void LLScrollListCtrl::updateColumns() } right = llmax(left, llmin(mItemListRect.getWidth(), right)); S32 header_width = right - left; - + last_header->reshape(header_width, mHeadingHeight); - last_header->translate(left - last_header->getRect().mLeft, top - last_header->getRect().mBottom); + last_header->translate( + left - last_header->getRect().mLeft, + top - last_header->getRect().mBottom); last_header->setVisible(mDisplayColumnHeaders && header_width > 0); left = right; } } // expand last column header we encountered to full list width - if (last_header) { - S32 header_strip_width = mItemListRect.getWidth() + (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); - S32 new_width = llmax(0, mItemListRect.mLeft + header_strip_width - last_header->getRect().mLeft); + S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); last_header->reshape(new_width, last_header->getRect().getHeight()); last_header->setVisible(mDisplayColumnHeaders && new_width > 0); } - } void LLScrollListCtrl::setDisplayHeading(BOOL display) { mDisplayColumnHeaders = display; - updateColumns(); - - setHeadingHeight(mHeadingHeight); + updateLayout(); } void LLScrollListCtrl::setHeadingHeight(S32 heading_height) { mHeadingHeight = heading_height; - reshape(mRect.getWidth(), mRect.getHeight()); + updateLayout(); - // Resize - mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); } void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) @@ -934,6 +958,8 @@ BOOL LLScrollListCtrl::selectFirstItem() BOOL LLScrollListCtrl::selectNthItem( S32 target_index ) { + if (mItemList.empty()) return FALSE; + // Deselects all other items BOOL success = FALSE; S32 index = 0; @@ -1012,7 +1038,32 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) } delete itemp; mItemList.erase(mItemList.begin() + target_index); - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); +} + +//FIXME: refactor item deletion +void LLScrollListCtrl::deleteItems(const LLSD& sd) +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter < mItemList.end(); ) + { + LLScrollListItem* itemp = *iter; + if (itemp->getValue().asString() == sd.asString()) + { + if (itemp == mLastSelected) + { + mLastSelected = NULL; + } + delete itemp; + mItemList.erase(iter++); + } + else + { + iter++; + } + } + + calcMaxContentWidth(NULL); } void LLScrollListCtrl::deleteSelectedItems() @@ -1032,7 +1083,7 @@ void LLScrollListCtrl::deleteSelectedItems() } } mLastSelected = NULL; - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); } void LLScrollListCtrl::highlightNthItem(S32 target_index) @@ -1108,7 +1159,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) if (!getFirstSelected()) { - selectFirstItem(); + // select last item + selectNthItem(getItemCount() - 1); } else { @@ -1130,7 +1182,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) break; } - prev_item = cur_item; + // don't allow navigation to disabled elements + prev_item = cur_item->getEnabled() ? cur_item : prev_item; } } @@ -1145,32 +1198,34 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) void LLScrollListCtrl::selectNextItem( BOOL extend_selection) { + LLScrollListItem* next_item = NULL; + if (!getFirstSelected()) { selectFirstItem(); } else { - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + item_list::reverse_iterator iter; + for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) { - LLScrollListItem* item = *iter; - if (item->getSelected()) + LLScrollListItem* cur_item = *iter; + + if (cur_item->getSelected()) { - if (++iter != mItemList.end()) + if (next_item) { - LLScrollListItem *next_item = *iter; - if (next_item) - { - selectItem(next_item, !extend_selection); - } - else - { - reportInvalidInput(); - } + selectItem(next_item, !extend_selection); + } + else + { + reportInvalidInput(); } break; } + + // don't allow navigation to disabled items + next_item = cur_item->getEnabled() ? cur_item : next_item; } } @@ -1213,10 +1268,29 @@ LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, EAd item->setEnabled(enabled); item->addColumn( item_text, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) ); addItem( item, pos ); + + // create new column on demand for "simple" items + if (mColumns.empty()) + { + LLSD new_column; + new_column["name"] = mDefaultColumnName; + new_column["label"] = ""; + new_column["dynamicwidth"] = TRUE; + addColumn(new_column); + } } return item; } +LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) +{ + LLSD item; + item["enabled"] = FALSE; + item["columns"][0]["type"] = "separator"; + item["columns"][0]["column"] = mDefaultColumnName; + + return addElement(item, pos); +} // Selects first enabled item of the given name. // Returns false if item not found. @@ -1242,7 +1316,7 @@ BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensiti { LLScrollListItem* item = *iter; // Only select enabled items with matching names - LLString item_text = item->getColumn(0)->getText(); + LLString item_text = item->getColumn(0)->getValue().asString(); if (!case_sensitive) { LLString::toLower(item_text); @@ -1288,7 +1362,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca LLScrollListItem* item = *iter; // Only select enabled items with matching names LLScrollListCell* cellp = item->getColumn(mSearchColumn); - BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getText()[0]) : FALSE; + BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; if (select) { selectItem(item); @@ -1315,7 +1389,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca { continue; } - LLWString item_label = utf8str_to_wstring(cellp->getText()); + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (!case_sensitive) { LLWString::toLower(item_label); @@ -1346,14 +1420,14 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca return found; } -const LLString& LLScrollListCtrl::getSimpleSelectedItem(S32 column) const +const LLString LLScrollListCtrl::getSimpleSelectedItem(S32 column) const { LLScrollListItem* item; item = getFirstSelected(); if (item) { - return item->getColumn(column)->getText(); + return item->getColumn(column)->getValue().asString(); } return LLString::null; @@ -1384,6 +1458,16 @@ LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, LLS item->setEnabled(enabled); item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width); addItem( item, pos ); + + // create new column on demand + if (mColumns.empty()) + { + LLSD new_column; + new_column["name"] = "default_column"; + new_column["label"] = ""; + new_column["dynamicwidth"] = TRUE; + addColumn(new_column); + } } return item; } @@ -1481,9 +1565,7 @@ void LLScrollListCtrl::drawItems() LLGLSUIDefault gls_ui; { - LLRect clip_rect = mItemListRect; - if(!mScrollbar->getVisible()) clip_rect.mRight += SCROLLBAR_SIZE; - LLLocalClipRect clip(clip_rect); + LLLocalClipRect clip(mItemListRect); S32 cur_x = x; S32 cur_y = y; @@ -1491,7 +1573,6 @@ void LLScrollListCtrl::drawItems() mDrewSelected = FALSE; S32 line = 0; - LLColor4 color; S32 max_columns = 0; item_list::iterator iter; @@ -1502,7 +1583,7 @@ void LLScrollListCtrl::drawItems() item_rect.setOriginAndSize( cur_x, cur_y, - mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + SCROLLBAR_SIZE, + mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + mScrollbar->getRect().getWidth(), mLineHeight ); //llinfos << item_rect.getWidth() << llendl; @@ -1514,37 +1595,43 @@ void LLScrollListCtrl::drawItems() max_columns = llmax(max_columns, item->getNumColumns()); + LLColor4 fg_color; LLRect bg_rect = item_rect; // pad background rectangle to separate it from contents bg_rect.stretch(LIST_BORDER_PAD, 0); + LLColor4 bg_color(0.f, 0.f, 0.f, 0.f); if( mScrollLines <= line && line < mScrollLines + num_page_lines ) { if( item->getSelected() && mCanSelect) { // Draw background of selected item - LLGLSNoTexture no_texture; - glColor4fv(mBgSelectedColor.mV); - gl_rect_2d( bg_rect ); - - color = mFgSelectedColor; + bg_color = mBgSelectedColor; + fg_color = (item->getEnabled() ? mFgSelectedColor : mFgDisabledColor); } else if (mHighlightedItem == line && mCanSelect) { - LLGLSNoTexture no_texture; - glColor4fv(mHighlightedColor.mV); - gl_rect_2d( bg_rect ); - color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); + bg_color = mHighlightedColor; + fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); } else { - color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); if (mDrawStripes && (line%2 == 0) && (max_columns > 1)) { - LLGLSNoTexture no_texture; - glColor4fv(mBgStripeColor.mV); - gl_rect_2d( bg_rect ); + bg_color = mBgStripeColor; } + fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); + } + + if (!item->getEnabled()) + { + bg_color = mBgReadOnlyColor; + } + // draw background rect + { + LLGLSNoTexture no_texture; + glColor4fv(bg_color.mV); + gl_rect_2d( bg_rect ); } S32 line_x = cur_x; @@ -1553,7 +1640,6 @@ void LLScrollListCtrl::drawItems() S32 cur_col = 0; S32 dynamic_width = 0; S32 dynamic_remainder = 0; - bool first_dynamic = true; if(mNumDynamicWidthColumns > 0) { dynamic_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; @@ -1563,15 +1649,9 @@ void LLScrollListCtrl::drawItems() for (LLScrollListCell* cell = item->getColumn(0); cur_col < num_cols; cell = item->getColumn(++cur_col)) { S32 cell_width = cell->getWidth(); - if(mColumnsIndexed.size() > (U32)cur_col && mColumnsIndexed[cur_col] && mColumnsIndexed[cur_col]->mDynamicWidth) { cell_width = dynamic_width + (--dynamic_remainder ? 1 : 0); - if(first_dynamic) - { - cell_width += mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE; - first_dynamic = false; - } cell->setWidth(cell_width); } // Two ways a cell could be hidden @@ -1585,7 +1665,7 @@ void LLScrollListCtrl::drawItems() F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout"); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); - cell->drawToWidth( space_left, color, highlight_color ); + cell->drawToWidth( space_left, fg_color, highlight_color ); LLUI::popMatrix(); cur_x += cell_width + mColumnPadding; @@ -1605,6 +1685,12 @@ void LLScrollListCtrl::draw() { if( getVisible() ) { + // if user specifies sort, make sure it is maintained + if (needsSorting() && !isSorted()) + { + sortItems(); + } + if (mNeedsScroll) { scrollToShowSelected(); @@ -1645,6 +1731,54 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return handled; } +BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) +{ + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListColumn* columnp = getColumn(column_index); + + if (columnp == NULL) return FALSE; + + BOOL handled = FALSE; + // show tooltip for full name of hovered item if it has been truncated + LLScrollListItem* hit_item = hitItem(x, y); + if (hit_item) + { + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return FALSE; + S32 cell_required_width = hit_cell->getContentWidth(); + if (hit_cell + && hit_cell->isText() + && cell_required_width > columnp->mWidth) + { + + S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; + S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item)); + LLRect cell_rect; + cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->mWidth, mLineHeight); + // Convert rect local to screen coordinates + localPointToScreen( + cell_rect.mLeft, cell_rect.mBottom, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + cell_rect.mRight, cell_rect.mTop, + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + + msg = hit_cell->getValue().asString(); + handled = TRUE; + } + } + + // otherwise, look for a tooltip associated with this column + LLColumnHeader* headerp = columnp->mHeader; + if (headerp && !handled) + { + headerp->handleToolTip(x, y, msg, sticky_rect_screen); + handled = !msg.empty(); + } + + return handled; +} + BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if (!mCanSelect) return FALSE; @@ -1652,6 +1786,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) BOOL selection_changed = FALSE; LLScrollListItem* hit_item = hitItem(x, y); + if( hit_item ) { if( mAllowMultipleSelection ) @@ -1686,6 +1821,11 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { selectItem(item, FALSE); selecting = !selecting; + if (hit_item == lastSelected) + { + // stop selecting now, since we just clicked on our last selected item + selecting = FALSE; + } } if (selecting) { @@ -1726,9 +1866,6 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) selectItem(hit_item); } - hit_item->handleClick(x - mBorderThickness - LIST_BORDER_PAD, - 1, mask); - selection_changed = mSelectionChanged; if (mCommitOnSelectionChange) { @@ -1750,19 +1887,17 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + BOOL handled = childrenHandleMouseDown(x, y, mask) != NULL; if( !handled ) { // set keyboard focus first, in case click action wants to move focus elsewhere setFocus(TRUE); - // clear selection changed flag so because user is starting a selection operation + // clear selection changed flag because user is starting a selection operation mSelectionChanged = FALSE; - gFocusMgr.setMouseCapture(this); - selectItemAt(x, y, mask); - mNeedsScroll = TRUE; + handleClick(x, y, mask); } return TRUE; @@ -1798,19 +1933,74 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) //BOOL handled = FALSE; if(getVisible()) { - // Offer the click to the children, even if we aren't enabled - // so the scroll bars will work. - if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) + BOOL handled = handleClick(x, y, mask); + + if (!handled) { - if( mCanSelect && mOnDoubleClickCallback ) + // Offer the click to the children, even if we aren't enabled + // so the scroll bars will work. + if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) { - mOnDoubleClickCallback( mCallbackUserData ); + if( mCanSelect && mOnDoubleClickCallback ) + { + mOnDoubleClickCallback( mCallbackUserData ); + } } } } return TRUE; } +BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) +{ + // which row was clicked on? + LLScrollListItem* hit_item = hitItem(x, y); + if (!hit_item) return FALSE; + + // get appropriate cell from that row + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return FALSE; + + // select item (thus deselecting any currently selected item) + // only if item is not already selected + if (!hit_item->getSelected()) + { + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = TRUE; + } + + if (hit_cell->handleClick()) + { + // propagate value of this cell to other selected items + // and commit the respective widgets + LLSD item_value = hit_cell->getValue(); + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + LLScrollListCell* cellp = item->getColumn(column_index); + cellp->setValue(item_value); + cellp->onCommit(); + } + } + //FIXME: find a better way to signal cell changes + onCommit(); + return TRUE; + } + else + { + // treat this as a normal single item selection + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = TRUE; + // do not stop click processing (click callback, etc) + return FALSE; + } +} + LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) { // Excludes disabled items. @@ -1847,6 +2037,59 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) return hit_item; } +S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) +{ + // which column did we hit? + S32 left = 0; + S32 right = 0; + S32 width = 0; + S32 column_index = 0; + + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + width = (*iter)->mWidth + mColumnPadding; + right += width; + if (left <= x && x < right ) + { + break; + } + + // set left for next column as right of current column + left = right; + column_index++; + } + + return llclamp(column_index, 0, getNumColumns() - 1); +} + + +S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) +{ + S32 column_offset = 0; + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + if (index-- <= 0) + { + return column_offset; + } + column_offset += (*iter)->mWidth + mColumnPadding; + } + + // when running off the end, return the rightmost pixel + return mItemListRect.mRight; +} + +S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) +{ + S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight) + - mLineHeight; + return row_bottom; +} + BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { @@ -1860,7 +2103,8 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) mNeedsScroll = TRUE; } } - else if (mCanSelect) + else + if (mCanSelect) { LLScrollListItem* item = hitItem(x, y); if (item) @@ -1875,13 +2119,6 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) handled = LLUICtrl::handleHover( x, y, mask ); - //if( !handled ) - //{ - // // Opaque - // getWindow()->setCursor(UI_CURSOR_ARROW); - // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - // handled = TRUE; - //} return handled; } @@ -2082,7 +2319,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_ if (cellp) { // Only select enabled items with matching first characters - LLWString item_label = utf8str_to_wstring(cellp->getText()); + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) { selectItem(item); @@ -2176,7 +2413,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) LLScrollListCell* cellp = itemp->getColumn(mSearchColumn); if (cellp) { - cellp->highlightText(0, 0); + cellp->highlightText(0, 0); } mSelectionChanged = TRUE; } @@ -2202,38 +2439,52 @@ BOOL LLScrollListCtrl::isSorted() return mSorted; } -// Called by scrollbar -//static -void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) +struct SameSortColumn { - LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; - self->mScrollLines = new_pos; -} + SameSortColumn(S32 column) : mColumn(column) {} + S32 mColumn; + bool operator()(std::pair<S32, BOOL> sort_column) { return sort_column.first == mColumn; } +}; -// First column is column 0 -void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) +BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) { - if (!mSorted || mSortColumn != column) + sort_column_t new_sort_column(column, ascending); + + if (mSortColumns.empty()) { - mSortColumn = column; - std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); - setSorted(TRUE); + mSortColumns.push_back(new_sort_column); + return TRUE; } + else + { + // grab current sort column + sort_column_t cur_sort_column = mSortColumns.back(); + + // remove any existing sort criterion referencing this column + // and add the new one + remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column)); + mSortColumns.push_back(new_sort_column); - // just reverse the list if changing sort order - if(mSortAscending != ascending) - { - std::reverse(mItemList.begin(), mItemList.end()); - mSortAscending = ascending; + // did the sort criteria change? + return (cur_sort_column != new_sort_column); } } +// Called by scrollbar +//static +void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) +{ + LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; + self->mScrollLines = new_pos; +} + + void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) { if (name.empty()) { - sortByColumn(mSortColumn, mSortAscending); + sortItems(); return; } @@ -2244,6 +2495,26 @@ void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) } } +// First column is column 0 +void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) +{ + if (setSort(column, ascending)) + { + sortItems(); + } +} + +void LLScrollListCtrl::sortItems() +{ + // do stable sort to preserve any previous sorts + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(mSortColumns)); + + setSorted(TRUE); +} + S32 LLScrollListCtrl::getScrollPos() { return mScrollbar->getDocPos(); @@ -2465,7 +2736,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac LLSD columns; S32 index = 0; LLXMLNodePtr child; - S32 total_static = 0, num_dynamic = 0; + S32 total_static = 0; for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) { if (child->hasName("column")) @@ -2491,8 +2762,10 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac S32 columnwidth = -1; child->getAttributeS32("width", columnwidth); + LLString tooltip; + child->getAttributeString("tool_tip", tooltip); + if(!columndynamicwidth) total_static += columnwidth; - else ++num_dynamic; F32 columnrelwidth = 0.f; child->getAttributeF32("relwidth", columnrelwidth); @@ -2509,10 +2782,11 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac columns[index]["relwidth"] = columnrelwidth; columns[index]["dynamicwidth"] = columndynamicwidth; columns[index]["halign"] = (S32)h_align; + columns[index]["tool_tip"] = tooltip; + index++; } } - scroll_list->setNumDynamicColumns(num_dynamic); scroll_list->setTotalStaticColumnWidth(total_static); scroll_list->setColumnHeadings(columns); @@ -2665,7 +2939,7 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) LLString name = column["name"].asString(); if (mColumns.empty()) { - mDefaultColumn = 0; + mDefaultColumnName = name; } // if no column name provided, just use ordinal as name if (name.empty()) @@ -2691,6 +2965,7 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) } else if(new_column->mDynamicWidth) { + mNumDynamicWidthColumns++; new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; } S32 top = mItemListRect.mTop; @@ -2724,17 +2999,16 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) new_column->mHeader->setLabel(new_column->mLabel); //new_column->mHeader->setLabel(new_column->mLabel); } + + new_column->mHeader->setToolTip(column["tool_tip"].asString()); + //RN: although it might be useful to change sort order with the keyboard, // mixing tab stops on child items along with the parent item is not supported yet new_column->mHeader->setTabStop(FALSE); addChild(new_column->mHeader); new_column->mHeader->setVisible(mDisplayColumnHeaders); - - // Move scroll to front - removeChild(mScrollbar); - addChild(mScrollbar); - + sendChildToFront(mScrollbar); } } updateColumns(); @@ -2753,18 +3027,18 @@ void LLScrollListCtrl::onClickColumn(void *userdata) LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; bool ascending = column->mSortAscending; - if (column->mSortingColumn != column->mName) + if (column->mSortingColumn != column->mName + && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) { - if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) - { - LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; - column_index = info_redir.mIndex; - } + LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; + column_index = info_redir.mIndex; } - if (column_index == parent->mSortColumn) + // if this column is the primary sort key, reverse the direction + sort_column_t cur_sort_column; + if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index) { - ascending = !parent->mSortAscending; + ascending = !parent->mSortColumns.back().second; } parent->sortByColumn(column_index, ascending); @@ -2777,12 +3051,17 @@ void LLScrollListCtrl::onClickColumn(void *userdata) std::string LLScrollListCtrl::getSortColumnName() { - LLScrollListColumn* column = mSortColumn >= 0 ? mColumnsIndexed[mSortColumn] : NULL; + LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first]; if (column) return column->mName; else return ""; } +BOOL LLScrollListCtrl::needsSorting() +{ + return !mSortColumns.empty(); +} + void LLScrollListCtrl::clearColumns() { std::map<LLString, LLScrollListColumn>::iterator itor; @@ -2796,6 +3075,7 @@ void LLScrollListCtrl::clearColumns() } } mColumns.clear(); + mSortColumns.clear(); } void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label) @@ -2851,11 +3131,6 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p { LLString column = (*itor)["column"].asString(); - if (mColumns.size() == 0) - { - mDefaultColumn = 0; - } - LLScrollListColumn* columnp = NULL; // empty columns strings index by ordinal @@ -2895,6 +3170,7 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p LLString type = (*itor)["type"].asString(); BOOL has_color = (*itor).has("color"); LLColor4 color = ((*itor)["color"]); + BOOL enabled = !(*itor).has("enabled") || (*itor)["enabled"].asBoolean() == true; const LLFontGL *font = gResMgr->getRes(fontname); if (!font) @@ -2906,7 +3182,8 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p if (type == "icon") { LLUUID image_id = value.asUUID(); - LLImageGL* icon = LLUI::sImageProvider->getUIImageByID(image_id); + // don't use special image with UUID::null, just don't draw an image + LLImageGL* icon = image_id.isNull() ? NULL : LLUI::sImageProvider->getImageByID(image_id); LLScrollListIcon* cell = new LLScrollListIcon(icon, width, image_id); if (has_color) { @@ -2916,8 +3193,10 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p } else if (type == "checkbox") { - LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl(value.asString(), - LLRect(0, 0, width, width), "label"); + LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl("check", + LLRect(0, width, width, 0), " "); + ctrl->setEnabled(enabled); + ctrl->setValue(value); LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width); if (has_color) { @@ -3070,18 +3349,12 @@ void LLScrollListCtrl::onFocusReceived() { // forget latent selection changes when getting focus mSelectionChanged = FALSE; + LLUICtrl::onFocusReceived(); } //virtual void LLScrollListCtrl::onFocusLost() { - if (mIsPopup) - { - if (getParent()) - { - getParent()->onFocusLost(); - } - } if (hasMouseCapture()) { gFocusMgr.setMouseCapture(NULL); @@ -3133,11 +3406,11 @@ void LLColumnHeader::draw() { if( getVisible() ) { - mDrawArrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; + BOOL draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); - mArrowImage = is_ascending ? LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("up_arrow.tga"))) - : LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("down_arrow.tga"))); + mButton->setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); + mArrowImage = mButton->getImageOverlay()->getImage(); //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); @@ -3237,11 +3510,11 @@ void LLColumnHeader::showList() { if (mColumn->mParentCtrl->getSortAscending()) { - low_item_text = cell->getText(); + low_item_text = cell->getValue().asString(); } else { - high_item_text = cell->getText(); + high_item_text = cell->getValue().asString(); } } } @@ -3254,11 +3527,11 @@ void LLColumnHeader::showList() { if (mColumn->mParentCtrl->getSortAscending()) { - high_item_text = cell->getText(); + high_item_text = cell->getValue().asString(); } else { - low_item_text = cell->getText(); + low_item_text = cell->getValue().asString(); } } } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index a98a411efa..001e10184b 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -59,14 +59,16 @@ public: virtual ~LLScrollListCell() {}; virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible virtual S32 getWidth() const = 0; + virtual S32 getContentWidth() const { return 0; } virtual S32 getHeight() const = 0; - virtual const LLString& getText() const { return LLString::null; } - virtual const LLString& getTextLower() const { return LLString::null; } + virtual const LLSD getValue() const { return LLString::null; } + virtual void setValue(LLSD value) { } virtual BOOL getVisible() const { return TRUE; } virtual void setWidth(S32 width) = 0; virtual void highlightText(S32 offset, S32 num_chars) {} virtual BOOL isText() = 0; virtual void setColor(const LLColor4&) = 0; + virtual void onCommit() {}; virtual BOOL handleClick() { return FALSE; } virtual void setEnabled(BOOL enable) { } @@ -96,20 +98,24 @@ public: virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const; virtual S32 getWidth() const { return mWidth; } + virtual S32 getContentWidth() const; virtual void setWidth(S32 width) { mWidth = width; } virtual S32 getHeight() const { return llround(mFont->getLineHeight()); } - virtual const LLString& getText() const { return mText.getString(); } + virtual const LLSD getValue() const { return LLSD(mText.getString()); } virtual BOOL getVisible() const { return mVisible; } virtual void highlightText(S32 offset, S32 num_chars) {mHighlightOffset = offset; mHighlightCount = num_chars;} - void setText(const LLStringExplicit& text); + virtual void setColor(const LLColor4&); virtual BOOL isText() { return TRUE; } + void setText(const LLStringExplicit& text); + void setFontStyle(const U8 font_style) { mFontStyle = font_style; } + private: LLUIString mText; const LLFontGL* mFont; LLColor4* mColor; - const U8 mFontStyle; + U8 mFontStyle; LLFontGL::HAlign mFontAlignment; S32 mWidth; BOOL mVisible; @@ -128,16 +134,16 @@ public: /*virtual*/ ~LLScrollListIcon(); virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const; virtual S32 getWidth() const { return mWidth; } - virtual S32 getHeight() const { return mIcon->getHeight(); } - virtual const LLString& getText() const { return mImageUUID; } - virtual const LLString& getTextLower() const { return mImageUUID; } + virtual S32 getHeight() const { return mIcon ? mIcon->getHeight() : 0; } + virtual const LLSD getValue() const { return LLSD(mImageUUID); } virtual void setWidth(S32 width) { mWidth = width; } virtual void setColor(const LLColor4&); virtual BOOL isText() { return FALSE; } + virtual void setValue(LLSD value); private: LLPointer<LLImageGL> mIcon; - LLString mImageUUID; + LLUUID mImageUUID; S32 mWidth; LLColor4 mColor; }; @@ -151,9 +157,12 @@ public: virtual S32 getWidth() const { return mWidth; } virtual S32 getHeight() const { return 0; } virtual void setWidth(S32 width) { mWidth = width; } + virtual const LLSD getValue() const { return mCheckBox->getValue(); } + virtual void setValue(LLSD value) { mCheckBox->setValue(value); } + virtual void onCommit() { mCheckBox->onCommit(); } virtual BOOL handleClick(); - virtual void setEnabled(BOOL enable) { if (mCheckBox) mCheckBox->setEnabled(enable); } + virtual void setEnabled(BOOL enable) { mCheckBox->setEnabled(enable); } virtual void setColor(const LLColor4& color) {}; LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } @@ -266,6 +275,7 @@ public: /*virtual*/ void draw(); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ void showList(); /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding); /*virtual*/ void userSetShape(const LLRect& new_rect); @@ -333,8 +343,6 @@ public: LLScrollListCell *getColumn(const S32 i) const { if (0 <= i && i < (S32)mColumns.size()) { return mColumns[i]; } return NULL; } - virtual BOOL handleClick(S32 x, S32 y, MASK mask); - LLString getContentsCSV(); private: @@ -370,9 +378,8 @@ public: void deleteAllItems() { clearRows(); } // Sets an array of column descriptors - void setColumnHeadings(LLSD headings); - // Numerical based sort by column function (used by LLComboBox) - void sortByColumn(U32 column, BOOL ascending); + void setColumnHeadings(LLSD headings); + void sortByColumn(U32 column, BOOL ascending); // LLCtrlListInterface functions virtual S32 getItemCount() const; @@ -421,18 +428,20 @@ public: BOOL isSorted(); virtual BOOL isSelected(LLSD value); - + + BOOL handleClick(S32 x, S32 y, MASK mask); BOOL selectFirstItem(); BOOL selectNthItem( S32 index ); BOOL selectItemAt(S32 x, S32 y, MASK mask); - void deleteSingleItem( S32 index ) ; + void deleteSingleItem( S32 index ); + void deleteItems(const LLSD& sd); void deleteSelectedItems(); void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change void highlightNthItem( S32 index ); void setDoubleClickCallback( void (*cb)(void*) ) { mOnDoubleClickCallback = cb; } - void setMaxiumumSelectCallback( void (*cb)(void*) ) { mOnMaximumSelectCallback = cb; } + void setMaximumSelectCallback( void (*cb)(void*) ) { mOnMaximumSelectCallback = cb; } void setSortChangedCallback( void (*cb)(void*) ) { mOnSortChangedCallback = cb; } void swapWithNext(S32 index); @@ -449,11 +458,12 @@ public: LLScrollListItem* addSimpleItem( const LLString& item_text, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE ); // Add an item with an associated LLSD LLScrollListItem* addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0 ); + LLScrollListItem* addSeparator(EAddPosition pos); BOOL selectSimpleItem( const LLString& item, BOOL case_sensitive = TRUE ); // FALSE if item not found BOOL selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive); BOOL selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive); - const LLString& getSimpleSelectedItem(S32 column = 0) const; + const LLString getSimpleSelectedItem(S32 column = 0) const; LLSD getSimpleSelectedValue(); // DEPRECATED: Use LLSD versions of addSimpleItem() and getSimpleSelectedValue(). @@ -472,6 +482,8 @@ public: LLScrollListItem* getFirstData() const; LLScrollListItem* getLastData() const; std::vector<LLScrollListItem*> getAllData() const; + + LLScrollListItem* getItem(const LLSD& sd) const; void setAllowMultipleSelection(BOOL mult ) { mAllowMultipleSelection = mult; } @@ -501,28 +513,34 @@ public: S32 getSearchColumn() { return mSearchColumn; } void setSearchColumn(S32 column) { mSearchColumn = column; } + S32 getColumnIndexFromOffset(S32 x); + S32 getColumnOffsetFromIndex(S32 index); + S32 getRowOffsetFromIndex(S32 index); void clearSearchString() { mSearchString.clear(); } // Overridden from LLView - virtual void draw(); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual void setEnabled(BOOL enabled); - virtual void setFocus( BOOL b ); - virtual void onFocusReceived(); - virtual void onFocusLost(); + /*virtual*/ void draw(); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); + /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent); + /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect); + /*virtual*/ void setEnabled(BOOL enabled); + /*virtual*/ void setFocus( BOOL b ); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual BOOL isDirty() const; virtual void resetDirty(); // Clear dirty state - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - virtual void arrange(S32 max_width, S32 max_height); + virtual void updateLayout(); + virtual void fitContents(S32 max_width, S32 max_height); + virtual LLRect getRequiredRect(); static BOOL rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); @@ -534,12 +552,12 @@ public: static void onClickColumn(void *userdata); void updateColumns(); - void updateMaxContentWidth(LLScrollListItem* changed_item); + void calcMaxContentWidth(LLScrollListItem* changed_item); + S32 getMaxContentWidth() { return mMaxContentWidth; } void setDisplayHeading(BOOL display); void setHeadingHeight(S32 heading_height); void setCollapseEmptyColumns(BOOL collapse); - void setIsPopup(BOOL is_popup) { mIsPopup = is_popup; } LLScrollListItem* hitItem(S32 x,S32 y); virtual void scrollToShowSelected(); @@ -564,9 +582,11 @@ public: void setTotalStaticColumnWidth(int width) { mTotalStaticColumnWidth = width; } std::string getSortColumnName(); - BOOL getSortAscending() { return mSortAscending; } + BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } + BOOL needsSorting(); S32 selectMultiple( LLDynamicArray<LLUUID> ids ); + void sortItems(); protected: // "Full" interface: use this when you're creating a list that has one or more of the following: @@ -596,6 +616,7 @@ protected: void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); void setSorted(BOOL sorted); + BOOL setSort(S32 column, BOOL ascending); protected: S32 mCurIndex; // For get[First/Next]Data @@ -616,7 +637,6 @@ protected: BOOL mCanSelect; BOOL mDisplayColumnHeaders; BOOL mCollapseEmptyColumns; - BOOL mIsPopup; typedef std::deque<LLScrollListItem *> item_list; item_list mItemList; @@ -626,7 +646,7 @@ protected: S32 mMaxItemCount; LLRect mItemListRect; - + S32 mMaxContentWidth; S32 mColumnPadding; BOOL mBackgroundVisible; @@ -652,22 +672,29 @@ protected: LLWString mSearchString; LLFrameTimer mSearchTimer; - LLString mDefaultColumn; + LLString mDefaultColumnName; S32 mSearchColumn; S32 mNumDynamicWidthColumns; S32 mTotalStaticColumnWidth; S32 mSortColumn; + S32 mSecondarySortColumn; + BOOL mSecondarySortAscending; BOOL mSortAscending; BOOL mSorted; - + std::map<LLString, LLScrollListColumn> mColumns; - std::vector<LLScrollListColumn*> mColumnsIndexed; BOOL mDirty; S32 mOriginalSelection; + typedef std::vector<LLScrollListColumn*> ordered_columns_t; + ordered_columns_t mColumnsIndexed; + + typedef std::pair<S32, BOOL> sort_column_t; + std::vector<sort_column_t> mSortColumns; + public: // HACK: Did we draw one selected item this frame? BOOL mDrewSelected; diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 29626c25d6..bd91d562aa 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -41,9 +41,6 @@ #include "llcontrol.h" #include "llimagegl.h" -const S32 THUMB_WIDTH = 8; -const S32 TRACK_HEIGHT = 6; - LLSlider::LLSlider( const LLString& name, const LLRect& rect, @@ -65,20 +62,24 @@ LLSlider::LLSlider( mIncrement( increment ), mVolumeSlider( volume ), mMouseOffset( 0 ), - mDragStartThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ), - mThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ), mTrackColor( LLUI::sColorsGroup->getColor( "SliderTrackColor" ) ), mThumbOutlineColor( LLUI::sColorsGroup->getColor( "SliderThumbOutlineColor" ) ), mThumbCenterColor( LLUI::sColorsGroup->getColor( "SliderThumbCenterColor" ) ), - mDisabledThumbColor(LLUI::sColorsGroup->getColor( "SliderDisabledThumbColor" ) ), mMouseDownCallback( NULL ), mMouseUpCallback( NULL ) { + mThumbImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-thumb_dark.tga"))); + mTrackImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-groove_dark.tga"))); + mTrackHighlightImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-highlight.tga"))); + // properly handle setting the starting thumb rect // do it this way to handle both the operating-on-settings // and standalone ways of using this setControlName(control_name, NULL); setValue(getValueF32()); + + updateThumbRect(); + mDragStartThumbRect = mThumbRect; } EWidgetType LLSlider::getWidgetType() const @@ -107,17 +108,26 @@ void LLSlider::setValue(F32 value, BOOL from_event) } mValue = value; + updateThumbRect(); +} +void LLSlider::updateThumbRect() +{ F32 t = (mValue - mMinValue) / (mMaxValue - mMinValue); - S32 left_edge = THUMB_WIDTH/2; - S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2); + S32 thumb_width = mThumbImage->getWidth(); + S32 thumb_height = mThumbImage->getHeight(); + S32 left_edge = (thumb_width / 2); + S32 right_edge = mRect.getWidth() - (thumb_width / 2); S32 x = left_edge + S32( t * (right_edge - left_edge) ); - mThumbRect.mLeft = x - (THUMB_WIDTH/2); - mThumbRect.mRight = x + (THUMB_WIDTH/2); + mThumbRect.mLeft = x - (thumb_width / 2); + mThumbRect.mRight = mThumbRect.mLeft + thumb_width; + mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2); + mThumbRect.mTop = mThumbRect.mBottom + thumb_height; } + void LLSlider::setValueAndCommit(F32 value) { F32 old_value = mValue; @@ -139,8 +149,9 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask) { if( hasMouseCapture() ) { - S32 left_edge = THUMB_WIDTH/2; - S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2); + S32 thumb_half_width = mThumbImage->getWidth()/2; + S32 left_edge = thumb_half_width; + S32 right_edge = mRect.getWidth() - (thumb_half_width); x += mMouseOffset; x = llclamp( x, left_edge, right_edge ); @@ -203,7 +214,7 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) // Find the offset of the actual mouse location from the center of the thumb. if (mThumbRect.pointInRect(x,y)) { - mMouseOffset = (mThumbRect.mLeft + THUMB_WIDTH/2) - x; + mMouseOffset = (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x; } else { @@ -251,6 +262,9 @@ void LLSlider::draw() { if( getVisible() ) { + // since thumb image might still be decoding, need thumb to accomodate image size + updateThumbRect(); + // Draw background and thumb. // drawing solids requires texturing be disabled @@ -260,104 +274,37 @@ void LLSlider::draw() F32 opacity = mEnabled ? 1.f : 0.3f; LLColor4 center_color = (mThumbCenterColor % opacity); - LLColor4 outline_color = (mThumbOutlineColor % opacity); LLColor4 track_color = (mTrackColor % opacity); - LLImageGL* thumb_imagep = NULL; - // Track - if (mVolumeSlider) - { - LLRect track(0, mRect.getHeight(), mRect.getWidth(), 0); - - track.mBottom += 3; - track.mTop -= 1; - track.mRight -= 1; - - gl_triangle_2d(track.mLeft, track.mBottom, - track.mRight, track.mBottom, - track.mRight, track.mTop, - center_color, - TRUE); - gl_triangle_2d(track.mLeft, track.mBottom, - track.mRight, track.mBottom, - track.mRight, track.mTop, - outline_color, - FALSE); - } - else - { - LLUUID thumb_image_id; - thumb_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga")); - thumb_imagep = LLUI::sImageProvider->getUIImageByID(thumb_image_id); + LLRect track_rect(mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() + (mTrackImage->getHeight() / 2), + mRect.getWidth() - mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() - (mTrackImage->getHeight() / 2) ); - S32 height_offset = (mRect.getHeight() - TRACK_HEIGHT) / 2; - LLRect track_rect(0, mRect.getHeight() - height_offset, mRect.getWidth(), height_offset ); + gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 3, 3, track_rect.getWidth(), track_rect.getHeight(), + mTrackImage, track_color); + gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 3, 3, mThumbRect.mLeft, track_rect.getHeight(), + mTrackHighlightImage, track_color); - track_rect.stretch(-1); - gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 16, 16, track_rect.getWidth(), track_rect.getHeight(), - thumb_imagep, track_color); - } // Thumb - if (!thumb_imagep) - { - if (mVolumeSlider) - { - if (hasMouseCapture()) - { - LLRect rect(mDragStartThumbRect); - gl_rect_2d( rect, outline_color ); - rect.stretch(-1); - gl_rect_2d( rect, mThumbCenterColor % 0.3f ); - - if (hasFocus()) - { - LLRect thumb_rect = mThumbRect; - thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()))); - gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor()); - } - gl_rect_2d( mThumbRect, mThumbOutlineColor ); - } - else - { - if (hasFocus()) - { - LLRect thumb_rect = mThumbRect; - thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()))); - gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor()); - } - LLRect rect(mThumbRect); - gl_rect_2d(rect, outline_color); - rect.stretch(-1); - gl_rect_2d( rect, center_color); - } - } - else - { - gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE); - if (hasMouseCapture()) - { - gl_rect_2d(mDragStartThumbRect, center_color, FALSE); - } - } - } - else if( hasMouseCapture() ) + if( hasMouseCapture() ) { - gl_draw_scaled_image_with_border(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, 16, 16, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), - thumb_imagep, mThumbCenterColor % 0.3f, TRUE); + gl_draw_scaled_image(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), + mThumbImage, mThumbCenterColor % 0.3f); if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); LLRect highlight_rect = mThumbRect; highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt))); - gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(), - thumb_imagep, gFocusMgr.getFocusColor()); + gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 0, 0, highlight_rect.getWidth(), highlight_rect.getHeight(), + mThumbImage, gFocusMgr.getFocusColor(), TRUE); } - gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), - thumb_imagep, mThumbOutlineColor, TRUE); + gl_draw_scaled_image(mThumbRect.mLeft, mThumbRect.mBottom, mThumbRect.getWidth(), mThumbRect.getHeight(), + mThumbImage, mThumbOutlineColor); } else @@ -367,12 +314,12 @@ void LLSlider::draw() F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); LLRect highlight_rect = mThumbRect; highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt))); - gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(), - thumb_imagep, gFocusMgr.getFocusColor()); + gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 0, 0, highlight_rect.getWidth(), highlight_rect.getHeight(), + mThumbImage, gFocusMgr.getFocusColor(), TRUE); } - gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), - thumb_imagep, center_color, TRUE); + gl_draw_scaled_image(mThumbRect.mLeft, mThumbRect.mBottom, mThumbRect.getWidth(), mThumbRect.getHeight(), + mThumbImage, center_color); } LLUICtrl::draw(); } diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 2641eaac74..55be2afbcc 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -36,6 +36,7 @@ #include "v4color.h" class LLUICtrlFactory; +class LLImageGL; class LLSlider : public LLUICtrl { @@ -85,6 +86,7 @@ public: protected: void setValueAndCommit(F32 value); + void updateThumbRect(); protected: F32 mValue; @@ -97,11 +99,14 @@ protected: S32 mMouseOffset; LLRect mDragStartThumbRect; + LLImageGL* mThumbImage; + LLImageGL* mTrackImage; + LLImageGL* mTrackHighlightImage; + LLRect mThumbRect; LLColor4 mTrackColor; LLColor4 mThumbOutlineColor; LLColor4 mThumbCenterColor; - LLColor4 mDisabledThumbColor; void (*mMouseDownCallback)(LLUICtrl* ctrl, void* userdata); void (*mMouseUpCallback)(LLUICtrl* ctrl, void* userdata); diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index dd4a9941c5..3ff3a4f884 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -125,7 +125,7 @@ LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect, &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus ); + mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus, this ); mEditor->setIgnoreTab(TRUE); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others @@ -150,7 +150,7 @@ LLSliderCtrl::~LLSliderCtrl() } // static -void LLSliderCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata ) +void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) { LLSliderCtrl* self = (LLSliderCtrl*) userdata; llassert( caller == self->mEditor ); diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index fa6c0bccae..de1c09639c 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -117,7 +117,7 @@ public: static void onSliderMouseUp(LLUICtrl* caller,void* userdata); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLUICtrl* caller, void *userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); private: diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 2be2814080..c4b7de768a 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -128,7 +128,7 @@ LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus ); + mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus, this ); //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand @@ -137,7 +137,7 @@ LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString addChild(mEditor); updateEditor(); - setSpanChildren( TRUE ); + setUseBoundingRect( TRUE ); } LLSpinCtrl::~LLSpinCtrl() @@ -230,7 +230,7 @@ void LLSpinCtrl::onDownBtn( void *userdata ) } // static -void LLSpinCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata ) +void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) { LLSpinCtrl* self = (LLSpinCtrl*) userdata; llassert( caller == self->mEditor ); diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index f2c7b40cfe..5fc3ccdcc1 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -113,7 +113,7 @@ public: virtual void draw(); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLUICtrl* caller, void *userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); static void onUpBtn(void *userdata); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 1f932f758c..2d3534e7be 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -230,7 +230,7 @@ void LLStyle::setImage(const LLString& src) } else { - mImagep = LLUI::sImageProvider->getUIImageByID(LLUUID(src)); + mImagep = LLUI::sImageProvider->getImageByID(LLUUID(src)); } } diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 1ab11f3644..148060359f 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -156,7 +156,12 @@ void LLTabContainerCommon::lockTabs(S32 num_tabs) { // count current tabs or use supplied value and ensure no new tabs get // inserted between them - mLockedTabCount = num_tabs > 0 ? num_tabs : getTabCount(); + mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); +} + +void LLTabContainerCommon::unlockTabs() +{ + mLockedTabCount = 0; } void LLTabContainerCommon::removeTabPanel(LLPanel* child) @@ -557,7 +562,7 @@ void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name, con } void LLTabContainerCommon::setTitle(const LLString& title) -{ +{ if (mTitleBox) { mTitleBox->setText( title ); @@ -721,6 +726,14 @@ void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint inser // insert the new tab in the front of the list mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); break; + case LEFT_OF_CURRENT: + // insert the new tab before the current tab (but not before mLockedTabCount) + { + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); + mTabList.insert(current_iter, tuple); + } + break; + case RIGHT_OF_CURRENT: // insert the new tab after the current tab (but not before mLockedTabCount) { @@ -946,14 +959,14 @@ void LLTabContainer::addTabPanel(LLPanel* child, if( LLTabContainer::TOP == mTabPosition ) { btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); - tab_img = "UIImgBtnTabTopOutUUID"; - tab_selected_img = "UIImgBtnTabTopInUUID"; + tab_img = "tab_top_blue.tga"; + tab_selected_img = "tab_top_selected_blue.tga"; } else { btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); - tab_img = "UIImgBtnTabBottomOutUUID"; - tab_selected_img = "UIImgBtnTabBottomInUUID"; + tab_img = "tab_bottom_blue.tga"; + tab_selected_img = "tab_bottom_selected_blue.tga"; } if (placeholder) @@ -979,7 +992,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, LLButton* btn = new LLButton( LLString(child->getName()) + " tab", btn_rect, - tab_img, tab_selected_img, "", + "", "", "", &LLTabContainer::onTabBtn, NULL, // set userdata below font, trimmed_label, trimmed_label ); @@ -987,7 +1000,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, btn->setVisible( FALSE ); btn->setToolTip( tooltip ); btn->setScaleImage(TRUE); - btn->setFixedBorder(14, 14); + btn->setImages(tab_img, tab_selected_img); // Try to squeeze in a bit more text btn->setLeftHPad( 4 ); @@ -1139,7 +1152,7 @@ BOOL LLTabContainer::selectTab(S32 which) //if( gFocusMgr.childHasKeyboardFocus( this ) ) //{ - // gFocusMgr.setKeyboardFocus( NULL, NULL ); + // gFocusMgr.setKeyboardFocus( NULL ); //} LLTabTuple* selected_tuple = mTabList[which]; @@ -1370,7 +1383,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) { LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton; gFocusMgr.setMouseCapture(this); - gFocusMgr.setKeyboardFocus(tab_button, NULL); + gFocusMgr.setKeyboardFocus(tab_button); } } return handled; @@ -1475,7 +1488,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect ) { BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect ); - if (!handled && mTabList.size() > 0 && getVisible() && pointInView( x, y ) ) + if (!handled && mTabList.size() > 0) { LLTabTuple* firsttuple = mTabList[0]; @@ -1645,12 +1658,12 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const L mTotalTabWidth -= tuple->mButton->getRect().getWidth(); S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? - tuple->mButton->getImageOverlay()->getWidth(0) : + tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0; tuple->mPadding = image_overlay_width; - tuple->mButton->setRightHPad(tuple->mPadding + LLBUTTON_H_PAD); + tuple->mButton->setRightHPad(6); tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tuple->mButton->getRect().getHeight()); // add back in button width to total tab strip width diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index c77547a771..b72ecb8126 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -54,6 +54,7 @@ public: { START, END, + LEFT_OF_CURRENT, RIGHT_OF_CURRENT } eInsertionPoint; @@ -91,6 +92,8 @@ public: eInsertionPoint insertion_point = END) = 0; virtual void addPlaceholder(LLPanel* child, const LLString& label); virtual void lockTabs(S32 num_tabs = 0); + virtual void unlockTabs(); + S32 getNumLockedTabs() { return mLockedTabCount; } virtual void enableTabButton(S32 which, BOOL enable); diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 8bd7b1509f..3b15e3a25f 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -56,6 +56,7 @@ LLTextBox::LLTextBox(const LLString& name, const LLRect& rect, const LLString& t mBorderVisible( FALSE ), mFontStyle(LLFontGL::DROP_SHADOW_SOFT), mBorderDropShadowVisible( FALSE ), + mUseEllipses( FALSE ), mHPad(0), mVPad(0), mHAlign( LLFontGL::LEFT ), @@ -84,6 +85,7 @@ LLTextBox::LLTextBox(const LLString& name, const LLString& text, F32 max_width, mBorderVisible(FALSE), mFontStyle(LLFontGL::DROP_SHADOW_SOFT), mBorderDropShadowVisible(FALSE), + mUseEllipses( FALSE ), mHPad(0), mVPad(0), mHAlign(LLFontGL::LEFT), @@ -393,7 +395,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color, mHAlign, mVAlign, mFontStyle, - line_length, mRect.getWidth(), NULL, TRUE ); + line_length, mRect.getWidth(), NULL, TRUE, mUseEllipses ); cur_pos += line_length + 1; y -= llfloor(mFontGL->getLineHeight()); } @@ -403,7 +405,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color, mHAlign, mVAlign, mFontStyle, - S32_MAX, mRect.getWidth(), NULL, TRUE); + S32_MAX, mRect.getWidth(), NULL, TRUE, mUseEllipses); } } @@ -481,7 +483,7 @@ LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f text_box->mFontStyle = LLFontGL::getStyleFromString(font_style); } - BOOL mouse_opaque; + BOOL mouse_opaque = text_box->getMouseOpaque(); if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) { text_box->setMouseOpaque(mouse_opaque); diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index c7c79464a0..d25452b941 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -79,6 +79,7 @@ public: void setText( const LLStringExplicit& text ); void setWrappedText(const LLStringExplicit& text, F32 max_width = -1.0); // default width means use existing control width + void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; } void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; } void setBorderVisible(BOOL visible) { mBorderVisible = visible; } @@ -124,6 +125,7 @@ protected: U8 mFontStyle; // style bit flags for font BOOL mBorderDropShadowVisible; + BOOL mUseEllipses; S32 mHPad; S32 mVPad; diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 5c8b7c7281..8b9353eb8e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -310,9 +310,9 @@ LLTextEditor::LLTextEditor( mWordWrap( FALSE ), mTabToNextField( TRUE ), mCommitOnFocusLost( FALSE ), - mTakesFocus( TRUE ), mHideScrollbarForShortDocs( FALSE ), mTakesNonScrollClicks( TRUE ), + mTrackBottom( TRUE ), mAllowEmbeddedItems( allow_embedded_items ), mAcceptCallingCardNames(FALSE), mHandleEditKeysDirectly( FALSE ), @@ -1141,46 +1141,42 @@ void LLTextEditor::selectAll() BOOL LLTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) { - if (pointInView(x, y) && getVisible()) + for ( child_list_const_iter_t child_it = getChildList()->begin(); + child_it != getChildList()->end(); ++child_it) { - for ( child_list_const_iter_t child_it = getChildList()->begin(); - child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) - { - return TRUE; - } - } - - if( mSegments.empty() ) + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) { return TRUE; } + } - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) - { - BOOL has_tool_tip = FALSE; - has_tool_tip = cur_segment->getToolTip( msg ); + if( mSegments.empty() ) + { + return TRUE; + } - if( has_tool_tip ) - { - // Just use a slop area around the cursor - // Convert rect local to screen coordinates - S32 SLOP = 8; - localPointToScreen( - x - SLOP, y - SLOP, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; - sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; - } + LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); + if( cur_segment ) + { + BOOL has_tool_tip = FALSE; + has_tool_tip = cur_segment->getToolTip( msg ); + + if( has_tool_tip ) + { + // Just use a slop area around the cursor + // Convert rect local to screen coordinates + S32 SLOP = 8; + localPointToScreen( + x - SLOP, y - SLOP, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; + sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; } - return TRUE; } - return FALSE; + return TRUE; } BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) @@ -1263,7 +1259,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) handled = TRUE; } - if (mTakesFocus) + if (hasTabStop()) { setFocus( TRUE ); handled = TRUE; @@ -1436,11 +1432,6 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) if( !handled && mTakesNonScrollClicks) { - if (mTakesFocus) - { - setFocus( TRUE ); - } - setCursorAtLocalPos( x, y, FALSE ); deselect(); @@ -3160,6 +3151,9 @@ void LLTextEditor::draw() } LLView::draw(); // Draw children (scrollbar and border) } + + // remember if we are supposed to be at the bottom of the buffer + mScrolledToBottom = isScrolledToBottom(); } void LLTextEditor::reportBadKeystroke() @@ -3328,6 +3322,17 @@ void LLTextEditor::changeLine( S32 delta ) unbindEmbeddedChars( mGLFont ); } +BOOL LLTextEditor::isScrolledToTop() +{ + return mScrollbar->isAtBeginning(); +} + +BOOL LLTextEditor::isScrolledToBottom() +{ + return mScrollbar->isAtEnd(); +} + + void LLTextEditor::startOfLine() { S32 line, offset; @@ -3448,6 +3453,13 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) { LLView::reshape( width, height, called_from_parent ); + // if scrolled to bottom, stay at bottom + // unless user is editing text + if (mScrolledToBottom && mTrackBottom && !hasFocus()) + { + endOfDoc(); + } + updateTextRect(); S32 line_height = llround( mGLFont->getLineHeight() ); @@ -4234,6 +4246,8 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node) node->getAttributeBOOL("word_wrap", word_wrap); setWordWrap(word_wrap); + node->getAttributeBOOL("track_bottom", mTrackBottom); + LLColor4 color; if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color)) { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 7049de7b89..a2ce0d2c47 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -206,13 +206,13 @@ public: void setTabToNextField(BOOL b) { mTabToNextField = b; } void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; } - // If takes focus, will take keyboard focus on click. - void setTakesFocus(BOOL b) { mTakesFocus = b; } - // Hack to handle Notecards virtual BOOL importBuffer(const LLString& buffer ); virtual BOOL exportBuffer(LLString& buffer ); + // If takes focus, will take keyboard focus on click. + void setTakesFocus(BOOL b) { mTakesFocus = b; } + void setSourceID(const LLUUID& id) { mSourceID = id; } void setAcceptCallingCardNames(BOOL enable) { mAcceptCallingCardNames = enable; } @@ -244,7 +244,10 @@ public: void startOfLine(); void endOfLine(); void endOfDoc(); - + + BOOL isScrolledToTop(); + BOOL isScrolledToBottom(); + // Getters const LLWString& getWText() const; llwchar getWChar(S32 pos); @@ -439,6 +442,8 @@ protected: BOOL mTakesFocus; BOOL mHideScrollbarForShortDocs; BOOL mTakesNonScrollClicks; + BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize + BOOL mScrolledToBottom; BOOL mAllowEmbeddedItems; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 00a230dff3..7561fe8b48 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -79,7 +79,7 @@ LLVector2 LLUI::sGLScaleFactor(1.f, 1.f); LLWindow* LLUI::sWindow = NULL; LLHtmlHelp* LLUI::sHtmlHelp = NULL; BOOL LLUI::sShowXUINames = FALSE; -std::stack<LLRect> LLUI::sClipRectStack; +std::stack<LLRect> LLScreenClipRect::sClipRectStack; // // Functions @@ -410,39 +410,76 @@ void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max } -void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color ) +void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect ) { - gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color ); + if (NULL == image) + { + llwarns << "image == NULL; aborting function" << llendl; + return; + } + gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color, uv_rect ); } -void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { - gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color ); + if (NULL == image) + { + llwarns << "image == NULL; aborting function" << llendl; + return; + } + gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color, uv_rect ); } -void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color) +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect) { - stop_glerror(); - F32 border_scale = 1.f; - if (NULL == image) { llwarns << "image == NULL; aborting function" << llendl; return; } - if (border_height * 2 > height) - { - border_scale = (F32)height / ((F32)border_height * 2.f); - } - if (border_width * 2 > width) + // scale screen size of borders down + F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0); + F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0); + + LLRectf scale_rect(border_width_fraction, 1.f - border_height_fraction, 1.f - border_width_fraction, border_height_fraction); + gl_draw_scaled_image_with_border(x, y, width, height, image, color, solid_color, uv_rect, scale_rect); +} + +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect, const LLRectf& scale_rect) +{ + stop_glerror(); + + if (NULL == image) { - border_scale = llmin(border_scale, (F32)width / ((F32)border_width * 2.f)); + llwarns << "image == NULL; aborting function" << llendl; + return; } // scale screen size of borders down - S32 scaled_border_width = llfloor(border_scale * (F32)border_width); - S32 scaled_border_height = llfloor(border_scale * (F32)border_height); + LLRectf clipped_scale_rect = uv_rect; + clipped_scale_rect.intersectWith(scale_rect); + + LLRect draw_rect(0, height, width, 0); + LLRect draw_scale_rect(llround((F32)image->getWidth() * scale_rect.mLeft), + llround((F32)image->getHeight() * scale_rect.mTop), + llround((F32)image->getWidth() * scale_rect.mRight), + llround((F32)image->getHeight() * scale_rect.mBottom)); + // scale fixed region of image up with drawn region + draw_scale_rect.mRight += width - image->getWidth(); + draw_scale_rect.mTop += height - image->getHeight(); + + S32 border_shrink_width = llmax(0, draw_scale_rect.mLeft - draw_scale_rect.mRight); + S32 border_shrink_height = llmax(0, draw_scale_rect.mBottom - draw_scale_rect.mTop); + + F32 shrink_width_ratio = scale_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image->getWidth() * (1.f - scale_rect.getWidth())); + F32 shrink_height_ratio = scale_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image->getHeight() * (1.f - scale_rect.getHeight())); + + F32 shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio); + draw_scale_rect.mLeft = llround((F32)draw_scale_rect.mLeft * shrink_scale); + draw_scale_rect.mTop = llround(lerp((F32)height, (F32)draw_scale_rect.mTop, shrink_scale)); + draw_scale_rect.mRight = llround(lerp((F32)width, (F32)draw_scale_rect.mRight, shrink_scale)); + draw_scale_rect.mBottom = llround((F32)draw_scale_rect.mBottom * shrink_scale); LLGLSUIDefault gls_ui; @@ -470,127 +507,124 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border glColor4fv(color.mV); - F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0); - F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0); - glBegin(GL_QUADS); { // draw bottom left - glTexCoord2f(0.f, 0.f); + glTexCoord2d(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, 0); - glTexCoord2f(border_width_fraction, 0.f); - glVertex2i(scaled_border_width, 0); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, 0); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(0.f, border_height_fraction); - glVertex2i(0, scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(0, draw_scale_rect.mBottom); // draw bottom middle - glTexCoord2f(border_width_fraction, 0.f); - glVertex2i(scaled_border_width, 0); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, 0); - glTexCoord2f(1.f - border_width_fraction, 0.f); - glVertex2i(width - scaled_border_width, 0); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, 0); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); // draw bottom right - glTexCoord2f(1.f - border_width_fraction, 0.f); - glVertex2i(width - scaled_border_width, 0); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, 0); - glTexCoord2f(1.f, 0.f); + glTexCoord2d(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, 0); - glTexCoord2f(1.f, border_height_fraction); - glVertex2i(width, scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(width, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); // draw left - glTexCoord2f(0.f, border_height_fraction); - glVertex2i(0, scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(0, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(0.f, 1.f - border_height_fraction); - glVertex2i(0, height - scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(0, draw_scale_rect.mTop); // draw middle - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); // draw right - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(1.f, border_height_fraction); - glVertex2i(width, scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(width, draw_scale_rect.mBottom); - glTexCoord2f(1.f, 1.f - border_height_fraction); - glVertex2i(width, height - scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(width, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); // draw top left - glTexCoord2f(0.f, 1.f - border_height_fraction); - glVertex2i(0, height - scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(0, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f); - glVertex2i(scaled_border_width, height); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, height); - glTexCoord2f(0.f, 1.f); + glTexCoord2d(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, height); // draw top middle - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f); - glVertex2i(width - scaled_border_width, height); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mTop); + glVertex2i(draw_scale_rect.mRight, height); - glTexCoord2f(border_width_fraction, 1.f); - glVertex2i(scaled_border_width, height); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, height); // draw top right - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(1.f, 1.f - border_height_fraction); - glVertex2i(width, height - scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(width, draw_scale_rect.mTop); - glTexCoord2f(1.f, 1.f); + glTexCoord2d(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, height); - glTexCoord2f(1.f - border_width_fraction, 1.f); - glVertex2i(width - scaled_border_width, height); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mTop); + glVertex2i(draw_scale_rect.mRight, height); } glEnd(); } @@ -602,12 +636,12 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border } } -void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color) +void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { - gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color ); + gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color, uv_rect ); } -void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { if (NULL == image) { @@ -635,16 +669,16 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre glBegin(GL_QUADS); { - glTexCoord2f(1.f, 1.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, height ); - glTexCoord2f(0.f, 1.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, height ); - glTexCoord2f(0.f, 0.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, 0); - glTexCoord2f(1.f, 0.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, 0); } glEnd(); @@ -653,7 +687,7 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre } -void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { if (NULL == image) { @@ -673,16 +707,16 @@ void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageG glBegin(GL_QUADS); { - glTexCoord2f(1.f, 0.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, height ); - glTexCoord2f(0.f, 0.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, height ); - glTexCoord2f(0.f, 1.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, 0); - glTexCoord2f(1.f, 1.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, 0); } glEnd(); @@ -1584,40 +1618,6 @@ void LLUI::loadIdentity() LLFontGL::sCurOrigin.mZ = 0; } -//static -void LLUI::setScissorRegionScreen(const LLRect& rect) -{ - stop_glerror(); - S32 x,y,w,h; - x = llround(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]); - y = llround(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]); - w = llround(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]); - h = llround(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]); - glScissor( x,y,w,h ); - stop_glerror(); -} - -//static -void LLUI::setScissorRegionLocal(const LLRect& rect) -{ - stop_glerror(); - S32 screen_left = LLFontGL::sCurOrigin.mX + rect.mLeft; - S32 screen_bottom = LLFontGL::sCurOrigin.mY + rect.mBottom; - - S32 x,y,w,h; - - x = llround((F32)screen_left * LLUI::sGLScaleFactor.mV[VX]); - y = llround((F32)screen_bottom * LLUI::sGLScaleFactor.mV[VY]); - w = llround((F32)rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]); - h = llround((F32)rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]); - - w = llmax(0,w); - h = llmax(0,h); - - glScissor(x,y,w,h); - stop_glerror(); -} - //static void LLUI::setScaleFactor(const LLVector2 &scale_factor) { @@ -1738,64 +1738,169 @@ LLUUID LLUI::findAssetUUIDByName(const LLString &asset_name) return LLUUID( foundValue ); } +//static +LLUIImage* LLUI::getUIImageByName(const LLString& name) +{ + return sImageProvider->getUIImageByID(findAssetUUIDByName(name)); +} + + // static void LLUI::setHtmlHelp(LLHtmlHelp* html_help) { LLUI::sHtmlHelp = html_help; } +LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST), mEnabled(enabled) +{ + if (mEnabled) + { + pushClipRect(rect); + } + mScissorState.setEnabled(!sClipRectStack.empty()); + updateScissorRegion(); +} + +LLScreenClipRect::~LLScreenClipRect() +{ + if (mEnabled) + { + popClipRect(); + } + updateScissorRegion(); +} + //static -void LLUI::pushClipRect(const LLRect& rect) +void LLScreenClipRect::pushClipRect(const LLRect& rect) { LLRect combined_clip_rect = rect; if (!sClipRectStack.empty()) { - combined_clip_rect.intersectWith(sClipRectStack.top()); + LLRect top = sClipRectStack.top(); + combined_clip_rect.intersectWith(top); } sClipRectStack.push(combined_clip_rect); - setScissorRegionScreen(combined_clip_rect); } //static -void LLUI::popClipRect() +void LLScreenClipRect::popClipRect() { sClipRectStack.pop(); - if (!sClipRectStack.empty()) - { - setScissorRegionScreen(sClipRectStack.top()); - } } -LLClipRect::LLClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled) +//static +void LLScreenClipRect::updateScissorRegion() { - if (mEnabled) - { - LLUI::pushClipRect(rect); - } + if (sClipRectStack.empty()) return; + + LLRect rect = sClipRectStack.top(); + stop_glerror(); + S32 x,y,w,h; + x = llfloor(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]); + y = llfloor(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]); + w = llmax(0, llceil(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX])) + 1; + h = llmax(0, llceil(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY])) + 1; + glScissor( x,y,w,h ); + stop_glerror(); } -LLClipRect::~LLClipRect() + +LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled) +: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, + rect.mTop + LLFontGL::sCurOrigin.mY, + rect.mRight + LLFontGL::sCurOrigin.mX, + rect.mBottom + LLFontGL::sCurOrigin.mY), + enabled) { - if (mEnabled) - { - LLUI::popClipRect(); - } } -LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled) + +// +// LLUIImage +// + +LLUIImage::LLUIImage(LLPointer<LLImageGL> image) : + mImage(image), + mScaleRegion(0.f, 1.f, 1.f, 0.f), + mClipRegion(0.f, 1.f, 1.f, 0.f), + mUniformScaling(TRUE), + mNoClip(TRUE) +{ +} + +void LLUIImage::setClipRegion(const LLRectf& region) +{ + mClipRegion = region; + mNoClip = mClipRegion.mLeft == 0.f + && mClipRegion.mRight == 1.f + && mClipRegion.mBottom == 0.f + && mClipRegion.mTop == 1.f; +} + +void LLUIImage::setScaleRegion(const LLRectf& region) +{ + mScaleRegion = region; + mUniformScaling = mScaleRegion.mLeft == 0.f + && mScaleRegion.mRight == 1.f + && mScaleRegion.mBottom == 0.f + && mScaleRegion.mTop == 1.f; +} + +//TODO: move drawing implementation inside class +void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) { - if (mEnabled) - { - LLRect scissor_rect = rect; - scissor_rect.translate(LLFontGL::sCurOrigin.mX, LLFontGL::sCurOrigin.mY); - LLUI::pushClipRect(scissor_rect); - } + gl_draw_image(x, y, mImage, color, mClipRegion); } -LLLocalClipRect::~LLLocalClipRect() +void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) { - if (mEnabled) + if (mUniformScaling) { - LLUI::popClipRect(); + gl_draw_scaled_image(x, y, width, height, mImage, color, mClipRegion); } + else + { + gl_draw_scaled_image_with_border( + x, y, + width, height, + mImage, + color, + FALSE, + mClipRegion, + mScaleRegion); + } +} + +void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) +{ + gl_draw_scaled_image_with_border( + x, y, + width, height, + mImage, + color, + TRUE, + mClipRegion, + mScaleRegion); +} + +void LLUIImage::drawSolid(S32 x, S32 y, const LLColor4& color) +{ + gl_draw_scaled_image_with_border( + x, y, + getWidth(), getHeight(), + mImage, + color, + TRUE, + mClipRegion, + mScaleRegion); +} + +S32 LLUIImage::getWidth() +{ + return mImage->getWidth(0); +} + +S32 LLUIImage::getHeight() +{ + return mImage->getHeight(0); } diff --git a/indra/llui/llui.h b/indra/llui/llui.h index b98f4d5de2..05982aa9e2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -41,14 +41,15 @@ #include "llhtmlhelp.h" #include "llgl.h" #include <stack> +#include "llimagegl.h" class LLColor4; class LLVector3; class LLVector2; -class LLImageGL; class LLUUID; class LLWindow; class LLView; +class LLUIImage; // UI colors extern const LLColor4 UI_VERTEX_COLOR; @@ -83,13 +84,14 @@ void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color); void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color); -void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE); +void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); // Flip vertical, used for LLFloaterHTML -void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); +void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom); void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase = 0.f ); @@ -166,13 +168,12 @@ public: //helper functions (should probably move free standing rendering helper functions here) static LLString locateSkin(const LLString& filename); - static void pushClipRect(const LLRect& rect); - static void popClipRect(); static void setCursorPositionScreen(S32 x, S32 y); static void setCursorPositionLocal(LLView* viewp, S32 x, S32 y); static void setScaleFactor(const LLVector2& scale_factor); static void setLineWidth(F32 width); static LLUUID findAssetUUIDByName(const LLString& name); + static LLUIImage* getUIImageByName(const LLString& name); static LLVector2 getWindowSize(); static void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y); static void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y); @@ -180,10 +181,6 @@ public: static void glRectToScreen(const LLRect& gl, LLRect *screen); static void setHtmlHelp(LLHtmlHelp* html_help); -private: - static void setScissorRegionScreen(const LLRect& rect); - static void setScissorRegionLocal(const LLRect& rect); // works assuming LLUI::translate has been called - public: static LLControlGroup* sConfigGroup; static LLControlGroup* sColorsGroup; @@ -194,7 +191,6 @@ public: static LLWindow* sWindow; static BOOL sShowXUINames; static LLHtmlHelp* sHtmlHelp; - static std::stack<LLRect> sClipRectStack; }; @@ -286,6 +282,7 @@ typedef enum e_widget_type WIDGET_TYPE_MEMORY_VIEW, WIDGET_TYPE_FRAME_STAT_VIEW, WIDGET_TYPE_LAYOUT_STACK, + WIDGET_TYPE_FLYOUT_BUTTON, WIDGET_TYPE_DONTCARE, WIDGET_TYPE_COUNT } EWidgetType; @@ -382,24 +379,65 @@ protected: template <class T, class U> T* LLUISingleton<T,U>::sInstance = NULL; -class LLClipRect +class LLScreenClipRect { public: - LLClipRect(const LLRect& rect, BOOL enabled = TRUE); - virtual ~LLClipRect(); -protected: + LLScreenClipRect(const LLRect& rect, BOOL enabled = TRUE); + virtual ~LLScreenClipRect(); + +private: + static void pushClipRect(const LLRect& rect); + static void popClipRect(); + static void updateScissorRegion(); + +private: LLGLState mScissorState; BOOL mEnabled; + + static std::stack<LLRect> sClipRectStack; }; -class LLLocalClipRect +class LLLocalClipRect : public LLScreenClipRect { public: LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE); - virtual ~LLLocalClipRect(); +}; + +class LLUIImage : public LLRefCount +{ +public: + LLUIImage(LLPointer<LLImageGL> image); + + void setClipRegion(const LLRectf& region); + void setScaleRegion(const LLRectf& region); + + LLPointer<LLImageGL> getImage() { return mImage; } + + void draw(S32 x, S32 y, const LLColor4& color = UI_VERTEX_COLOR); + void draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color = UI_VERTEX_COLOR); + void drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color); + void drawSolid(S32 x, S32 y, const LLColor4& color); + + S32 getWidth(); + S32 getHeight(); + protected: - LLGLState mScissorState; - BOOL mEnabled; + LLRectf mScaleRegion; + LLRectf mClipRegion; + LLPointer<LLImageGL> mImage; + BOOL mUniformScaling; + BOOL mNoClip; +}; + +//RN: maybe this needs to moved elsewhere? +class LLImageProviderInterface +{ +public: + LLImageProviderInterface() {}; + virtual ~LLImageProviderInterface() {}; + + virtual LLUIImage* getUIImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0; + virtual LLImageGL* getImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0; }; #endif diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 7d354753d3..8645f50764 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -47,11 +47,53 @@ const U32 MAX_STRING_LENGTH = 10; -LLUICtrl::LLUICtrl() : - mCommitCallback(NULL), - mFocusLostCallback(NULL), +LLFocusableElement::LLFocusableElement() +: mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), + mFocusCallbackUserData(NULL) +{ +} + +void LLFocusableElement::onFocusReceived() +{ + if( mFocusReceivedCallback ) + { + mFocusReceivedCallback( this, mFocusCallbackUserData ); + } + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +void LLFocusableElement::onFocusLost() +{ + if( mFocusLostCallback ) + { + mFocusLostCallback( this, mFocusCallbackUserData ); + } + + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +BOOL LLFocusableElement::hasFocus() const +{ + return FALSE; +} + +void LLFocusableElement::setFocus(BOOL b) +{ +} + + + +LLUICtrl::LLUICtrl() : + mCommitCallback(NULL), + mLostTopCallback(NULL), mValidateCallback(NULL), mCallbackUserData(NULL), mTentative(FALSE), @@ -68,9 +110,7 @@ LLUICtrl::LLUICtrl(const LLString& name, const LLRect& rect, BOOL mouse_opaque, // of buttons in the UI. JC 7/20/2002 LLView( name, rect, mouse_opaque, reshape ), mCommitCallback( on_commit_callback) , - mFocusLostCallback( NULL ), - mFocusReceivedCallback( NULL ), - mFocusChangedCallback( NULL ), + mLostTopCallback( NULL ), mValidateCallback( NULL ), mCallbackUserData( callback_userdata ), mTentative( FALSE ), @@ -128,6 +168,86 @@ LLCtrlScrollInterface* LLUICtrl::getScrollInterface() return NULL; } +BOOL LLUICtrl::hasFocus() const +{ + return (gFocusMgr.childHasKeyboardFocus(this)); +} + +void LLUICtrl::setFocus(BOOL b) +{ + // focus NEVER goes to ui ctrls that are disabled! + if (!mEnabled) + { + return; + } + if( b ) + { + if (!hasFocus()) + { + gFocusMgr.setKeyboardFocus( this ); + } + } + else + { + if( gFocusMgr.childHasKeyboardFocus(this)) + { + gFocusMgr.setKeyboardFocus( NULL ); + } + } +} + +void LLUICtrl::onFocusReceived() +{ + // trigger callbacks + LLFocusableElement::onFocusReceived(); + + // find first view in hierarchy above new focus that is a LLUICtrl + LLView* viewp = getParent(); + LLUICtrl* last_focus = gFocusMgr.getLastKeyboardFocus(); + + while (viewp && !viewp->isCtrl()) + { + viewp = viewp->getParent(); + } + + // and if it has newly gained focus, call onFocusReceived() + LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp); + if (ctrlp && (!last_focus || !last_focus->hasAncestor(ctrlp))) + { + ctrlp->onFocusReceived(); + } +} + +void LLUICtrl::onFocusLost() +{ + // trigger callbacks + LLFocusableElement::onFocusLost(); + + // find first view in hierarchy above old focus that is a LLUICtrl + LLView* viewp = getParent(); + while (viewp && !viewp->isCtrl()) + { + viewp = viewp->getParent(); + } + + // and if it has just lost focus, call onFocusReceived() + LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp); + // hasFocus() includes any descendants + if (ctrlp && !ctrlp->hasFocus()) + { + ctrlp->onFocusLost(); + } +} + +void LLUICtrl::onLostTop() +{ + if (mLostTopCallback) + { + mLostTopCallback(this, mCallbackUserData); + } +} + + // virtual void LLUICtrl::setTabStop( BOOL b ) { @@ -168,67 +288,6 @@ BOOL LLUICtrl::getIsChrome() const return mIsChrome; } -void LLUICtrl::onFocusReceived() -{ - if( mFocusReceivedCallback ) - { - mFocusReceivedCallback( this, mCallbackUserData ); - } - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mCallbackUserData ); - } -} - -void LLUICtrl::onFocusLost() -{ - if( mFocusLostCallback ) - { - mFocusLostCallback( this, mCallbackUserData ); - } - - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mCallbackUserData ); - } -} - -BOOL LLUICtrl::hasFocus() const -{ - return (gFocusMgr.childHasKeyboardFocus(this)); -} - -void LLUICtrl::setFocus(BOOL b) -{ - // focus NEVER goes to ui ctrls that are disabled! - if (!mEnabled) - { - return; - } - if( b ) - { - if (!hasFocus()) - { - gFocusMgr.setKeyboardFocus( this, &LLUICtrl::onFocusLostCallback ); - onFocusReceived(); - } - } - else - { - if( gFocusMgr.childHasKeyboardFocus(this)) - { - gFocusMgr.setKeyboardFocus( NULL, NULL ); - onFocusLost(); - } - } -} - -// static -void LLUICtrl::onFocusLostCallback( LLUICtrl* old_focus ) -{ - old_focus->onFocusLost(); -} - // this comparator uses the crazy disambiguating logic of LLCompareByTabOrder, // but to switch up the order so that children that have the default tab group come first // and those that are prior to the default tab group come last @@ -262,6 +321,7 @@ public: } }; + BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields) { // try to select default tab group child diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 00f78748a7..ae360f401f 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -53,8 +53,31 @@ class LLCtrlScrollInterface; typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata); typedef BOOL (*LLUICtrlValidate)(LLUICtrl* ctrl, void* userdata); +class LLFocusableElement +{ + friend class LLFocusMgr; // allow access to focus change handlers +public: + LLFocusableElement(); + virtual ~LLFocusableElement() {}; + + virtual void setFocus( BOOL b ); + virtual BOOL hasFocus() const; + + void setFocusLostCallback(void (*cb)(LLFocusableElement* caller, void*), void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusReceivedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusChangedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; } + +protected: + virtual void onFocusReceived(); + virtual void onFocusLost(); + void (*mFocusLostCallback)( LLFocusableElement* caller, void* userdata ); + void (*mFocusReceivedCallback)( LLFocusableElement* ctrl, void* userdata ); + void (*mFocusChangedCallback)( LLFocusableElement* ctrl, void* userdata ); + void* mFocusCallbackUserData; +}; + class LLUICtrl -: public LLView +: public LLView, public LLFocusableElement { public: LLUICtrl(); @@ -85,6 +108,11 @@ public: virtual void setFocus( BOOL b ); virtual BOOL hasFocus() const; + virtual void onFocusReceived(); + virtual void onFocusLost(); + + virtual void onLostTop(); // called when registered as top ctrl and user clicks elsewhere + virtual void setTabStop( BOOL b ); virtual BOOL hasTabStop() const; @@ -115,6 +143,7 @@ public: void setCommitCallback( void (*cb)(LLUICtrl*, void*) ) { mCommitCallback = cb; } void setValidateBeforeCommit( BOOL(*cb)(LLUICtrl*, void*) ) { mValidateCallback = cb; } + void setLostTopCallback( void (*cb)(LLUICtrl*, void*) ) { mLostTopCallback = cb; } // Defaults to no-op! virtual void setDoubleClickCallback( void (*cb)(void*) ); @@ -126,23 +155,8 @@ public: virtual void setMinValue(LLSD min_value); virtual void setMaxValue(LLSD max_value); - // In general, only LLPanel uses these. - void setFocusLostCallback(void (*cb)(LLUICtrl* caller, void* user_data)) { mFocusLostCallback = cb; } - void setFocusReceivedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusReceivedCallback = cb; } - void setFocusChangedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusChangedCallback = cb; } - - static void onFocusLostCallback(LLUICtrl* old_focus); - /*virtual*/ BOOL focusFirstItem(BOOL prefer_text_fields = FALSE ); - class LLTabStopPostFilter : public LLQueryFilter, public LLSingleton<LLTabStopPostFilter> - { - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const - { - return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->hasTabStop() && children.size() == 0, TRUE); - } - }; - class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const @@ -157,16 +171,9 @@ public: virtual void resetDirty() {}; protected: - virtual void onFocusReceived(); - virtual void onFocusLost(); - void onChangeFocus( S32 direction ); - -protected: void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata ); - void (*mFocusLostCallback)( LLUICtrl* caller, void* userdata ); - void (*mFocusReceivedCallback)( LLUICtrl* ctrl, void* userdata ); - void (*mFocusChangedCallback)( LLUICtrl* ctrl, void* userdata ); + void (*mLostTopCallback)( LLUICtrl* ctrl, void* userdata ); BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); void* mCallbackUserData; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 88e4e89a2b..1e8798e7f7 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -173,7 +173,7 @@ std::vector<LLString> LLUICtrlFactory::mXUIPaths; class LLUICtrlLocate : public LLUICtrl { public: - LLUICtrlLocate() : LLUICtrl("locate", LLRect(0,0,0,0), FALSE, NULL, NULL) {} + LLUICtrlLocate() : LLUICtrl("locate", LLRect(0,0,0,0), FALSE, NULL, NULL) { setTabStop(FALSE); } virtual void draw() { } virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LOCATE; } @@ -181,7 +181,11 @@ public: static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { + LLString name("pad"); + node->getAttributeString("name", name); + LLUICtrlLocate *new_ctrl = new LLUICtrlLocate(); + new_ctrl->setName(name); new_ctrl->initFromXML(node, parent); return new_ctrl; } @@ -196,6 +200,7 @@ LLUICtrlFactory::LLUICtrlFactory() LLUICtrlCreator<LLButton>::registerCreator(LL_BUTTON_TAG, this); LLUICtrlCreator<LLCheckBoxCtrl>::registerCreator(LL_CHECK_BOX_CTRL_TAG, this); LLUICtrlCreator<LLComboBox>::registerCreator(LL_COMBO_BOX_TAG, this); + LLUICtrlCreator<LLFlyoutButton>::registerCreator(LL_FLYOUT_BUTTON_TAG, this); LLUICtrlCreator<LLLineEditor>::registerCreator(LL_LINE_EDITOR_TAG, this); LLUICtrlCreator<LLSearchEditor>::registerCreator(LL_SEARCH_EDITOR_TAG, this); LLUICtrlCreator<LLScrollListCtrl>::registerCreator(LL_SCROLL_LIST_CTRL_TAG, this); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index a047f9912e..370288e949 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -113,7 +113,7 @@ LLView::LLView() : mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -133,7 +133,7 @@ LLView::LLView(const LLString& name, BOOL mouse_opaque) : mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -148,6 +148,7 @@ LLView::LLView( mParentView(NULL), mName(name), mRect(rect), + mBoundingRect(rect), mReshapeFlags(reshape), mDefaultTabGroup(0), mEnabled(TRUE), @@ -156,7 +157,7 @@ LLView::LLView( mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -235,10 +236,16 @@ BOOL LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& return TRUE; } +void LLView::setToolTipArgs( const LLString::format_map_t& args ) +{ + mToolTipMsg.setArgList(args); +} + // virtual void LLView::setRect(const LLRect& rect) { mRect = rect; + updateBoundingRect(); } @@ -287,9 +294,18 @@ void LLView::setName(LLString name) mName = name; } -void LLView::setSpanChildren( BOOL span_children ) +void LLView::setUseBoundingRect( BOOL use_bounding_rect ) +{ + if (mUseBoundingRect != use_bounding_rect) + { + mUseBoundingRect = use_bounding_rect; + updateBoundingRect(); + } +} + +BOOL LLView::getUseBoundingRect() { - mSpanChildren = span_children; updateRect(); + return mUseBoundingRect; } const LLString& LLView::getToolTip() @@ -306,7 +322,7 @@ const LLString& LLView::getName() const void LLView::sendChildToFront(LLView* child) { - if (child->mParentView == this) + if (child && child->getParent() == this) { mChildList.remove( child ); mChildList.push_front(child); @@ -315,7 +331,7 @@ void LLView::sendChildToFront(LLView* child) void LLView::sendChildToBack(LLView* child) { - if (child->mParentView == this) + if (child && child->getParent() == this) { mChildList.remove( child ); mChildList.push_back(child); @@ -330,6 +346,14 @@ void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child) } } +void LLView::moveChildToBackOfTabGroup(LLUICtrl* child) +{ + if(mCtrlOrder.find(child) != mCtrlOrder.end()) + { + mCtrlOrder[child].second = mNextInsertionOrdinal++; + } +} + void LLView::addChild(LLView* child, S32 tab_group) { if (mParentView == child) @@ -353,7 +377,7 @@ void LLView::addChild(LLView* child, S32 tab_group) } child->mParentView = this; - updateRect(); + updateBoundingRect(); } @@ -380,7 +404,7 @@ void LLView::addChildAtEnd(LLView* child, S32 tab_group) } child->mParentView = this; - updateRect(); + updateBoundingRect(); } // remove the specified child from the view, and set it's parent to NULL. @@ -403,6 +427,7 @@ void LLView::removeChild(LLView* child, BOOL deleteIt) { llerrs << "LLView::removeChild called with non-child" << llendl; } + updateBoundingRect(); } void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group) @@ -782,6 +807,7 @@ void LLView::setVisible(BOOL visible) // tell all children of this view that the visibility may have changed onVisibilityChange( visible ); } + updateBoundingRect(); } } @@ -815,6 +841,7 @@ void LLView::onVisibilityChange ( BOOL new_visibility ) void LLView::translate(S32 x, S32 y) { mRect.translate(x, y); + updateBoundingRect(); } // virtual @@ -831,7 +858,8 @@ void LLView::snappedTo(LLView* snap_view) BOOL LLView::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleHover( x, y, mask ) != NULL; - if( !handled && mMouseOpaque && pointInView( x, y ) ) + if( !handled + && blockMouseEvent(x, y) ) { LLUI::sWindow->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; @@ -876,45 +904,46 @@ BOOL LLView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_scre LLString tool_tip; - if ( getVisible() && getEnabled()) + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + LLView* viewp = *child_it; + S32 local_x = x - viewp->mRect.mLeft; + S32 local_y = y - viewp->mRect.mBottom; + if( viewp->pointInView(local_x, local_y) + && viewp->getVisible() + && viewp->getEnabled() + && viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen )) { - LLView* viewp = *child_it; - S32 local_x = x - viewp->mRect.mLeft; - S32 local_y = y - viewp->mRect.mBottom; - if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) - { - handled = TRUE; - break; - } + handled = TRUE; + break; } + } - tool_tip = mToolTipMsg.getString(); - if (LLUI::sShowXUINames && (tool_tip.find(".xml", 0) == LLString::npos) && - (mName.find("Drag", 0) == LLString::npos)) - { - tool_tip = getShowNamesToolTip(); - } - + tool_tip = mToolTipMsg.getString(); + if ( + LLUI::sShowXUINames && + (tool_tip.find(".xml", 0) == LLString::npos) && + (mName.find("Drag", 0) == LLString::npos)) + { + tool_tip = getShowNamesToolTip(); + } - BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX); + BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX); - if( !handled && (mMouseOpaque || showNamesTextBox) && pointInView( x, y ) && !tool_tip.empty()) - { + if( !handled && (blockMouseEvent(x, y) || showNamesTextBox) && !tool_tip.empty()) + { - msg = tool_tip; + msg = tool_tip; - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - mRect.getWidth(), mRect.getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); - - handled = TRUE; - } + // Convert rect local to screen coordinates + localPointToScreen( + 0, 0, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + mRect.getWidth(), mRect.getHeight(), + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + + handled = TRUE; } return handled; @@ -1025,7 +1054,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, cargo_data, accept, tooltip_msg) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { *accept = ACCEPT_NO; handled = TRUE; @@ -1081,7 +1110,7 @@ BOOL LLView::hasMouseCapture() BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1092,7 +1121,7 @@ BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) { LLView* handled_view = childrenHandleMouseDown( x, y, mask ); BOOL handled = (handled_view != NULL); - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; handled_view = this; @@ -1118,7 +1147,7 @@ BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handleMouseDown(x, y, mask); handled = TRUE; @@ -1132,7 +1161,7 @@ BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) if( getVisible() && mEnabled ) { handled = childrenHandleScrollWheel( x, y, clicks ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1143,7 +1172,7 @@ BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1153,7 +1182,7 @@ BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1428,10 +1457,10 @@ void LLView::draw() focus_view = NULL; } + ++sDepth; for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) { LLView *viewp = *child_iter; - ++sDepth; if (viewp->getVisible() && viewp != focus_view) { @@ -1449,8 +1478,8 @@ void LLView::draw() } } - --sDepth; } + --sDepth; if (focus_view && focus_view->getVisible()) { @@ -1467,50 +1496,61 @@ void LLView::draw() //Draw a box for debugging. void LLView::drawDebugRect() { - // drawing solids requires texturing be disabled - LLGLSNoTexture no_texture; - - // draw red rectangle for the border - LLColor4 border_color(0.f, 0.f, 0.f, 1.f); - if (sEditingUI) + LLUI::pushMatrix(); { - border_color.mV[0] = 1.f; - } - else - { - border_color.mV[sDepth%3] = 1.f; - } + // drawing solids requires texturing be disabled + LLGLSNoTexture no_texture; - glColor4fv( border_color.mV ); + if (mUseBoundingRect) + { + LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom, 0.f); + } - glBegin(GL_LINES); - glVertex2i(0, mRect.getHeight() - 1); - glVertex2i(0, 0); + LLRect debug_rect = mUseBoundingRect ? mBoundingRect : mRect; - glVertex2i(0, 0); - glVertex2i(mRect.getWidth() - 1, 0); + // draw red rectangle for the border + LLColor4 border_color(0.f, 0.f, 0.f, 1.f); + if (sEditingUI) + { + border_color.mV[0] = 1.f; + } + else + { + border_color.mV[sDepth%3] = 1.f; + } - glVertex2i(mRect.getWidth() - 1, 0); - glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1); + glColor4fv( border_color.mV ); - glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1); - glVertex2i(0, mRect.getHeight() - 1); - glEnd(); + glBegin(GL_LINES); + glVertex2i(0, debug_rect.getHeight() - 1); + glVertex2i(0, 0); - // Draw the name if it's not a leaf node - if (mChildList.size() && !sEditingUI) - { - //char temp[256]; - S32 x, y; - glColor4fv( border_color.mV ); - x = mRect.getWidth()/2; - y = mRect.getHeight()/2; - LLString debug_text = llformat("%s (%d x %d)", getName().c_str(), - mRect.getWidth(), mRect.getHeight()); - LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, - LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, - S32_MAX, S32_MAX, NULL, FALSE); + glVertex2i(0, 0); + glVertex2i(debug_rect.getWidth() - 1, 0); + + glVertex2i(debug_rect.getWidth() - 1, 0); + glVertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + + glVertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + glVertex2i(0, debug_rect.getHeight() - 1); + glEnd(); + + // Draw the name if it's not a leaf node + if (mChildList.size() && !sEditingUI) + { + //char temp[256]; + S32 x, y; + glColor4fv( border_color.mV ); + x = debug_rect.getWidth()/2; + y = debug_rect.getHeight()/2; + LLString debug_text = llformat("%s (%d x %d)", getName().c_str(), + debug_rect.getWidth(), debug_rect.getHeight()); + LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, + LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, + S32_MAX, S32_MAX, NULL, FALSE); + } } + LLUI::popMatrix(); } void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw) @@ -1537,9 +1577,6 @@ void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_dr void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) { - // make sure this view contains all its children - updateRect(); - // compute how much things changed and apply reshape logic to children S32 delta_width = width - mRect.getWidth(); S32 delta_height = height - mRect.getHeight(); @@ -1608,6 +1645,8 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE); } } + + updateBoundingRect(); } LLRect LLView::getRequiredRect() @@ -1615,6 +1654,53 @@ LLRect LLView::getRequiredRect() return mRect; } +void LLView::updateBoundingRect() +{ + if (isDead()) return; + + if (mUseBoundingRect) + { + LLRect local_bounding_rect = LLRect::null; + + child_list_const_iter_t child_it; + for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + { + LLView* childp = *child_it; + if (!childp->getVisible()) continue; + + LLRect child_bounding_rect = childp->getBoundingRect(); + + if (local_bounding_rect.isNull()) + { + // start out with bounding rect equal to first visible child's bounding rect + local_bounding_rect = child_bounding_rect; + } + else + { + // accumulate non-null children rectangles + if (!child_bounding_rect.isNull()) + { + local_bounding_rect.unionWith(child_bounding_rect); + } + } + } + + mBoundingRect = local_bounding_rect; + // translate into parent-relative coordinates + mBoundingRect.translate(mRect.mLeft, mRect.mBottom); + } + else + { + mBoundingRect = mRect; + } + + // give parent view a chance to resize, in case we just moved, for example + if (getParent() && getParent()->mUseBoundingRect) + { + getParent()->updateBoundingRect(); + } +} + const LLRect LLView::getScreenRect() const { // *FIX: check for one-off error @@ -1624,6 +1710,15 @@ const LLRect LLView::getScreenRect() const return screen_rect; } +const LLRect LLView::getLocalBoundingRect() const +{ + LLRect local_bounding_rect = getBoundingRect(); + local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); + + return local_bounding_rect; +} + + const LLRect LLView::getLocalRect() const { LLRect local_rect(0, mRect.getHeight(), mRect.getWidth(), 0); @@ -1637,38 +1732,7 @@ const LLRect LLView::getLocalSnapRect() const return local_snap_rect; } -void LLView::updateRect() -{ - if (mSpanChildren && mChildList.size()) - { - LLView* first_child = (*mChildList.begin()); - LLRect child_spanning_rect = first_child->mRect; - - for ( child_list_iter_t child_it = ++mChildList.begin(); child_it != mChildList.end(); ++child_it) - { - LLView* viewp = *child_it; - if (viewp->getVisible()) - { - child_spanning_rect.unionWith(viewp->mRect); - } - } - - S32 translate_x = llmin(0, child_spanning_rect.mLeft); - S32 translate_y = llmin(0, child_spanning_rect.mBottom); - S32 new_width = llmax(mRect.getWidth() + translate_x, child_spanning_rect.getWidth()); - S32 new_height = llmax(mRect.getHeight() + translate_y, child_spanning_rect.getHeight()); - - mRect.setOriginAndSize(mRect.mLeft + translate_x, mRect.mBottom + translate_y, new_width, new_height); - - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) - { - LLView* viewp = *child_it; - viewp->mRect.translate(-translate_x, -translate_y); - } - } -} - -BOOL LLView::hasAncestor(LLView* parentp) +BOOL LLView::hasAncestor(const LLView* parentp) { if (!parentp) { @@ -1743,14 +1807,23 @@ LLView* LLView::getChildByName(const LLString& name, BOOL recurse) const return NULL; } -// virtual -void LLView::onFocusLost() -{ +BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const +{ + return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + ? mBoundingRect.pointInRect( x, y ) + : mRect.pointInRect( x, y ); } -// virtual -void LLView::onFocusReceived() +BOOL LLView::pointInView(S32 x, S32 y, EHitTestType type) const +{ + return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) + : mRect.localPointInRect( x, y ); +} + +BOOL LLView::blockMouseEvent(S32 x, S32 y) const { + return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT); } // virtual @@ -2024,9 +2097,9 @@ LLXMLNodePtr LLView::getXML(bool save_children) const // Export all widgets as enabled and visible - code must disable. node->createChild("hidden", TRUE)->setBoolValue(mHidden); node->createChild("mouse_opaque", TRUE)->setBoolValue(mMouseOpaque ); - if (!mToolTipMsg.empty()) + if (!mToolTipMsg.getString().empty()) { - node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg); + node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg.getString()); } if (mSoundFlags != MOUSE_UP) { @@ -2116,7 +2189,7 @@ const LLCtrlQuery & LLView::getTabOrderQuery() query.addPreFilter(LLVisibleFilter::getInstance()); query.addPreFilter(LLEnabledFilter::getInstance()); query.addPreFilter(LLTabStopFilter::getInstance()); - query.addPostFilter(LLUICtrl::LLTabStopPostFilter::getInstance()); + query.addPostFilter(LLLeavesFilter::getInstance()); } return query; } @@ -2129,6 +2202,7 @@ const LLCtrlQuery & LLView::getFocusRootsQuery() query.addPreFilter(LLVisibleFilter::getInstance()); query.addPreFilter(LLEnabledFilter::getInstance()); query.addPreFilter(LLView::LLFocusRootsFilter::getInstance()); + query.addPostFilter(LLRootsFilter::getInstance()); } return query; } @@ -2593,10 +2667,10 @@ const S32 VPAD = 4; U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect) { U32 follows = 0; - S32 x = FLOATER_H_MARGIN; - S32 y = 0; - S32 w = 0; - S32 h = 0; + S32 x = rect.mLeft; + S32 y = rect.mBottom; + S32 w = rect.getWidth(); + S32 h = rect.getHeight(); U32 last_x = 0; U32 last_y = 0; @@ -2639,8 +2713,15 @@ U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, con // view if you don't specify a width. if (parent_view) { - w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x); - h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight()); + if(w == 0) + { + w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x); + } + + if(h == 0) + { + h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight()); + } } if (node->hasAttribute("width")) @@ -2765,44 +2846,7 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) setRect(view_rect); setFollows(follows_flags); - if (node->hasAttribute("follows")) - { - setFollowsNone(); - - LLString follows; - node->getAttributeString("follows", follows); - - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("|"); - tokenizer tokens(follows, sep); - tokenizer::iterator token_iter = tokens.begin(); - - while(token_iter != tokens.end()) - { - const std::string& token_str = *token_iter; - if (token_str == "left") - { - setFollowsLeft(); - } - else if (token_str == "right") - { - setFollowsRight(); - } - else if (token_str == "top") - { - setFollowsTop(); - } - else if (token_str == "bottom") - { - setFollowsBottom(); - } - else if (token_str == "all") - { - setFollowsAll(); - } - ++token_iter; - } - } + parseFollowsFlags(node); if (node->hasAttribute("control_name")) { @@ -2839,11 +2883,57 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) setHidden(hidden); } + node->getAttributeBOOL("use_bounding_rect", mUseBoundingRect); + node->getAttributeBOOL("mouse_opaque", mMouseOpaque); + node->getAttributeS32("default_tab_group", mDefaultTabGroup); reshape(view_rect.getWidth(), view_rect.getHeight()); } +void LLView::parseFollowsFlags(LLXMLNodePtr node) +{ + if (node->hasAttribute("follows")) + { + setFollowsNone(); + + LLString follows; + node->getAttributeString("follows", follows); + + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|"); + tokenizer tokens(follows, sep); + tokenizer::iterator token_iter = tokens.begin(); + + while(token_iter != tokens.end()) + { + const std::string& token_str = *token_iter; + if (token_str == "left") + { + setFollowsLeft(); + } + else if (token_str == "right") + { + setFollowsRight(); + } + else if (token_str == "top") + { + setFollowsTop(); + } + else if (token_str == "bottom") + { + setFollowsBottom(); + } + else if (token_str == "all") + { + setFollowsAll(); + } + ++token_iter; + } + } +} + + // static LLFontGL* LLView::selectFont(LLXMLNodePtr node) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 8248d50d9d..e54983d67d 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -50,6 +50,7 @@ #include "llviewquery.h" #include "llxmlnode.h" #include "stdenums.h" +#include "lluistring.h" class LLColor4; class LLWindow; @@ -146,6 +147,7 @@ protected: LLString mName; // location in pixels, relative to surrounding structure, bottom,left=0,0 LLRect mRect; + LLRect mBoundingRect; U32 mReshapeFlags; @@ -161,11 +163,11 @@ protected: BOOL mSaveToXML; BOOL mIsFocusRoot; + BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements public: LLViewHandle mViewHandle; BOOL mLastVisible; - BOOL mSpanChildren; private: BOOL mVisible; @@ -217,6 +219,7 @@ public: void setMouseOpaque( BOOL b ); void setToolTip( const LLStringExplicit& msg ); BOOL setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); + void setToolTipArgs( const LLString::format_map_t& args ); virtual void setRect(const LLRect &rect); void setFollows(U32 flags); @@ -231,13 +234,15 @@ public: void setSoundFlags(U8 flags); void setName(LLString name); - void setSpanChildren( BOOL span_children ); + void setUseBoundingRect( BOOL use_bounding_rect ); + BOOL getUseBoundingRect(); const LLString& getToolTip(); void sendChildToFront(LLView* child); void sendChildToBack(LLView* child); void moveChildToFrontOfTabGroup(LLUICtrl* child); + void moveChildToBackOfTabGroup(LLUICtrl* child); void addChild(LLView* view, S32 tab_group = 0); void addChildAtEnd(LLView* view, S32 tab_group = 0); @@ -264,7 +269,7 @@ public: { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot()); + return filterResult_t(view->isCtrl() && view->isFocusRoot(), TRUE); } }; @@ -312,20 +317,22 @@ public: BOOL followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } const LLRect& getRect() const { return mRect; } + const LLRect& getBoundingRect() const { return mBoundingRect; } + const LLRect getLocalBoundingRect() const; const LLRect getScreenRect() const; const LLRect getLocalRect() const; virtual const LLRect getSnapRect() const { return mRect; } virtual const LLRect getLocalSnapRect() const; virtual LLRect getRequiredRect(); // Get required size for this object. 0 for width/height means don't care. - virtual void updateRect(); // apply procedural updates to own rectangle + void updateBoundingRect(); LLView* getRootView(); LLView* getParent() const { return mParentView; } LLView* getFirstChild() { return (mChildList.empty()) ? NULL : *(mChildList.begin()); } S32 getChildCount() const { return (S32)mChildList.size(); } template<class _Pr3> void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); } - BOOL hasAncestor(LLView* parentp); + BOOL hasAncestor(const LLView* parentp); BOOL hasChild(const LLString& childname, BOOL recurse = FALSE) const; @@ -390,6 +397,7 @@ public: static U32 createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect = LLRect()); virtual void initFromXML(LLXMLNodePtr node, LLView* parent); + void parseFollowsFlags(LLXMLNodePtr node); static LLFontGL* selectFont(LLXMLNodePtr node); static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); @@ -428,12 +436,16 @@ public: BOOL getVisible() const { return mVisible && !mHidden; } U8 getSoundFlags() const { return mSoundFlags; } - // Default to no action - virtual void onFocusLost(); - virtual void onFocusReceived(); + typedef enum e_hit_test_type + { + HIT_TEST_USE_BOUNDING_RECT, + HIT_TEST_IGNORE_BOUNDING_RECT + }EHitTestType; + + BOOL parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + BOOL pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + BOOL blockMouseEvent(S32 x, S32 y) const; - BOOL parentPointInView(S32 x, S32 y) const { return mRect.pointInRect( x, y ); } - BOOL pointInView(S32 x, S32 y) const { return mRect.localPointInRect( x, y ); } virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; virtual BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view); diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index f6cbe3aac1..8fbe671613 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -84,7 +84,7 @@ void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColo void LLViewBorder::setTexture( const LLUUID &image_id ) { - mTexture = LLUI::sImageProvider->getUIImageByID(image_id); + mTexture = LLUI::sImageProvider->getImageByID(image_id); } diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index c07587f0ff..db00c76821 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -37,9 +37,14 @@ void LLQuerySorter::operator() (LLView * parent, viewList_t &children) const {} -filterResult_t LLNoLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const +filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(!(view->getChildList()->size() == 0), TRUE); + return filterResult_t(children.empty(), TRUE); +} + +filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(TRUE, FALSE); } filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const @@ -56,6 +61,16 @@ filterResult_t LLTabStopFilter::operator() (const LLView* const view, const view view->canFocusChildren()); } +filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->isCtrl(),TRUE); +} + +filterResult_t LLWidgetTypeFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->getWidgetType() == mType, TRUE); +} + // LLViewQuery LLViewQuery::LLViewQuery(): mPreFilters(), mPostFilters(), mSorterp() @@ -73,45 +88,53 @@ const LLViewQuery::filterList_t & LLViewQuery::getPostFilters() const { return m void LLViewQuery::setSorter(const LLQuerySorter* sorterp) { mSorterp = sorterp; } const LLQuerySorter* LLViewQuery::getSorter() const { return mSorterp; } -viewList_t LLViewQuery::run(LLView * view) const +viewList_t LLViewQuery::run(LLView* view) const { viewList_t result; - filterResult_t pre = runFilters(view, viewList_t(), mPreFilters); + // prefilter gets immediate children of view + filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); if(!pre.first && !pre.second) { - // skip post filters completely if we're not including ourselves or the children + // not including ourselves or the children + // nothing more to do return result; } + + viewList_t filtered_children; + filterResult_t post(TRUE, TRUE); if(pre.second) { // run filters on children - viewList_t filtered_children; filterChildren(view, filtered_children); - filterResult_t post = runFilters(view, filtered_children, mPostFilters); - if(pre.first && post.first) - { - result.push_back(view); - } - if(post.second) + // only run post filters if this element passed pre filters + // so if you failed to pass the pre filter, you can't filter out children in post + if (pre.first) { - result.insert(result.end(), filtered_children.begin(), filtered_children.end()); + post = runFilters(view, filtered_children, mPostFilters); } } - else + + if(pre.first && post.first) { - if(pre.first) - { - result.push_back(view); - } + result.push_back(view); + } + + if(pre.second && post.second) + { + result.insert(result.end(), filtered_children.begin(), filtered_children.end()); } + return result; } void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { LLView::child_list_t views(*(view->getChildList())); - (*mSorterp)(view, views); // sort the children per the sorter + if (mSorterp) + { + (*mSorterp)(view, views); // sort the children per the sorter + } for(LLView::child_list_iter_t iter = views.begin(); iter != views.end(); iter++) diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 3919ba4bcb..63559e8240 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -35,6 +35,7 @@ #include <list> #include "llmemory.h" +#include "llui.h" class LLView; @@ -42,35 +43,60 @@ typedef std::list<LLView *> viewList_t; typedef std::pair<BOOL, BOOL> filterResult_t; // Abstract base class for all filters. -class LLQueryFilter : public LLRefCount +class LLQueryFilter { public: + virtual ~LLQueryFilter() {}; virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const =0; }; -class LLQuerySorter : public LLRefCount +class LLQuerySorter { public: + virtual ~LLQuerySorter() {}; virtual void operator() (LLView * parent, viewList_t &children) const; }; -class LLNoLeavesFilter : public LLQueryFilter, public LLSingleton<LLNoLeavesFilter> +class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + +class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter> +{ + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +}; + class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; +class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter> +{ + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +}; + +class LLWidgetTypeFilter : public LLQueryFilter +{ +public: + LLWidgetTypeFilter(EWidgetType type) : mType(type) {}; +private: + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; + + EWidgetType mType; +}; + // Algorithm for flattening class LLViewQuery { diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 5aaf9d0097..a692ef6a3e 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2880,7 +2880,7 @@ void LLAgent::endAnimationUpdateUI() mCameraLag.clearVec(); // JC - Added for always chat in third person option - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); gToolMgr->setCurrentToolset(gMouselookToolset); @@ -4004,7 +4004,7 @@ void LLAgent::changeCameraToMouselook(BOOL animate) if( mCameraMode != CAMERA_MODE_MOUSELOOK ) { - gViewerWindow->setKeyboardFocus( NULL, NULL ); + gViewerWindow->setKeyboardFocus( NULL ); mLastCameraMode = mCameraMode; mCameraMode = CAMERA_MODE_MOUSELOOK; @@ -4225,7 +4225,7 @@ void LLAgent::changeCameraToCustomizeAvatar(BOOL animate) mbFlagsDirty = TRUE; } - gViewerWindow->setKeyboardFocus( NULL, NULL ); + gViewerWindow->setKeyboardFocus( NULL ); gViewerWindow->setMouseCapture( NULL ); LLVOAvatar::onCustomizeStart(); @@ -5225,6 +5225,102 @@ void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **) } } +class LLAgentDropGroupViewerNode : public LLHTTPNode +{ + virtual void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const + { + + if ( + !input.isMap() || + !input.has("body") ) + { + //what to do with badly formed message? + response->status(400); + response->result(LLSD("Invalid message parameters")); + } + + LLSD body = input["body"]; + if ( body.has("body") ) + { + //stupid message system doubles up the "body"s + body = body["body"]; + } + + if ( + body.has("AgentData") && + body["AgentData"].isArray() && + body["AgentData"][0].isMap() ) + { + llinfos << "VALID DROP GROUP" << llendl; + + //there is only one set of data in the AgentData block + LLSD agent_data = body["AgentData"][0]; + LLUUID agent_id; + LLUUID group_id; + + agent_id = agent_data["AgentID"].asUUID(); + group_id = agent_data["GroupID"].asUUID(); + + if (agent_id != gAgentID) + { + llwarns + << "AgentDropGroup for agent other than me" << llendl; + + response->notFound(); + return; + } + + // Remove the group if it already exists remove it + // and add the new data to pick up changes. + LLGroupData gd; + gd.mID = group_id; + S32 index = gAgent.mGroups.find(gd); + if (index != -1) + { + gAgent.mGroups.remove(index); + if (gAgent.getGroupID() == group_id) + { + gAgent.mGroupID.setNull(); + gAgent.mGroupPowers = 0; + gAgent.mGroupName[0] = '\0'; + gAgent.mGroupTitle[0] = '\0'; + } + + // refresh all group information + gAgent.sendAgentDataUpdateRequest(); + + gGroupMgr->clearGroupData(group_id); + // close the floater for this group, if any. + LLFloaterGroupInfo::closeGroup(group_id); + // refresh the group panel of the search window, + //if necessary. + LLFloaterDirectory::refreshGroup(group_id); + } + else + { + llwarns + << "AgentDropGroup, agent is not part of group " + << group_id << llendl; + } + + response->result(LLSD()); + } + else + { + //what to do with badly formed message? + response->status(400); + response->result(LLSD("Invalid message parameters")); + } + } +}; + +LLHTTPRegistration<LLAgentDropGroupViewerNode> + gHTTPRegistrationAgentDropGroupViewerNode( + "/message/AgentDropGroup"); + // static void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **) { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ad934abfa7..8b126b7597 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -425,7 +425,7 @@ static void ui_audio_callback(const LLUUID& uuid) { if (gAudiop) { - F32 volume = gSavedSettings.getF32("AudioLevelUI"); + F32 volume = gSavedSettings.getBOOL("MuteUI") ? 0.f : gSavedSettings.getF32("AudioLevelUI"); gAudiop->triggerSound(uuid, gAgent.getID(), volume); } } diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index c3c892a572..76cfe92c4c 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -252,7 +252,6 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) if(view) { LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); - LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback(); view->getPanel()->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO); if((LLAssetType::AT_TEXTURE == asset_type) @@ -262,7 +261,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) } //LLInventoryView::dumpSelectionInformation((void*)view); // restore keyboard focus - gFocusMgr.setKeyboardFocus(focus_ctrl, callback); + gFocusMgr.setKeyboardFocus(focus_ctrl); } } else diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 3517c1e18a..78365e66f1 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -93,51 +93,20 @@ private: // Functions // -//inline constructor -// for chat bars embedded in floaters, etc -LLChatBar::LLChatBar(const std::string& name) -: LLPanel(name, LLRect(), BORDER_NO), +LLChatBar::LLChatBar() +: LLPanel("", LLRect(), BORDER_NO), mInputEditor(NULL), mGestureLabelTimer(), mLastSpecialChatChannel(0), mIsBuilt(FALSE), - mDynamicLayout(FALSE), - mGestureCombo(NULL), - mObserver(NULL) -{ -} - -LLChatBar::LLChatBar(const std::string& name, const LLRect& rect) -: LLPanel(name, rect, BORDER_NO), - mInputEditor(NULL), - mGestureLabelTimer(), - mLastSpecialChatChannel(0), - mIsBuilt(FALSE), - mDynamicLayout(TRUE), mGestureCombo(NULL), mObserver(NULL) { setIsChrome(TRUE); - gUICtrlFactory->buildPanel(this,"panel_chat_bar.xml"); - - mIsFocusRoot = TRUE; - - setRect(rect); // override xml rect - - setBackgroundOpaque(TRUE); - setBackgroundVisible(TRUE); - - // Start visible if we left the app while chatting. - setVisible( gSavedSettings.getBOOL("ChatVisible") ); - - // Apply custom layout. - layout(); - -#if !LL_RELEASE_FOR_DOWNLOAD + #if !LL_RELEASE_FOR_DOWNLOAD childDisplayNotFound(); #endif - } @@ -151,25 +120,18 @@ LLChatBar::~LLChatBar() BOOL LLChatBar::postBuild() { childSetAction("History", toggleChatHistory, this); - childSetAction("Say", onClickSay, this); - childSetAction("Shout", onClickShout, this); + childSetCommitCallback("Say", onClickSay, this); // attempt to bind to an existing combo box named gesture setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture")); - LLButton * sayp = static_cast<LLButton*>(getChildByName("Say", TRUE)); - if(sayp) - { - setDefaultBtn(sayp); - } - mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor"); if (mInputEditor) { mInputEditor->setCallbackUserData(this); mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke); - mInputEditor->setFocusLostCallback(&onInputEditorFocusLost); - mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus ); + mInputEditor->setFocusLostCallback(&onInputEditorFocusLost, this); + mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus, this ); mInputEditor->setCommitOnFocusLost( FALSE ); mInputEditor->setRevertOnEsc( FALSE ); mInputEditor->setIgnoreTab(TRUE); @@ -189,16 +151,6 @@ BOOL LLChatBar::postBuild() //----------------------------------------------------------------------- // virtual -void LLChatBar::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLPanel::reshape(width, height, called_from_parent); - if (mIsBuilt) - { - layout(); - } -} - -// virtual BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) { BOOL handled = FALSE; @@ -208,13 +160,6 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) // ALT-RETURN is reserved for windowed/fullscreen toggle if( KEY_RETURN == key ) { - //if (childGetValue("Chat Editor").asString().empty()) - //{ - // // no text, just close chat bar - // stopChat(); - // return TRUE; - //} - if (mask == MASK_CONTROL) { // shout @@ -239,78 +184,8 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) return handled; } - -void LLChatBar::layout() -{ - if (!mDynamicLayout) return; - - S32 rect_width = mRect.getWidth(); - S32 count = 9; // number of elements in LLToolBar - S32 pad = 4; - - LLRect gesture_rect; - S32 gesture_width = 0; - if (childGetRect("Gesture", gesture_rect)) - { - gesture_width = gesture_rect.getWidth(); - } - F32 segment_width = (F32)(rect_width - (pad + gesture_width)) / (F32)count; - - S32 btn_width = lltrunc(segment_width-pad); - - S32 x = 0; - S32 y = 1; - LLRect r; - - x = llround(0 * segment_width); - r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); - childSetRect("History", r); - - x = llround(1 * segment_width); - // Hack this one up so it looks nice. - if (mInputEditor) - { - r.setOriginAndSize(x, y+2, llfloor(6*segment_width-pad), 18); - mInputEditor->reshape(r.getWidth(), r.getHeight(), TRUE); - mInputEditor->setRect(r); - } - - x = llround(7 * segment_width); - r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); - childSetRect("Say", r); - - x = llround(8 * segment_width); - r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); - childSetRect("Shout", r); - - x = rect_width - (pad + gesture_width); - r.setOriginAndSize(x, y, gesture_width, BTN_HEIGHT); - childSetRect("Gesture", r); -} - - void LLChatBar::refresh() { - //BOOL chat_mode = gSavedSettings.getBOOL("ChatVisible"); - - //// Grab focus when no one else has it, and we're in chat mode. - //if (!gFocusMgr.getKeyboardFocus() - // && chat_mode) - //{ - // childSetFocus("Chat Editor", TRUE); - //} - - // Only show this view when user wants to be chatting - //setVisible(chat_mode); - - // hide in mouselook, but keep previous visibility state - //BOOL mouselook = gAgent.cameraMouselook(); - // call superclass setVisible so that we don't overwrite the saved setting - if (mDynamicLayout) - { - LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible")); - } - // HACK: Leave the name of the gesture in place for a few seconds. const F32 SHOW_GESTURE_NAME_TIME = 2.f; if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME) @@ -584,12 +459,7 @@ void LLChatBar::stopChat() // hide chat bar so it doesn't grab focus back gChatBar->setVisible(FALSE); -} - -void LLChatBar::setVisible(BOOL visible) -{ - gSavedSettings.setBOOL("ChatVisible", visible); - LLPanel::setVisible(visible); + gSavedSettings.setBOOL("ChatVisible", FALSE); } // static @@ -663,30 +533,30 @@ void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata ) } // static -void LLChatBar::onInputEditorFocusLost( LLUICtrl* caller, void* userdata) +void LLChatBar::onInputEditorFocusLost( LLFocusableElement* caller, void* userdata) { // stop typing animation gAgent.stopTyping(); } // static -void LLChatBar::onInputEditorGainFocus( LLUICtrl* caller, void* userdata ) +void LLChatBar::onInputEditorGainFocus( LLFocusableElement* caller, void* userdata ) { LLFloaterChat::setHistoryCursorAndScrollToEnd(); } // static -void LLChatBar::onClickSay( void* userdata ) +void LLChatBar::onClickSay( LLUICtrl* ctrl, void* userdata ) { LLChatBar* self = (LLChatBar*) userdata; - self->sendChat( CHAT_TYPE_NORMAL ); -} - -// static -void LLChatBar::onClickShout( void* userdata ) -{ - LLChatBar *self = (LLChatBar *)userdata; - self->sendChat( CHAT_TYPE_SHOUT ); + if (ctrl->getValue().asString() == "shout") + { + self->sendChat( CHAT_TYPE_SHOUT ); + } + else + { + self->sendChat( CHAT_TYPE_NORMAL ); + } } void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h index 76a80125ae..f198574dcd 100644 --- a/indra/newview/llchatbar.h +++ b/indra/newview/llchatbar.h @@ -49,17 +49,12 @@ class LLChatBar { public: // constructor for inline chat-bars (e.g. hosted in chat history window) - LLChatBar(const std::string& name); - LLChatBar(const std::string& name, const LLRect& rect); + LLChatBar(); ~LLChatBar(); virtual BOOL postBuild(); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent); virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); - // Adjust buttons and input field for width - void layout(); - void refresh(); void refreshGestures(); @@ -86,20 +81,18 @@ public: LLWString stripChannelNumber(const LLWString &mesg, S32* channel); // callbacks - static void onClickSay( void* userdata ); - static void onClickShout( void* userdata ); + static void onClickSay( LLUICtrl*, void* userdata ); static void onTabClick( void* userdata ); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - static void onInputEditorFocusLost(LLUICtrl* caller,void* userdata); - static void onInputEditorGainFocus(LLUICtrl* caller,void* userdata); + static void onInputEditorFocusLost(LLFocusableElement* caller,void* userdata); + static void onInputEditorGainFocus(LLFocusableElement* caller,void* userdata); static void onCommitGesture(LLUICtrl* ctrl, void* data); static void startChat(void*); static void stopChat(); - /*virtual*/ void setVisible(BOOL visible); protected: void sendChat(EChatType type); void updateChat(); @@ -113,7 +106,6 @@ protected: S32 mLastSpecialChatChannel; BOOL mIsBuilt; - BOOL mDynamicLayout; LLComboBox* mGestureCombo; LLChatBarGestureObserver* mObserver; diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp index 693ffe091b..18c3983b9a 100644 --- a/indra/newview/llfloateranimpreview.cpp +++ b/indra/newview/llfloateranimpreview.cpp @@ -155,7 +155,6 @@ BOOL LLFloaterAnimPreview::postBuild() mPlayButton->setDisabledImages("",""); mPlayButton->setScaleImage(TRUE); - mPlayButton->setFixedBorder(0, 0); mStopButton = LLViewerUICtrlFactory::getButtonByName(this, "stop_btn"); if (!mStopButton) @@ -170,7 +169,6 @@ BOOL LLFloaterAnimPreview::postBuild() mStopButton->setDisabledImages("",""); mStopButton->setScaleImage(TRUE); - mStopButton->setFixedBorder(0, 0); r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); childSetCommitCallback("playback_slider", onSliderMove, this); diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index a2ea8c2794..8dcd38c0bb 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -189,12 +189,15 @@ void LLFloaterAvatarPicker::onList(LLUICtrl* ctrl, void* userdata) return; } - std::vector<LLScrollListItem*> items = self->mListNames->getAllSelected(); - for (std::vector<LLScrollListItem*>::iterator iter = items.begin(); - iter != items.end(); ++iter) + std::vector<LLScrollListItem*> items = + self->mListNames->getAllSelected(); + for ( + std::vector<LLScrollListItem*>::iterator iter = items.begin(); + iter != items.end(); + ++iter) { LLScrollListItem* item = *iter; - self->mAvatarNames.push_back(item->getColumn(0)->getText()); + self->mAvatarNames.push_back(item->getColumn(0)->getValue().asString()); self->mAvatarIDs.push_back(item->getUUID()); self->childSetEnabled("Select", TRUE); } @@ -225,8 +228,8 @@ void LLFloaterAvatarPicker::doSelectionChange(const std::deque<LLFolderViewItem* mAvatarNames.clear(); childSetEnabled("Select", FALSE); } + BOOL first_calling_card = TRUE; - BOOL first_calling_card = TRUE; std::deque<LLFolderViewItem*>::const_iterator item_it; for (item_it = items.begin(); item_it != items.end(); ++item_it) { diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 5fdbf7d408..82fe16172b 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -164,10 +164,16 @@ void LLFloaterChat::onVisibilityChange(BOOL new_visibility) { // Hide the chat overlay when our history is visible. gConsole->setVisible( !new_visibility ); + + // stop chat history tab from flashing when it appears + if (new_visibility) + { + LLFloaterChatterBox::getInstance()->setFloaterFlashing(this, FALSE); + } + LLFloater::onVisibilityChange(new_visibility); } - void add_timestamped_line(LLViewerTextEditor* edit, const LLString& line, const LLColor4& color) { bool prepend_newline = true; @@ -238,6 +244,12 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) { chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT); } + + // start tab flashing on incoming text from other users (ignoring system text, etc) + if (!chat_floater->isInVisibleChain() && chat.mSourceType == CHAT_SOURCE_AGENT) + { + LLFloaterChatterBox::getInstance()->setFloaterFlashing(chat_floater, TRUE); + } } // static @@ -246,8 +258,14 @@ void LLFloaterChat::setHistoryCursorAndScrollToEnd() LLViewerTextEditor* history_editor = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor", TRUE); LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor with mute", TRUE); - history_editor->setCursorAndScrollToEnd(); - history_editor_with_mute->setCursorAndScrollToEnd(); + if (history_editor) + { + history_editor->setCursorAndScrollToEnd(); + } + if (history_editor_with_mute) + { + history_editor_with_mute->setCursorAndScrollToEnd(); + } } @@ -269,7 +287,7 @@ void LLFloaterChat::onClickMute(void *data) if (gFloaterMute) { - gFloaterMute->show(); + LLFloaterMute::showInstance(); } } @@ -433,7 +451,7 @@ void* LLFloaterChat::createSpeakersPanel(void* data) //static void* LLFloaterChat::createChatPanel(void* data) { - LLChatBar* chatp = new LLChatBar("floating_chat_bar"); + LLChatBar* chatp = new LLChatBar(); return chatp; } @@ -441,7 +459,7 @@ void* LLFloaterChat::createChatPanel(void* data) void LLFloaterChat::hideInstance(const LLSD& id) { LLFloaterChat* floaterp = LLFloaterChat::getInstance(LLSD()); - // don't do anything when hosted in the chatterbox + if(floaterp->getHost()) { LLFloaterChatterBox::hideInstance(LLSD()); diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h index 808393b267..58fa607b38 100644 --- a/indra/newview/llfloaterchat.h +++ b/indra/newview/llfloaterchat.h @@ -82,6 +82,7 @@ public: protected: LLPanelActiveSpeakers* mPanel; + BOOL mScrolledToEnd; }; #endif diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index bdd30906d2..85bc8182a5 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -86,9 +86,15 @@ LLFloaterMyFriends* LLFloaterMyFriends::showInstance(const LLSD& id) //static void LLFloaterMyFriends::hideInstance(const LLSD& id) { - if(instanceVisible(id)) + LLFloaterMyFriends* floaterp = LLFloaterMyFriends::getInstance(); + + if(floaterp->getHost()) { - LLFloaterChatterBox::hideInstance(LLSD()); + LLFloaterChatterBox::hideInstance(); + } + else + { + LLUISingleton<LLFloaterMyFriends>::hideInstance(id); } } @@ -124,10 +130,23 @@ LLFloaterChatterBox::LLFloaterChatterBox(const LLSD& seed) : mAutoResize = FALSE; gUICtrlFactory->buildFloater(this, "floater_chatterbox.xml", NULL, FALSE); - addFloater(LLFloaterMyFriends::getInstance(0), TRUE); + if (gSavedSettings.getBOOL("ContactsTornOff")) + { + LLFloaterMyFriends* floater_contacts = LLFloaterMyFriends::getInstance(0); + // add then remove to set up relationship for re-attach + addFloater(floater_contacts, FALSE); + removeFloater(floater_contacts); + // reparent to floater view + gFloaterView->addChild(floater_contacts); + } + else + { + addFloater(LLFloaterMyFriends::getInstance(0), TRUE); + } + if (gSavedSettings.getBOOL("ChatHistoryTornOff")) { - LLFloaterChat* floater_chat = LLFloaterChat::getInstance(LLSD()); + LLFloaterChat* floater_chat = LLFloaterChat::getInstance(); // add then remove to set up relationship for re-attach addFloater(floater_chat, FALSE); removeFloater(floater_chat); @@ -217,7 +236,7 @@ void LLFloaterChatterBox::draw() mActiveVoiceFloater = current_active_floater; - LLFloater::draw(); + LLMultiFloater::draw(); } void LLFloaterChatterBox::onOpen() @@ -236,10 +255,17 @@ void LLFloaterChatterBox::removeFloater(LLFloater* floaterp) if (floaterp->getName() == "chat floater") { // only my friends floater now locked - mTabContainer->lockTabs(1); + mTabContainer->lockTabs(mTabContainer->getNumLockedTabs() - 1); gSavedSettings.setBOOL("ChatHistoryTornOff", TRUE); floaterp->setCanClose(TRUE); } + else if (floaterp->getName() == "floater_my_friends") + { + // only chat floater now locked + mTabContainer->lockTabs(mTabContainer->getNumLockedTabs() - 1); + gSavedSettings.setBOOL("ContactsTornOff", TRUE); + floaterp->setCanClose(TRUE); + } LLMultiFloater::removeFloater(floaterp); } @@ -247,19 +273,43 @@ void LLFloaterChatterBox::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainerCommon::eInsertionPoint insertion_point) { + S32 num_locked_tabs = mTabContainer->getNumLockedTabs(); + + // already here + if (floaterp->getHost() == this) return; + // make sure my friends and chat history both locked when re-attaching chat history if (floaterp->getName() == "chat floater") { - // select my friends tab - mTabContainer->selectFirstTab(); - // add chat history to the right of the my friends tab - //*TODO: respect select_added_floater so that we don't leave first tab selected - LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT); + mTabContainer->unlockTabs(); + // add chat history as second tab if contact window is present, first tab otherwise + if (getChildByName("floater_my_friends", TRUE)) + { + // assuming contacts window is first tab, select it + mTabContainer->selectFirstTab(); + // and add ourselves after + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT); + } + else + { + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); + } + // make sure first two tabs are now locked - mTabContainer->lockTabs(2); + mTabContainer->lockTabs(num_locked_tabs + 1); gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE); floaterp->setCanClose(FALSE); } + else if (floaterp->getName() == "floater_my_friends") + { + mTabContainer->unlockTabs(); + // add contacts window as first tab + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); + // make sure first two tabs are now locked + mTabContainer->lockTabs(num_locked_tabs + 1); + gSavedSettings.setBOOL("ContactsTornOff", FALSE); + floaterp->setCanClose(FALSE); + } else { LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); @@ -276,24 +326,27 @@ void LLFloaterChatterBox::addFloater(LLFloater* floaterp, //static LLFloaterChatterBox* LLFloaterChatterBox::showInstance(const LLSD& seed) { - LLFloaterChatterBox* floater = LLUISingleton<LLFloaterChatterBox>::showInstance(seed); + LLFloaterChatterBox* chatterbox_floater = LLUISingleton<LLFloaterChatterBox>::showInstance(seed); // if TRUE, show tab for active voice channel, otherwise, just show last tab - if (seed.asBoolean()) + LLFloater* floater_to_show = NULL; + LLUUID session_id = seed.asUUID(); + if (session_id.notNull()) { - LLFloater* floater_to_show = getCurrentVoiceFloater(); - if (floater_to_show) - { - floater_to_show->open(); - } - else - { - // just open chatterbox if there is no active voice window - LLUISingleton<LLFloaterChatterBox>::getInstance(seed)->open(); - } + floater_to_show = gIMMgr->findFloaterBySession(session_id); + } + + if (floater_to_show) + { + floater_to_show->open(); + } + else + { + // just open chatterbox to the last selected tab + chatterbox_floater->open(); } - return floater; + return chatterbox_floater; } //static diff --git a/indra/newview/llfloaterchatterbox.h b/indra/newview/llfloaterchatterbox.h index 34410935f2..bf06ee9d38 100644 --- a/indra/newview/llfloaterchatterbox.h +++ b/indra/newview/llfloaterchatterbox.h @@ -50,7 +50,7 @@ public: void onClose(bool app_quitting); // override LLUISingleton behavior - static LLFloaterMyFriends* showInstance(const LLSD& id); + static LLFloaterMyFriends* showInstance(const LLSD& id = LLSD()); static void hideInstance(const LLSD& id); static BOOL instanceVisible(const LLSD& id); @@ -77,7 +77,7 @@ public: BOOL select_added_floater, LLTabContainerCommon::eInsertionPoint insertion_point = LLTabContainerCommon::END); - static LLFloaterChatterBox* showInstance(const LLSD& seed); + static LLFloaterChatterBox* showInstance(const LLSD& seed = LLSD()); static BOOL instanceVisible(const LLSD& seed); static LLFloater* getCurrentVoiceFloater(); diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp index 6f4945e54b..9d52107a20 100644 --- a/indra/newview/llfloaterfriends.cpp +++ b/indra/newview/llfloaterfriends.cpp @@ -104,7 +104,6 @@ LLPanelFriends::LLPanelFriends() : LLPanel(), LLEventTimer(1000000), mObserver(NULL), - mMenuState(0), mShowMaxSelectWarning(TRUE), mAllowRightsChange(TRUE), mNumRightsChanged(0) @@ -182,16 +181,13 @@ BOOL LLPanelFriends::postBuild() { mFriendsList = LLUICtrlFactory::getScrollListByName(this, "friend_list"); mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT); - mFriendsList->setMaxiumumSelectCallback(onMaximumSelect); + mFriendsList->setMaximumSelectCallback(onMaximumSelect); mFriendsList->setCommitOnSelectionChange(TRUE); childSetCommitCallback("friend_list", onSelectName, this); childSetDoubleClickCallback("friend_list", onClickIM); refreshNames(); - childSetCommitCallback("online_status_cb", onClickOnlineStatus, this); - childSetCommitCallback("map_status_cb", onClickMapStatus, this); - childSetCommitCallback("modify_status_cb", onClickModifyStatus, this); childSetAction("im_btn", onClickIM, this); childSetAction("profile_btn", onClickProfile, this); childSetAction("offer_teleport_btn", onClickOfferTeleport, this); @@ -204,6 +200,10 @@ BOOL LLPanelFriends::postBuild() updateFriends(LLFriendObserver::ADD); refreshUI(); + // primary sort = online status, secondary sort = name + mFriendsList->sortByColumn("friend_name", TRUE); + mFriendsList->sortByColumn("icon_online_status", TRUE); + return TRUE; } @@ -222,64 +222,63 @@ void LLPanelFriends::addFriend(const std::string& name, const LLUUID& agent_id) element["columns"][LIST_FRIEND_NAME]["font"] = "SANSSERIF"; element["columns"][LIST_FRIEND_NAME]["font-style"] = "NORMAL"; element["columns"][LIST_ONLINE_STATUS]["column"] = "icon_online_status"; - element["columns"][LIST_ONLINE_STATUS]["type"] = "text"; + element["columns"][LIST_ONLINE_STATUS]["type"] = "icon"; if (online) { element["columns"][LIST_FRIEND_NAME]["font-style"] = "BOLD"; - element["columns"][LIST_ONLINE_STATUS]["type"] = "icon"; element["columns"][LIST_ONLINE_STATUS]["value"] = gViewerArt.getString("icon_avatar_online.tga"); } - element["columns"][LIST_VISIBLE_ONLINE]["column"] = "icon_visible_online"; - element["columns"][LIST_VISIBLE_ONLINE]["type"] = "text"; - if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) - { - element["columns"][LIST_VISIBLE_ONLINE]["type"] = "icon"; - element["columns"][LIST_VISIBLE_ONLINE]["value"] = gViewerArt.getString("ff_visible_online.tga"); - } + element["columns"][LIST_VISIBLE_ONLINE]["type"] = "checkbox"; + element["columns"][LIST_VISIBLE_ONLINE]["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS); + element["columns"][LIST_VISIBLE_MAP]["column"] = "icon_visible_map"; - element["columns"][LIST_VISIBLE_MAP]["type"] = "text"; - if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)) - { - element["columns"][LIST_VISIBLE_MAP]["type"] = "icon"; - element["columns"][LIST_VISIBLE_MAP]["value"] = gViewerArt.getString("ff_visible_map.tga"); - } + element["columns"][LIST_VISIBLE_MAP]["type"] = "checkbox"; + element["columns"][LIST_VISIBLE_MAP]["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION); + element["columns"][LIST_EDIT_MINE]["column"] = "icon_edit_mine"; - element["columns"][LIST_EDIT_MINE]["type"] = "text"; - if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) - { - element["columns"][LIST_EDIT_MINE]["type"] = "icon"; - element["columns"][LIST_EDIT_MINE]["value"] = gViewerArt.getString("ff_edit_mine.tga"); - } + element["columns"][LIST_EDIT_MINE]["type"] = "checkbox"; + element["columns"][LIST_EDIT_MINE]["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS); + element["columns"][LIST_EDIT_THEIRS]["column"] = "icon_edit_theirs"; - element["columns"][LIST_EDIT_THEIRS]["type"] = "text"; - if(relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS)) - { - element["columns"][LIST_EDIT_THEIRS]["type"] = "icon"; - element["columns"][LIST_EDIT_THEIRS]["value"] = gViewerArt.getString("ff_edit_theirs.tga"); - } + element["columns"][LIST_EDIT_THEIRS]["type"] = "checkbox"; + element["columns"][LIST_EDIT_THEIRS]["enabled"] = ""; + element["columns"][LIST_EDIT_THEIRS]["value"] = relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS); + + element["columns"][LIST_FRIEND_UPDATE_GEN]["column"] = "friend_last_update_generation"; + element["columns"][LIST_FRIEND_UPDATE_GEN]["value"] = relationInfo->getChangeSerialNum(); + mFriendsList->addElement(element, ADD_BOTTOM); } +// propagate actual relationship to UI +void LLPanelFriends::updateFriendItem(LLScrollListItem* itemp, const LLRelationship* info) +{ + if (!itemp) return; + if (!info) return; + + itemp->getColumn(LIST_ONLINE_STATUS)->setValue(info->isOnline() ? gViewerArt.getString("icon_avatar_online.tga") : LLString()); + // render name of online friends in bold text + ((LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME))->setFontStyle(info->isOnline() ? LLFontGL::BOLD : LLFontGL::NORMAL); + itemp->getColumn(LIST_VISIBLE_ONLINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)); + itemp->getColumn(LIST_VISIBLE_MAP)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)); + itemp->getColumn(LIST_EDIT_MINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)); + itemp->getColumn(LIST_FRIEND_UPDATE_GEN)->setValue(info->getChangeSerialNum()); + + // enable this item, in case it was disabled after user input + itemp->setEnabled(TRUE); + + // changed item in place, need to request sort + mFriendsList->sortItems(); +} + void LLPanelFriends::refreshRightsChangeList() { LLDynamicArray<LLUUID> friends = getSelectedIDs(); S32 num_selected = friends.size(); - LLSD row; bool can_offer_teleport = num_selected >= 1; - - // aggregate permissions over all selected friends - bool friends_see_online = true; - bool friends_see_on_map = true; - bool friends_modify_objects = true; - - // do at least some of the friends selected have these rights? - bool some_friends_see_online = false; - bool some_friends_see_on_map = false; - bool some_friends_modify_objects = false; - bool selected_friends_online = true; LLTextBox* processing_label = LLUICtrlFactory::getTextBoxByName(this, "process_rights_label"); @@ -294,12 +293,9 @@ void LLPanelFriends::refreshRightsChangeList() num_selected = 0; } } - else + else if(processing_label) { - if(processing_label) - { - processing_label->setVisible(false); - } + processing_label->setVisible(false); } const LLRelationship* friend_status = NULL; @@ -308,20 +304,6 @@ void LLPanelFriends::refreshRightsChangeList() friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr); if (friend_status) { - bool can_see_online = friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS); - bool can_see_on_map = friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION); - bool can_modify_objects = friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS); - - // aggregate rights of this friend into total selection - friends_see_online &= can_see_online; - friends_see_on_map &= can_see_on_map; - friends_modify_objects &= can_modify_objects; - - // can at least one of your selected friends do any of these? - some_friends_see_online |= can_see_online; - some_friends_see_on_map |= can_see_on_map; - some_friends_modify_objects |= can_modify_objects; - if(!friend_status->isOnline()) { can_offer_teleport = false; @@ -331,44 +313,13 @@ void LLPanelFriends::refreshRightsChangeList() else // missing buddy info, don't allow any operations { can_offer_teleport = false; - - friends_see_online = false; - friends_see_on_map = false; - friends_modify_objects = false; - - some_friends_see_online = false; - some_friends_see_on_map = false; - some_friends_modify_objects = false; } } - - // seeing a friend on the map requires seeing online status as a prerequisite - friends_see_on_map &= friends_see_online; - - mMenuState = 0; - - // make checkboxes visible after we have finished processing rights - childSetVisible("online_status_cb", mAllowRightsChange); - childSetVisible("map_status_cb", mAllowRightsChange); - childSetVisible("modify_status_cb", mAllowRightsChange); - if (num_selected == 0) // nothing selected { childSetEnabled("im_btn", FALSE); childSetEnabled("offer_teleport_btn", FALSE); - - childSetEnabled("online_status_cb", FALSE); - childSetValue("online_status_cb", FALSE); - childSetTentative("online_status_cb", FALSE); - - childSetEnabled("map_status_cb", FALSE); - childSetValue("map_status_cb", FALSE); - childSetTentative("map_status_cb", FALSE); - - childSetEnabled("modify_status_cb", FALSE); - childSetValue("modify_status_cb", FALSE); - childSetTentative("modify_status_cb", FALSE); } else // we have at least one friend selected... { @@ -376,61 +327,76 @@ void LLPanelFriends::refreshRightsChangeList() // to be consistent with context menus in inventory and because otherwise // offline friends would be silently dropped from the session childSetEnabled("im_btn", selected_friends_online || num_selected == 1); - childSetEnabled("offer_teleport_btn", can_offer_teleport); - - childSetEnabled("online_status_cb", TRUE); - childSetValue("online_status_cb", some_friends_see_online); - childSetTentative("online_status_cb", some_friends_see_online != friends_see_online); - if (friends_see_online) - { - mMenuState |= LLRelationship::GRANT_ONLINE_STATUS; - } - - childSetEnabled("map_status_cb", TRUE); - childSetValue("map_status_cb", some_friends_see_on_map); - childSetTentative("map_status_cb", some_friends_see_on_map != friends_see_on_map); - if(friends_see_on_map) - { - mMenuState |= LLRelationship::GRANT_MAP_LOCATION; - } - - // for now, don't allow modify rights change for multiple select - childSetEnabled("modify_status_cb", num_selected == 1); - childSetValue("modify_status_cb", some_friends_modify_objects); - childSetTentative("modify_status_cb", some_friends_modify_objects != friends_modify_objects); - if(friends_modify_objects) - { - mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS; - } } } +struct SortFriendsByID +{ + bool SortFriendsByID::operator() (const LLScrollListItem* const a, const LLScrollListItem* const b) const + { + return a->getValue().asUUID() < b->getValue().asUUID(); + } +}; + void LLPanelFriends::refreshNames() { LLDynamicArray<LLUUID> selected_ids = getSelectedIDs(); S32 pos = mFriendsList->getScrollPos(); - mFriendsList->operateOnAll(LLCtrlListInterface::OP_DELETE); - LLCollectAllBuddies collect; - LLAvatarTracker::instance().applyFunctor(collect); - LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); - LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); + // get all buddies we know about + LLAvatarTracker::buddy_map_t all_buddies; + LLAvatarTracker::instance().copyBuddyList(all_buddies); - for ( ; it != end; ++it) - { - const std::string& name = it->first; - const LLUUID& agent_id = it->second; - addFriend(name, agent_id); - } - it = collect.mOffline.begin(); - end = collect.mOffline.end(); - for ( ; it != end; ++it) + // get all friends in list and sort by UUID + std::vector<LLScrollListItem*> items = mFriendsList->getAllData(); + std::sort(items.begin(), items.end(), SortFriendsByID()); + + std::vector<LLScrollListItem*>::iterator item_it = items.begin(); + std::vector<LLScrollListItem*>::iterator item_end = items.end(); + + LLAvatarTracker::buddy_map_t::iterator buddy_it; + for (buddy_it = all_buddies.begin() ; buddy_it != all_buddies.end(); ++buddy_it) { - const std::string& name = it->first; - const LLUUID& agent_id = it->second; - addFriend(name, agent_id); + // erase any items that reflect residents who are no longer buddies + while(item_it != item_end && buddy_it->first > (*item_it)->getValue().asUUID()) + { + mFriendsList->deleteItems((*item_it)->getValue()); + ++item_it; + } + + // update existing friends with new info + if (item_it != item_end && buddy_it->first == (*item_it)->getValue().asUUID()) + { + const LLRelationship* info = buddy_it->second; + if (!info) + { + ++item_it; + continue; + } + + S32 last_change_generation = (*item_it)->getColumn(LIST_FRIEND_UPDATE_GEN)->getValue().asInteger(); + if (last_change_generation < info->getChangeSerialNum()) + { + // update existing item in UI + updateFriendItem(mFriendsList->getItem(buddy_it->first), info); + } + ++item_it; + } + // add new friend to list + else + { + const LLUUID& buddy_id = buddy_it->first; + char first_name[DB_FIRST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/ + char last_name[DB_LAST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/ + + gCacheName->getName(buddy_id, first_name, last_name); + std::ostringstream fullname; + fullname << first_name << " " << last_name; + addFriend(fullname.str(), buddy_id); + } } + mFriendsList->selectMultiple(selected_ids); mFriendsList->setScrollPos(pos); } @@ -451,7 +417,7 @@ void LLPanelFriends::refreshUI() } else { - childSetText("friend_name_label", mFriendsList->getFirstSelected()->getColumn(LIST_FRIEND_NAME)->getText() + "..."); + childSetText("friend_name_label", mFriendsList->getFirstSelected()->getColumn(LIST_FRIEND_NAME)->getValue().asString() + "..."); } } else @@ -493,6 +459,8 @@ void LLPanelFriends::onSelectName(LLUICtrl* ctrl, void* user_data) if(panelp) { panelp->refreshUI(); + // check to see if rights have changed + panelp->applyRightsToFriends(); } } @@ -687,35 +655,22 @@ void LLPanelFriends::onClickPay(void* user_data) } } -void LLPanelFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data) -{ - LLPanelFriends* panelp = (LLPanelFriends*)user_data; - - bool checked = ctrl->getValue(); - panelp->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked); - panelp->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked); -} - -void LLPanelFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data) -{ - LLPanelFriends* panelp = (LLPanelFriends*)user_data; - bool checked = ctrl->getValue(); - panelp->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked); - panelp->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked); -} - -void LLPanelFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data) +void LLPanelFriends::confirmModifyRights(rights_map_t& ids, EGrantRevoke command) { - LLPanelFriends* panelp = (LLPanelFriends*)user_data; - - bool checked = ctrl->getValue(); - LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs(); + if (ids.empty()) return; + LLStringBase<char>::format_map_t args; if(ids.size() > 0) { + // copy map of ids onto heap + rights_map_t* rights = new rights_map_t(ids); + // package with panel pointer + std::pair<LLPanelFriends*, rights_map_t*>* user_data = new std::pair<LLPanelFriends*, rights_map_t*>(this, rights); + + // for single friend, show their name if(ids.size() == 1) { - LLUUID agent_id = ids[0]; + LLUUID agent_id = ids.begin()->first; char first[DB_FIRST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/ char last[DB_LAST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/ if(gCacheName->getName(agent_id, first, last)) @@ -723,56 +678,174 @@ void LLPanelFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data) args["[FIRST_NAME]"] = first; args["[LAST_NAME]"] = last; } - if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, user_data); - else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, user_data); + if (command == GRANT) + { + gViewerWindow->alertXml("GrantModifyRights", args, modifyRightsConfirmation, user_data); + } + else + { + gViewerWindow->alertXml("RevokeModifyRights", args, modifyRightsConfirmation, user_data); + } + } + else + { + if (command == GRANT) + { + gViewerWindow->alertXml("GrantModifyRightsMultiple", args, modifyRightsConfirmation, user_data); + } + else + { + gViewerWindow->alertXml("RevokeModifyRightsMultiple", args, modifyRightsConfirmation, user_data); + } } - else return; } } -void LLPanelFriends::handleModifyRights(S32 option, void* user_data) +// static +void LLPanelFriends::modifyRightsConfirmation(S32 option, void* user_data) { - LLPanelFriends* panelp = (LLPanelFriends*)user_data; + std::pair<LLPanelFriends*, rights_map_t*>* data = (std::pair<LLPanelFriends*, rights_map_t*>*)user_data; + LLPanelFriends* panelp = data->first; if(panelp) { - if(!option) + if(0 == option) { - panelp->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0)); - panelp->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0)); + panelp->sendRightsGrant(*(data->second)); + } + else + { + // need to resync view with model, since user cancelled operation + rights_map_t* rights = data->second; + rights_map_t::iterator rights_it; + for (rights_it = rights->begin(); rights_it != rights->end(); ++rights_it) + { + LLScrollListItem* itemp = panelp->mFriendsList->getItem(rights_it->first); + const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(rights_it->first); + panelp->updateFriendItem(itemp, info); + } } panelp->refreshUI(); } + + delete data->second; + delete data; } -void LLPanelFriends::updateMenuState(S32 flag, BOOL value) +void LLPanelFriends::applyRightsToFriends() { - if(value) mMenuState |= flag; - else mMenuState &= ~flag; + BOOL rights_changed = FALSE; + + // store modify rights separately for confirmation + rights_map_t rights_updates; + + BOOL need_confirmation = FALSE; + EGrantRevoke confirmation_type = GRANT; + + // this assumes that changes only happened to selected items + std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected(); + for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr) + { + LLUUID id = (*itr)->getValue(); + const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(id); + if (buddy_relationship == NULL) continue; + + bool show_online_staus = (*itr)->getColumn(LIST_VISIBLE_ONLINE)->getValue().asBoolean(); + bool show_map_location = (*itr)->getColumn(LIST_VISIBLE_MAP)->getValue().asBoolean(); + bool allow_modify_objects = (*itr)->getColumn(LIST_EDIT_MINE)->getValue().asBoolean(); + + S32 rights = buddy_relationship->getRightsGrantedTo(); + if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) != show_online_staus) + { + rights_changed = TRUE; + if(show_online_staus) + { + rights |= LLRelationship::GRANT_ONLINE_STATUS; + } + else + { + // ONLINE_STATUS necessary for MAP_LOCATION + rights &= ~LLRelationship::GRANT_ONLINE_STATUS; + rights &= ~LLRelationship::GRANT_MAP_LOCATION; + // propagate rights constraint to UI + (*itr)->getColumn(LIST_VISIBLE_MAP)->setValue(FALSE); + } + } + if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) != show_map_location) + { + rights_changed = TRUE; + if(show_map_location) + { + // ONLINE_STATUS necessary for MAP_LOCATION + rights |= LLRelationship::GRANT_MAP_LOCATION; + rights |= LLRelationship::GRANT_ONLINE_STATUS; + (*itr)->getColumn(LIST_VISIBLE_ONLINE)->setValue(TRUE); + } + else + { + rights &= ~LLRelationship::GRANT_MAP_LOCATION; + } + } + + // now check for change in modify object rights, which requires confirmation + if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects) + { + rights_changed = TRUE; + need_confirmation = TRUE; + + if(allow_modify_objects) + { + rights |= LLRelationship::GRANT_MODIFY_OBJECTS; + confirmation_type = GRANT; + } + else + { + rights &= ~LLRelationship::GRANT_MODIFY_OBJECTS; + confirmation_type = REVOKE; + } + } + + if (rights_changed) + { + rights_updates.insert(std::make_pair(id, rights)); + // disable these ui elements until response from server + // to avoid race conditions + (*itr)->setEnabled(FALSE); + } + } + + // separately confirm grant and revoke of modify rights + if (need_confirmation) + { + confirmModifyRights(rights_updates, confirmation_type); + } + else + { + sendRightsGrant(rights_updates); + } } -void LLPanelFriends::applyRightsToFriends(S32 flag, BOOL value) +void LLPanelFriends::sendRightsGrant(rights_map_t& ids) { + if (ids.empty()) return; + LLMessageSystem* msg = gMessageSystem; + + // setup message header msg->newMessageFast(_PREHASH_GrantUserRights); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUID(_PREHASH_AgentID, gAgent.getID()); msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID()); - LLDynamicArray<LLUUID> ids = getSelectedIDs(); - S32 rights; - for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr) + rights_map_t::iterator id_it; + rights_map_t::iterator end_it = ids.end(); + for(id_it = ids.begin(); id_it != end_it; ++id_it) { - rights = LLAvatarTracker::instance().getBuddyInfo(*itr)->getRightsGrantedTo(); - if(LLAvatarTracker::instance().getBuddyInfo(*itr)->isRightGrantedTo(flag) != (bool)value) - { - if(value) rights |= flag; - else rights &= ~flag; - msg->nextBlockFast(_PREHASH_Rights); - msg->addUUID(_PREHASH_AgentRelated, *itr); - msg->addS32(_PREHASH_RelatedRights, rights); - } + msg->nextBlockFast(_PREHASH_Rights); + msg->addUUID(_PREHASH_AgentRelated, id_it->first); + msg->addS32(_PREHASH_RelatedRights, id_it->second); } + mNumRightsChanged = ids.size(); gAgent.sendReliableMessage(); } diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h index 3d0b7a9bba..46e4eaaf79 100644 --- a/indra/newview/llfloaterfriends.h +++ b/indra/newview/llfloaterfriends.h @@ -40,6 +40,7 @@ #include "lltimer.h" class LLFriendObserver; +class LLRelationship; /** @@ -88,19 +89,27 @@ private: LIST_VISIBLE_ONLINE, LIST_VISIBLE_MAP, LIST_EDIT_MINE, - LIST_EDIT_THEIRS + LIST_EDIT_THEIRS, + LIST_FRIEND_UPDATE_GEN }; // protected members - + typedef std::map<LLUUID, S32> rights_map_t; void reloadNames(); void refreshNames(); void refreshUI(); void refreshRightsChangeList(); - void applyRightsToFriends(S32 flag, BOOL value); - void updateMenuState(S32 flag, BOOL value); - S32 getMenuState() { return mMenuState; } + void applyRightsToFriends(); void addFriend(const std::string& name, const LLUUID& agent_id); + void updateFriendItem(LLScrollListItem* itemp, const LLRelationship* relationship); + + typedef enum + { + GRANT, + REVOKE + } EGrantRevoke; + void confirmModifyRights(rights_map_t& ids, EGrantRevoke command); + void sendRightsGrant(rights_map_t& ids); // return LLUUID::null if nothing is selected LLDynamicArray<LLUUID> getSelectedIDs(); @@ -119,12 +128,10 @@ private: static void onClickOfferTeleport(void* user_data); static void onClickPay(void* user_data); - static void onClickOnlineStatus(LLUICtrl* ctrl, void* user_data); - static void onClickMapStatus(LLUICtrl* ctrl, void* user_data); static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data); static void handleRemove(S32 option, void* user_data); - static void handleModifyRights(S32 option, void* user_data); + static void modifyRightsConfirmation(S32 option, void* user_data); private: // member data @@ -132,7 +139,6 @@ private: LLUUID mAddFriendID; LLString mAddFriendName; LLScrollListCtrl* mFriendsList; - S32 mMenuState; BOOL mShowMaxSelectWarning; BOOL mAllowRightsChange; S32 mNumRightsChanged; diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 0ce91ef740..9f1624d20c 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -262,6 +262,7 @@ void LLFloaterInspect::refresh() void LLFloaterInspect::onFocusReceived() { gToolMgr->setTransientTool(gToolInspect); + LLFloater::onFocusReceived(); } void LLFloaterInspect::dirty() diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 3b96a4ce5e..2a352dfc3d 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1045,8 +1045,10 @@ BOOL LLPanelLandObjects::postBuild() mSelectedObjects = LLUICtrlFactory::getTextBoxByName(this, "selected_objects_text"); mCleanOtherObjectsTime = LLUICtrlFactory::getLineEditorByName(this, "clean other time"); - mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus); + + mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus, this); mCleanOtherObjectsTime->setCommitCallback(onCommitClean); + childSetPrevalidate("clean other time", LLLineEditor::prevalidateNonNegativeS32); childSetUserData("clean other time", this); @@ -1135,7 +1137,7 @@ void LLPanelLandObjects::onDoubleClickOwner(void *userdata) return; } // Is this a group? - BOOL is_group = cell->getText() == OWNER_GROUP; + BOOL is_group = cell->getValue().asString() == OWNER_GROUP; if (is_group) { LLFloaterGroupInfo::showFromUUID(owner_id); @@ -1180,19 +1182,16 @@ void LLPanelLandObjects::refresh() } else { - S32 sw_max = 0; - S32 sw_total = 0; - S32 max = 0; - S32 total = 0; - S32 owned = 0; - S32 group = 0; - S32 other = 0; - S32 selected = 0; - F32 parcel_object_bonus = 0.f; - - gParcelMgr->getPrimInfo(sw_max, sw_total, - max, total, owned, group, other, selected, - parcel_object_bonus, mOtherTime); + S32 sw_max = parcel->getSimWideMaxPrimCapacity(); + S32 sw_total = parcel->getSimWidePrimCount(); + S32 max = llround(parcel->getMaxPrimCapacity() * parcel->getParcelPrimBonus()); + S32 total = parcel->getPrimCount(); + S32 owned = parcel->getOwnerPrimCount(); + S32 group = parcel->getGroupPrimCount(); + S32 other = parcel->getOtherPrimCount(); + S32 selected = parcel->getSelectedPrimCount(); + F32 parcel_object_bonus = parcel->getParcelPrimBonus(); + mOtherTime = parcel->getCleanOtherTime(); // Can't have more than region max tasks, regardless of parcel // object bonus factor. @@ -1442,14 +1441,6 @@ void LLPanelLandObjects::onClickReturnOwnerList(void* userdata) { LLPanelLandObjects *self = (LLPanelLandObjects *)userdata; - S32 sw_max, sw_total; - S32 max, total; - S32 owned, group, other, selected; - F32 parcel_object_bonus; - S32 other_time; - - gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time); - LLParcel* parcelp = self->mParcel->getParcel(); if (!parcelp) return; @@ -1632,11 +1623,11 @@ void LLPanelLandObjects::onCommitList(LLUICtrl* ctrl, void* data) return; } // Is this a group? - self->mSelectedIsGroup = cell->getText() == OWNER_GROUP; + self->mSelectedIsGroup = cell->getValue().asString() == OWNER_GROUP; cell = item->getColumn(2); - self->mSelectedName = cell->getText(); + self->mSelectedName = cell->getValue().asString(); cell = item->getColumn(3); - self->mSelectedCount = atoi(cell->getText().c_str()); + self->mSelectedCount = atoi(cell->getValue().asString().c_str()); // Set the selection, and enable the return button. self->mSelectedOwners.clear(); @@ -1700,18 +1691,14 @@ void LLPanelLandObjects::onClickShowOtherObjects(void* userdata) // static void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata) { - S32 sw_max=0, sw_total=0; - S32 max=0, total=0; - S32 owned=0, group=0, other=0, selected=0; - F32 parcel_object_bonus=0; - S32 other_time=0; - - gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time); + S32 owned = 0; LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata; LLParcel* parcel = panelp->mParcel->getParcel(); if (!parcel) return; + owned = parcel->getOwnerPrimCount(); + send_parcel_select_objects(parcel->getLocalID(), RT_OWNER); LLUUID owner_id = parcel->getOwnerID(); @@ -1739,14 +1726,6 @@ void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata) // static void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata) { - S32 sw_max=0, sw_total=0; - S32 max=0, total=0; - S32 owned=0, group=0, other=0, selected=0; - F32 parcel_object_bonus=0; - S32 other_time=0; - - gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time); - LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata; LLParcel* parcel = panelp->mParcel->getParcel(); if (!parcel) return; @@ -1758,7 +1737,7 @@ void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata) LLStringBase<char>::format_map_t args; args["[NAME]"] = group_name; - args["[N]"] = llformat("%d",group); + args["[N]"] = llformat("%d", parcel->getGroupPrimCount()); // create and show confirmation textbox gViewerWindow->alertXml("ReturnObjectsDeededToGroup", args, callbackReturnGroupObjects, userdata); @@ -1767,17 +1746,13 @@ void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata) // static void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata) { - S32 sw_max=0, sw_total=0; - S32 max=0, total=0; - S32 owned=0, group=0, other=0, selected=0; - F32 parcel_object_bonus=0; - S32 other_time=0; - - gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time); + S32 other = 0; LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata; LLParcel* parcel = panelp->mParcel->getParcel(); if (!parcel) return; + + other = parcel->getOtherPrimCount(); send_parcel_select_objects(parcel->getLocalID(), RT_OTHER); @@ -1817,9 +1792,9 @@ void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata) } // static -void LLPanelLandObjects::onLostFocus(LLUICtrl *caller, void* user_data) +void LLPanelLandObjects::onLostFocus(LLFocusableElement* caller, void* user_data) { - onCommitClean(caller, user_data); + onCommitClean((LLUICtrl*)caller, user_data); } // static @@ -2408,6 +2383,13 @@ void LLPanelLandMedia::refresh() mCheckSoundLocal->set( parcel->getSoundLocal() ); mCheckSoundLocal->setEnabled( can_change_media ); + LLViewerRegion* selection_region = gParcelMgr->getSelectionRegion(); + BOOL region_allows_voice = FALSE; + if (selection_region) + { + region_allows_voice = selection_region->isVoiceEnabled(); + } + if(parcel->getVoiceEnabled()) { if(parcel->getVoiceUseEstateChannel()) @@ -2420,7 +2402,7 @@ void LLPanelLandMedia::refresh() mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatDisable); } - mRadioVoiceChat->setEnabled( can_change_media ); + mRadioVoiceChat->setEnabled( can_change_media && region_allows_voice ); // don't display urls if you're not able to change it // much requested change in forums so people can't 'steal' urls @@ -2535,7 +2517,7 @@ void LLPanelLandMedia::onClickStopMedia ( void* data ) void LLPanelLandMedia::onClickStartMedia ( void* data ) { // force a commit - gFocusMgr.setKeyboardFocus ( NULL, NULL ); + gFocusMgr.setKeyboardFocus ( NULL ); // force a reload LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, false, std::string()); diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index fa941caf78..4bb88aa127 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -257,7 +257,7 @@ public: static void onDoubleClickOwner(void*); static void onCommitList(LLUICtrl* ctrl, void* data); - static void onLostFocus(LLUICtrl* caller, void* user_data); + static void onLostFocus(LLFocusableElement* caller, void* user_data); static void onCommitClean(LLUICtrl* caller, void* user_data); static void processParcelObjectOwnersReply(LLMessageSystem *msg, void **); diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp index a00f512515..5306ce11b1 100644 --- a/indra/newview/llfloaterpostcard.cpp +++ b/indra/newview/llfloaterpostcard.cpp @@ -137,8 +137,7 @@ BOOL LLFloaterPostcard::postBuild() MsgField->setWordWrap(TRUE); // For the first time a user focusess to .the msg box, all text will be selected. - MsgField->setFocusChangedCallback(onMsgFormFocusRecieved); - MsgField->setCallbackUserData(this); + MsgField->setFocusChangedCallback(onMsgFormFocusRecieved, this); } childSetFocus("to_form", TRUE); @@ -347,7 +346,7 @@ void LLFloaterPostcard::updateUserInfo(const char *email) } } -void LLFloaterPostcard::onMsgFormFocusRecieved(LLUICtrl* receiver, void* data) +void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data) { LLFloaterPostcard* self = (LLFloaterPostcard *)data; if(self) diff --git a/indra/newview/llfloaterpostcard.h b/indra/newview/llfloaterpostcard.h index 78da8b55d8..287d34c706 100644 --- a/indra/newview/llfloaterpostcard.h +++ b/indra/newview/llfloaterpostcard.h @@ -65,7 +65,7 @@ public: static void updateUserInfo(const char *email); - static void onMsgFormFocusRecieved(LLUICtrl* receiver, void* data); + static void onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data); static void missingSubjMsgAlertCallback(S32 option, void* data); void sendPostcard(); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 3306142856..eaf7832eab 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -161,50 +161,55 @@ bool estate_dispatch_initialized = false; /// LLFloaterRegionInfo ///---------------------------------------------------------------------------- -LLFloaterRegionInfo* LLFloaterRegionInfo::sInstance = NULL; //S32 LLFloaterRegionInfo::sRequestSerial = 0; LLUUID LLFloaterRegionInfo::sRequestInvoice; -LLFloaterRegionInfo::LLFloaterRegionInfo(const LLRect& rect) : - LLFloater("regioninfo", rect, "Region/Estate") +LLFloaterRegionInfo::LLFloaterRegionInfo(const LLSD& seed) { - LLRect tr(0, rect.getHeight() - LLFLOATER_HEADER_SIZE, rect.getWidth(), 0); - mTab = new LLTabContainer("tab", tr, LLTabContainer::TOP, NULL,NULL,""); - mTab->setBorderVisible(FALSE); - addChild(mTab); + gUICtrlFactory->buildFloater(this, "floater_region_info.xml", NULL, FALSE); +} + +BOOL LLFloaterRegionInfo::postBuild() +{ + mTab = gUICtrlFactory->getTabContainerByName(this, "region_panels"); // contruct the panels - LLPanel* panel; + LLPanelRegionInfo* panel; panel = new LLPanelRegionGeneralInfo; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_general.xml"); mTab->addTabPanel(panel, panel->getLabel(), TRUE); panel = new LLPanelRegionDebugInfo; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_debug.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); panel = new LLPanelRegionTextureInfo; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_texture.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); panel = new LLPanelRegionTerrainInfo; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_terrain.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); panel = new LLPanelEstateInfo; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_estate.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); panel = new LLPanelEstateCovenant; - mInfoPanels.push_back((LLPanelRegionInfo*)panel); + mInfoPanels.push_back(panel); gUICtrlFactory->buildPanel(panel, "panel_region_covenant.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); + gMessageSystem->setHandlerFunc( + "EstateOwnerMessage", + &processEstateOwnerRequest); + + return TRUE; } LLFloaterRegionInfo::~LLFloaterRegionInfo() @@ -212,23 +217,20 @@ LLFloaterRegionInfo::~LLFloaterRegionInfo() sInstance = NULL; } -// static -void LLFloaterRegionInfo::show(LLViewerRegion* region) +void LLFloaterRegionInfo::onOpen() { - if (!sInstance) - { - LLRect rect = gSavedSettings.getRect("FloaterRegionInfo"); - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - rect.translate(left,top); - sInstance = new LLFloaterRegionInfo(rect); - gMessageSystem->setHandlerFunc( - "EstateOwnerMessage", - &processEstateOwnerRequest); - } - sInstance->open(); /* Flawfinder: ignore*/ - sInstance->refreshFromRegion(region); + LLRect rect = gSavedSettings.getRect("FloaterRegionInfo"); + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + rect.translate(left,top); + + requestRegionInfo(); + refreshFromRegion(gAgent.getRegion()); + LLFloater::onOpen(); +} +void LLFloaterRegionInfo::requestRegionInfo() +{ // Must allow anyone to request the RegionInfo data // so non-owners/non-gods can see the values. // Therefore can't use an EstateOwnerMessage JC @@ -242,18 +244,6 @@ void LLFloaterRegionInfo::show(LLViewerRegion* region) } // static -void LLFloaterRegionInfo::show(void*) -{ - show(gAgent.getRegion()); -} - -// static -LLFloaterRegionInfo* LLFloaterRegionInfo::getInstance() -{ - return sInstance; -} - -// static void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**) { static LLDispatcher dispatch; @@ -264,7 +254,7 @@ void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**) LLPanelEstateInfo::initDispatch(dispatch); } - LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab"); + LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "region_panels"); if (!tab) return; LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate"); @@ -293,7 +283,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) llinfos << "LLFloaterRegionInfo::processRegionInfo" << llendl; if(!sInstance) return; - LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab"); + LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "region_panels"); if(!tab) return; // extract message @@ -376,7 +366,7 @@ LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate() { LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance(); if (!floater) return NULL; - LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab"); + LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "region_panels"); if (!tab) return NULL; LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab,"Estate"); return panel; @@ -387,7 +377,7 @@ LLPanelEstateCovenant* LLFloaterRegionInfo::getPanelCovenant() { LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance(); if (!floater) return NULL; - LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab"); + LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "region_panels"); if (!tab) return NULL; LLPanelEstateCovenant* panel = (LLPanelEstateCovenant*)LLUICtrlFactory::getPanelByName(tab, "Covenant"); return panel; @@ -1241,7 +1231,7 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate() LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance(); if (!floater) return true; - LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab"); + LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "region_panels"); if (!tab) return true; LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate"); @@ -2803,7 +2793,7 @@ bool LLDispatchSetEstateOwner::operator()( LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance(); if (!floater) return true; - LLTabContainer* tab = (LLTabContainer*)(floater->getChildByName("tab")); + LLTabContainer* tab = (LLTabContainer*)(floater->getChildByName("region_panels")); if (!tab) return true; LLPanelEstateInfo* panel = (LLPanelEstateInfo*)(tab->getChildByName("Estate")); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index abf4789b6d..6f9ed1fc34 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -58,14 +58,15 @@ class LLPanelRegionTerrainInfo; class LLPanelEstateInfo; class LLPanelEstateCovenant; -class LLFloaterRegionInfo : public LLFloater +class LLFloaterRegionInfo : public LLFloater, public LLUISingleton<LLFloaterRegionInfo> { + friend class LLUISingleton<LLFloaterRegionInfo>; public: ~LLFloaterRegionInfo(); - static void show(LLViewerRegion* region); - static void show(void*); - static LLFloaterRegionInfo* getInstance(); + /*virtual*/ void onOpen(); + /*virtual*/ BOOL postBuild(); + static void processEstateOwnerRequest(LLMessageSystem* msg, void**); // get and process region info if necessary. @@ -82,15 +83,14 @@ public: // from LLPanel virtual void refresh(); + void requestRegionInfo(); + protected: - LLFloaterRegionInfo(const LLRect& rect); + LLFloaterRegionInfo(const LLSD& seed); void refreshFromRegion(LLViewerRegion* region); - // static data - static LLFloaterRegionInfo* sInstance; - // member data - LLTabContainer* mTab; + LLTabContainerCommon* mTab; typedef std::vector<LLPanelRegionInfo*> info_panels_t; info_panels_t mInfoPanels; //static S32 sRequestSerial; // serial # of last EstateOwnerRequest diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp index 9d600befdb..80a686d162 100644 --- a/indra/newview/llfloaterscriptdebug.cpp +++ b/indra/newview/llfloaterscriptdebug.cpp @@ -177,7 +177,7 @@ LLFloaterScriptDebugOutput::LLFloaterScriptDebugOutput(const LLUUID& object_id) mHistoryEditor->setWordWrap( TRUE ); mHistoryEditor->setFollowsAll(); mHistoryEditor->setEnabled( FALSE ); - mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history. + mHistoryEditor->setTabStop( TRUE ); // We want to be able to cut or copy from the history. addChild(mHistoryEditor); } @@ -200,7 +200,7 @@ void LLFloaterScriptDebugOutput::init(const LLString& title, BOOL resizable, mHistoryEditor->setWordWrap( TRUE ); mHistoryEditor->setFollowsAll(); mHistoryEditor->setEnabled( FALSE ); - mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history. + mHistoryEditor->setTabStop( TRUE ); // We want to be able to cut or copy from the history. addChild(mHistoryEditor); } diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 22581c6576..d03ce373cc 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -942,7 +942,7 @@ void commit_select_component(LLUICtrl *ctrl, void *data) //forfeit focus if (gFocusMgr.childHasKeyboardFocus(floaterp)) { - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } BOOL select_individuals = floaterp->mCheckSelectIndividual->get(); @@ -992,4 +992,5 @@ void LLFloaterTools::setEditTool(void* tool_pointer) void LLFloaterTools::onFocusReceived() { gToolMgr->setCurrentToolset(gBasicToolset); + LLFloater::onFocusReceived(); } diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index e3f236becc..6bbb748a10 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -257,8 +257,8 @@ void LLFloaterTopObjects::updateSelectionInfo() std::string object_id_string = object_id.asString(); childSetValue("id_editor", LLSD(object_id_string)); - childSetValue("object_name_editor", list->getFirstSelected()->getColumn(1)->getText()); - childSetValue("owner_name_editor", list->getFirstSelected()->getColumn(2)->getText()); + childSetValue("object_name_editor", list->getFirstSelected()->getColumn(1)->getValue().asString()); + childSetValue("owner_name_editor", list->getFirstSelected()->getColumn(2)->getValue().asString()); } // static @@ -451,8 +451,8 @@ void LLFloaterTopObjects::showBeacon() LLScrollListItem* first_selected = list->getFirstSelected(); if (!first_selected) return; - LLString name = first_selected->getColumn(1)->getText(); - LLString pos_string = first_selected->getColumn(3)->getText(); + LLString name = first_selected->getColumn(1)->getValue().asString(); + LLString pos_string = first_selected->getColumn(3)->getValue().asString(); F32 x, y, z; S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z); diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 4c03a15619..b1bbb341fd 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -234,7 +234,7 @@ BOOL LLFloaterWorldMap::postBuild() childSetAction("DoSearch", onLocationCommit, this); - childSetFocusChangedCallback("location", updateSearchEnabled); + childSetFocusChangedCallback("location", onLocationFocusChanged, this); LLLineEditor *location_editor = LLUICtrlFactory::getLineEditorByName(this, "location"); if (location_editor) @@ -1221,6 +1221,12 @@ void LLFloaterWorldMap::onAvatarComboCommit( LLUICtrl* ctrl, void* userdata ) } } +//static +void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus, void* userdata ) +{ + updateSearchEnabled((LLUICtrl*)focus, userdata); +} + // static void LLFloaterWorldMap::updateSearchEnabled( LLUICtrl* ctrl, void* userdata ) { diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index c069b40929..6f3c583557 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -153,6 +153,7 @@ protected: void teleportToAvatar(); static void updateSearchEnabled( LLUICtrl* ctrl, void* userdata ); + static void onLocationFocusChanged( LLFocusableElement* ctrl, void* userdata ); static void onLocationCommit( void* userdata ); static void onCommitLocation( LLUICtrl* ctrl, void* userdata ); static void onCommitSearchResult( LLUICtrl* ctrl, void* userdata ); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 2577474e24..deffca3b79 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -94,7 +94,7 @@ 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); -void renamer_focus_lost( LLUICtrl* handler, void* user_data ); +void renamer_focus_lost( LLFocusableElement* handler, void* user_data ); ///---------------------------------------------------------------------------- /// Class LLFolderViewItem @@ -693,7 +693,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) // Release keyboard focus, so that if stuff is dropped into the // world, pressing the delete key won't blow away the inventory // item. - gViewerWindow->setKeyboardFocus(NULL, NULL); + gViewerWindow->setKeyboardFocus(NULL); return gToolDragAndDrop->handleHover( x, y, mask ); } @@ -3164,7 +3164,7 @@ void LLFolderView::draw() } if(gViewerWindow->hasKeyboardFocus(this) && !getVisible()) { - gViewerWindow->setKeyboardFocus( NULL, NULL ); + gViewerWindow->setKeyboardFocus( NULL ); } // while dragging, update selection rendering to reflect single/multi drag status @@ -3656,7 +3656,7 @@ void LLFolderView::startRenamingSelectedItem( void ) mRenamer->setVisible( TRUE ); // set focus will fail unless item is visible mRenamer->setFocus( TRUE ); - mRenamer->setFocusLostCallback(renamer_focus_lost); + mRenamer->setLostTopCallback(onRenamerLost); gViewerWindow->setTopCtrl( mRenamer ); } } @@ -3730,7 +3730,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) { if( gViewerWindow->childHasKeyboardFocus( this ) ) { - gViewerWindow->setKeyboardFocus( NULL, NULL ); + gViewerWindow->setKeyboardFocus( NULL ); } } mSearchString.clear(); @@ -4438,12 +4438,10 @@ bool LLInventorySort::operator()(LLFolderViewItem* a, LLFolderViewItem* b) } } -void renamer_focus_lost( LLUICtrl* ctrl, void* userdata) +//static +void LLFolderView::onRenamerLost( LLUICtrl* renamer, void* user_data) { - if( ctrl ) - { - ctrl->setVisible( FALSE ); - } + renamer->setVisible(FALSE); } void delete_selected_item(void* user_data) diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index dd0dd21705..9fcf94d802 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -876,6 +876,8 @@ protected: LLScrollableContainerView* mScrollContainer; // NULL if this is not a child of a scroll container. static void commitRename( LLUICtrl* renamer, void* user_data ); + static void onRenamerLost( LLUICtrl* renamer, void* user_data); + void finishRenamingItem( void ); void revertRenamingItem( void ); diff --git a/indra/newview/llhudview.cpp b/indra/newview/llhudview.cpp index e40e84b9da..7f549bb2eb 100644 --- a/indra/newview/llhudview.cpp +++ b/indra/newview/llhudview.cpp @@ -52,8 +52,8 @@ LLHUDView *gHUDView = NULL; const S32 HUD_ARROW_SIZE = 32; -LLHUDView::LLHUDView(const std::string& name, const LLRect& rect) -: LLView(name, rect, FALSE) +LLHUDView::LLHUDView() +: LLPanel() { } LLHUDView::~LLHUDView() diff --git a/indra/newview/llhudview.h b/indra/newview/llhudview.h index b2353ad5df..cbefdc121e 100644 --- a/indra/newview/llhudview.h +++ b/indra/newview/llhudview.h @@ -32,16 +32,16 @@ #ifndef LL_LLHUDVIEW_H #define LL_LLHUDVIEW_H -#include "llview.h" +#include "llpanel.h" #include "v4color.h" class LLVector3d; class LLHUDView -: public LLView +: public LLPanel { public: - LLHUDView(const std::string& name, const LLRect& rect); + LLHUDView(); virtual ~LLHUDView(); virtual EWidgetType getWidgetType() const; diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 810d3a26a1..b1fefc4f5d 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -63,6 +63,7 @@ #include "llviewerstats.h" #include "llviewercontrol.h" #include "llvieweruictrlfactory.h" +#include "llviewerwindow.h" #include "lllogchat.h" #include "llfloaterhtml.h" #include "llweb.h" @@ -92,10 +93,14 @@ static LLString sSessionStartString = "Starting session with [NAME] please wait. LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; +LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; -void session_starter_helper(const LLUUID& temp_session_id, - const LLUUID& other_participant_id, - EInstantMessage im_type) +BOOL LLVoiceChannel::sSuspended = FALSE; + +void session_starter_helper( + const LLUUID& temp_session_id, + const LLUUID& other_participant_id, + EInstantMessage im_type) { LLMessageSystem *msg = gMessageSystem; @@ -122,47 +127,111 @@ void session_starter_helper(const LLUUID& temp_session_id, msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); } +void start_deprecated_conference_chat( + const LLUUID& temp_session_id, + const LLUUID& creator_id, + const LLUUID& other_participant_id, + const LLSD& agents_to_invite) +{ + U8* bucket; + U8* pos; + S32 count; + S32 bucket_size; + + // *FIX: this could suffer from endian issues + count = agents_to_invite.size(); + bucket_size = UUID_BYTES * count; + bucket = new U8[bucket_size]; + pos = bucket; + + for(S32 i = 0; i < count; ++i) + { + LLUUID agent_id = agents_to_invite[i].asUUID(); + + memcpy(pos, &agent_id, UUID_BYTES); + pos += UUID_BYTES; + } + + session_starter_helper( + temp_session_id, + other_participant_id, + IM_SESSION_CONFERENCE_START); + + gMessageSystem->addBinaryDataFast( + _PREHASH_BinaryBucket, + bucket, + bucket_size); + + gAgent.sendReliableMessage(); + + delete[] bucket; +} + +class LLStartConferenceChatResponder : public LLHTTPClient::Responder +{ +public: + LLStartConferenceChatResponder( + const LLUUID& temp_session_id, + const LLUUID& creator_id, + const LLUUID& other_participant_id, + const LLSD& agents_to_invite) + { + mTempSessionID = temp_session_id; + mCreatorID = creator_id; + mOtherParticipantID = other_participant_id; + mAgents = agents_to_invite; + } + + virtual void error(U32 statusNum, const std::string& reason) + { + //try an "old school" way. + if ( statusNum == 400 ) + { + start_deprecated_conference_chat( + mTempSessionID, + mCreatorID, + mOtherParticipantID, + mAgents); + } + + //else throw an error back to the client? + //in theory we should have just have these error strings + //etc. set up in this file as opposed to the IMMgr, + //but the error string were unneeded here previously + //and it is not worth the effort switching over all + //the possible different language translations + } + +private: + LLUUID mTempSessionID; + LLUUID mCreatorID; + LLUUID mOtherParticipantID; + + LLSD mAgents; +}; + // Returns true if any messages were sent, false otherwise. // Is sort of equivalent to "does the server need to do anything?" -bool send_start_session_messages(const LLUUID& temp_session_id, - const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, - EInstantMessage dialog) +bool send_start_session_messages( + const LLUUID& temp_session_id, + const LLUUID& other_participant_id, + const LLDynamicArray<LLUUID>& ids, + EInstantMessage dialog) { - if ( (dialog == IM_SESSION_GROUP_START) || - (dialog == IM_SESSION_CONFERENCE_START) ) + if ( dialog == IM_SESSION_GROUP_START ) { - S32 count = ids.size(); - S32 bucket_size = UUID_BYTES * count; - U8* bucket; - U8* pos; - - session_starter_helper(temp_session_id, - other_participant_id, - dialog); + session_starter_helper( + temp_session_id, + other_participant_id, + dialog); switch(dialog) { case IM_SESSION_GROUP_START: - gMessageSystem->addBinaryDataFast(_PREHASH_BinaryBucket, - EMPTY_BINARY_BUCKET, - EMPTY_BINARY_BUCKET_SIZE); - break; - case IM_SESSION_CONFERENCE_START: - bucket = new U8[bucket_size]; - pos = bucket; - - // *FIX: this could suffer from endian issues - for(S32 i = 0; i < count; ++i) - { - memcpy(pos, &(ids.get(i)), UUID_BYTES); - pos += UUID_BYTES; - } - gMessageSystem->addBinaryDataFast(_PREHASH_BinaryBucket, - bucket, - bucket_size); - delete[] bucket; - + gMessageSystem->addBinaryDataFast( + _PREHASH_BinaryBucket, + EMPTY_BINARY_BUCKET, + EMPTY_BINARY_BUCKET_SIZE); break; default: break; @@ -171,6 +240,44 @@ bool send_start_session_messages(const LLUUID& temp_session_id, return true; } + else if ( dialog == IM_SESSION_CONFERENCE_START ) + { + LLSD agents; + for (int i = 0; i < (S32) ids.size(); i++) + { + agents.append(ids.get(i)); + } + + //we have a new way of starting conference calls now + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability( + "ChatSessionRequest"); + LLSD data; + data["method"] = "start conference"; + data["session-id"] = temp_session_id; + + data["params"] = agents; + + LLHTTPClient::post( + url, + data, + new LLStartConferenceChatResponder( + temp_session_id, + gAgent.getID(), + other_participant_id, + data["params"])); + } + else + { + start_deprecated_conference_chat( + temp_session_id, + gAgent.getID(), + other_participant_id, + agents); + } + } return false; } @@ -194,8 +301,20 @@ void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) << status << ": " << reason << ")" << llendl; LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (channelp) + if ( channelp ) { + if ( 403 == status ) + { + LLNotifyBox::showXml( + "VoiceNotAllowed", + channelp->getNotifyArgs()); + } + else + { + LLNotifyBox::showXml( + "VoiceCallGenericError", + channelp->getNotifyArgs()); + } channelp->deactivate(); } } @@ -242,10 +361,6 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const LLString& session LLVoiceChannel::~LLVoiceChannel() { - // CANNOT do this here, since it will crash on quit in the LLVoiceChannelProximal singleton destructor. - // Do it in all other subclass destructors instead. - // deactivate(); - // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. if(gVoiceClient) { @@ -266,7 +381,19 @@ void LLVoiceChannel::setChannelInfo( if (mState == STATE_NO_CHANNEL_INFO) { - if(!mURI.empty() && !mCredentials.empty()) + if (mURI.empty()) + { + LLNotifyBox::showXml("VoiceChannelJoinFailed", mNotifyArgs); + llwarns << "Received empty URI for channel " << mSessionName << llendl; + deactivate(); + } + else if (mCredentials.empty()) + { + LLNotifyBox::showXml("VoiceChannelJoinFailed", mNotifyArgs); + llwarns << "Received empty credentials for channel " << mSessionName << llendl; + deactivate(); + } + else { setState(STATE_READY); @@ -279,12 +406,6 @@ void LLVoiceChannel::setChannelInfo( activate(); } } - else - { - //*TODO: notify user - llwarns << "Received invalid credentials for channel " << mSessionName << llendl; - deactivate(); - } } } @@ -325,7 +446,7 @@ void LLVoiceChannel::handleStatusChange(EStatusType type) } break; case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave) + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) { // if forceably removed from channel // update the UI and revert to default channel @@ -496,6 +617,38 @@ void LLVoiceChannel::initClass() sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); } + +//static +void LLVoiceChannel::suspend() +{ + if (!sSuspended) + { + sSuspendedVoiceChannel = sCurrentVoiceChannel; + sSuspended = TRUE; + } +} + +//static +void LLVoiceChannel::resume() +{ + if (sSuspended) + { + if (gVoiceClient->voiceEnabled()) + { + if (sSuspendedVoiceChannel) + { + sSuspendedVoiceChannel->activate(); + } + else + { + LLVoiceChannelProximal::getInstance()->activate(); + } + } + sSuspended = FALSE; + } +} + + // // LLVoiceChannelGroup // @@ -507,11 +660,6 @@ LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLStrin mIsRetrying = FALSE; } -LLVoiceChannelGroup::~LLVoiceChannelGroup() -{ - deactivate(); -} - void LLVoiceChannelGroup::deactivate() { if (callStarted()) @@ -635,6 +783,7 @@ void LLVoiceChannelGroup::handleError(EStatusType status) } break; + case ERROR_UNKNOWN: default: break; @@ -677,11 +826,6 @@ LLVoiceChannelProximal::LLVoiceChannelProximal() : activate(); } -LLVoiceChannelProximal::~LLVoiceChannelProximal() -{ - // DO NOT call deactivate() here, since this will only happen at atexit() time. -} - BOOL LLVoiceChannelProximal::isActive() { return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); @@ -725,6 +869,9 @@ void LLVoiceChannelProximal::handleStatusChange(EStatusType status) case STATUS_LEFT_CHANNEL: // do not notify user when leaving proximal channel return; + case STATUS_VOICE_DISABLED: + gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); + return; default: break; } @@ -762,29 +909,26 @@ void LLVoiceChannelProximal::deactivate() } } + // // LLVoiceChannelP2P // LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id) : LLVoiceChannelGroup(session_id, session_name), - mOtherUserID(other_user_id) + mOtherUserID(other_user_id), + mReceivedCall(FALSE) { // make sure URI reflects encoded version of other user's agent id setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); } -LLVoiceChannelP2P::~LLVoiceChannelP2P() -{ - deactivate(); -} - void LLVoiceChannelP2P::handleStatusChange(EStatusType type) { // status updates switch(type) { case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave) + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) { if (mState == STATE_RINGING) { @@ -832,6 +976,7 @@ void LLVoiceChannelP2P::activate() // no session handle yet, we're starting the call if (mSessionHandle.empty()) { + mReceivedCall = FALSE; LLVoiceClient::getInstance()->callUser(mOtherUserID); } // otherwise answering the call @@ -879,24 +1024,37 @@ void LLVoiceChannelP2P::setSessionHandle(const LLString& handle) mSessionHandle = handle; // The URI of a p2p session should always be the other end's SIP URI. setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); - + mReceivedCall = TRUE; + if (needs_activate) { activate(); } } +void LLVoiceChannelP2P::setState(EState state) +{ + // you only "answer" voice invites in p2p mode + // so provide a special purpose message here + if (mReceivedCall && state == STATE_RINGING) + { + gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); + mState = state; + return; + } + LLVoiceChannel::setState(state); +} + + // // LLFloaterIMPanel // LLFloaterIMPanel::LLFloaterIMPanel( - const std::string& name, - const LLRect& rect, const std::string& session_label, const LLUUID& session_id, const LLUUID& other_participant_id, EInstantMessage dialog) : - LLFloater(name, rect, session_label), + LLFloater(session_label, LLRect(), session_label), mInputEditor(NULL), mHistoryEditor(NULL), mSessionUUID(session_id), @@ -909,6 +1067,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mOtherTyping(FALSE), mTypingLineStartIndex(0), mSentTypingState(TRUE), + mNumUnreadMessages(0), mShowSpeakersOnConnect(TRUE), mAutoConnect(FALSE), mSpeakerPanel(NULL), @@ -919,14 +1078,12 @@ LLFloaterIMPanel::LLFloaterIMPanel( } LLFloaterIMPanel::LLFloaterIMPanel( - const std::string& name, - const LLRect& rect, const std::string& session_label, const LLUUID& session_id, const LLUUID& other_participant_id, const LLDynamicArray<LLUUID>& ids, EInstantMessage dialog) : - LLFloater(name, rect, session_label), + LLFloater(session_label, LLRect(), session_label), mInputEditor(NULL), mHistoryEditor(NULL), mSessionUUID(session_id), @@ -952,13 +1109,15 @@ LLFloaterIMPanel::LLFloaterIMPanel( void LLFloaterIMPanel::init(const LLString& session_label) { + mSessionLabel = session_label; + LLString xml_filename; switch(mDialog) { case IM_SESSION_GROUP_START: mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); xml_filename = "floater_instant_message_group.xml"; - mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, mSessionLabel); break; case IM_SESSION_INVITE: mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); @@ -970,21 +1129,21 @@ void LLFloaterIMPanel::init(const LLString& session_label) { xml_filename = "floater_instant_message_ad_hoc.xml"; } - mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, mSessionLabel); break; case IM_SESSION_P2P_INVITE: xml_filename = "floater_instant_message.xml"; - mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); + mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, mSessionLabel, mOtherParticipantUUID); break; case IM_SESSION_CONFERENCE_START: mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); xml_filename = "floater_instant_message_ad_hoc.xml"; - mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, mSessionLabel); break; // just received text from another user case IM_NOTHING_SPECIAL: xml_filename = "floater_instant_message.xml"; - mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); + mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, mSessionLabel, mOtherParticipantUUID); break; default: llwarns << "Unknown session type" << llendl; @@ -999,15 +1158,14 @@ void LLFloaterIMPanel::init(const LLString& session_label) &getFactoryMap(), FALSE); - setLabel(session_label); - setTitle(session_label); + setTitle(mSessionLabel); mInputEditor->setMaxTextLength(1023); // enable line history support for instant message bar mInputEditor->setEnableLineHistory(TRUE); if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) { - LLLogChat::loadHistory(session_label, + LLLogChat::loadHistory(mSessionLabel, &chatFromLogFile, (void *)this); } @@ -1060,16 +1218,12 @@ BOOL LLFloaterIMPanel::postBuild() { requires("chat_editor", WIDGET_TYPE_LINE_EDITOR); requires("im_history", WIDGET_TYPE_TEXT_EDITOR); - requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX); - requires("title_string", WIDGET_TYPE_TEXT_BOX); - requires("typing_start_string", WIDGET_TYPE_TEXT_BOX); - requires("session_start_string", WIDGET_TYPE_TEXT_BOX); if (checkRequirements()) { mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "chat_editor"); - mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived ); - mInputEditor->setFocusLostCallback( onInputEditorFocusLost ); + mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); + mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); mInputEditor->setKeystrokeCallback( onInputEditorKeystroke ); mInputEditor->setCommitCallback( onCommitChat ); mInputEditor->setCallbackUserData(this); @@ -1084,6 +1238,7 @@ BOOL LLFloaterIMPanel::postBuild() childSetAction("send_btn", onClickSend, this); childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this); + childSetAction("moderator_kick_speaker", onKickSpeaker, this); //LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn"); //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); @@ -1094,17 +1249,11 @@ BOOL LLFloaterIMPanel::postBuild() { childSetEnabled("profile_btn", FALSE); } - LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string"); - sTitleString = title->getText(); - - LLTextBox* typing_start = LLUICtrlFactory::getTextBoxByName(this, "typing_start_string"); - - sTypingStartString = typing_start->getText(); - - LLTextBox* session_start = LLUICtrlFactory::getTextBoxByName( - this, - "session_start_string"); - sSessionStartString = session_start->getText(); + + sTitleString = getFormattedUIString("title_string"); + sTypingStartString = getFormattedUIString("typing_start_string"); + sSessionStartString = getFormattedUIString("session_start_string"); + if (mSpeakerPanel) { mSpeakerPanel->refreshSpeakers(); @@ -1112,7 +1261,7 @@ BOOL LLFloaterIMPanel::postBuild() if (mDialog == IM_NOTHING_SPECIAL) { - childSetCommitCallback("mute_btn", onClickMuteVoice, this); + childSetAction("mute_btn", onClickMuteVoice, this); childSetCommitCallback("speaker_volume", onVolumeChange, this); } @@ -1131,7 +1280,7 @@ void* LLFloaterIMPanel::createSpeakersPanel(void* data) } //static -void LLFloaterIMPanel::onClickMuteVoice(LLUICtrl* source, void* user_data) +void LLFloaterIMPanel::onClickMuteVoice(void* user_data) { LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; if (floaterp) @@ -1171,10 +1320,22 @@ void LLFloaterIMPanel::draw() && LLVoiceClient::voiceEnabled(); // hide/show start call and end call buttons - childSetVisible("end_call_btn", mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); - childSetVisible("start_call_btn", mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("end_call_btn", LLVoiceClient::voiceEnabled() && mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("start_call_btn", LLVoiceClient::voiceEnabled() && mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); childSetEnabled("start_call_btn", enable_connect); childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty()); + + LLPointer<LLSpeaker> self_speaker = mSpeakers->findSpeaker(gAgent.getID()); + if (self_speaker.notNull() && self_speaker->mModeratorMutedText) + { + mInputEditor->setEnabled(FALSE); + mInputEditor->setLabel(getFormattedUIString("muted_text_label")); + } + else + { + mInputEditor->setEnabled(TRUE); + mInputEditor->setLabel(getFormattedUIString("default_text_label")); + } if (mAutoConnect && enable_connect) { @@ -1215,11 +1376,11 @@ void LLFloaterIMPanel::draw() else { // refresh volume and mute checkbox - childSetEnabled("speaker_volume", mVoiceChannel->isActive()); + childSetVisible("speaker_volume", LLVoiceClient::voiceEnabled() && mVoiceChannel->isActive()); childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID)); childSetValue("mute_btn", gMuteListp->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); - childSetEnabled("mute_btn", mVoiceChannel->isActive()); + childSetVisible("mute_btn", LLVoiceClient::voiceEnabled() && mVoiceChannel->isActive()); } LLFloater::draw(); } @@ -1235,7 +1396,6 @@ public: void error(U32 statusNum, const std::string& reason) { llinfos << "Error inviting all agents to session" << llendl; - //throw something back to the viewer here? } @@ -1243,6 +1403,48 @@ private: LLUUID mSessionID; }; +class LLSessionImmediateInviteResponder : public LLHTTPClient::Responder +{ +public: + LLSessionImmediateInviteResponder( + const LLUUID& session_id, + const std::string& chat_req_url, + const LLSD& post_data) + { + mSessionID = session_id; + mURL = chat_req_url; + mPostData = post_data; + } + + void error(U32 statusNum, const std::string& reason) + { + if ( statusNum == 400 ) + { + //hrm 400 indicates invalid parameters...more + //than likely the method doesn't exist + //so try a soon to be deprecated old school way of doing this + mPostData["method"] = "invite"; + + LLHTTPClient::post( + mURL, + mPostData, + new LLSessionInviteResponder(mSessionID)); + } + else + { + //throw something back to the viewer here? + llinfos << "Error inviting all agents to session" << llendl; + } + } + +private: + LLUUID mSessionID; + LLSD mPostData; + + std::string mURL; +}; + + BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) { LLViewerRegion* region = gAgent.getRegion(); @@ -1267,12 +1469,15 @@ BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) data["params"].append(ids.get(i)); } - data["method"] = "invite"; + data["method"] = "immediate invite"; data["session-id"] = mSessionUUID; LLHTTPClient::post( url, data, - new LLSessionInviteResponder(mSessionUUID)); + new LLSessionImmediateInviteResponder( + mSessionUUID, + url, + data)); } else @@ -1289,6 +1494,15 @@ BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &utf8msg, const LLColor4& color, bool log_to_file) { + // start tab flashing when receiving im for background session from user + LLMultiFloater* hostp = getHost(); + if( !isInVisibleChain() + && hostp + && source != gAgent.getID()) + { + hostp->setFloaterFlashing(this, TRUE); + } + addHistoryLine(utf8msg, color, log_to_file); mSpeakers->speakerChatted(source); mSpeakers->setSpeakerTyping(source, FALSE); @@ -1296,14 +1510,6 @@ void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &u void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file) { - LLMultiFloater* hostp = getHost(); - if( !getVisible() && hostp && log_to_file) - { - // Only flash for logged ("real") messages - LLTabContainer* parent = (LLTabContainer*) getParent(); - parent->setTabPanelFlashing( this, TRUE ); - } - // Now we're adding the actual line of text, so erase the // "Foo is typing..." text segment, and the optional timestamp // if it was present. JC @@ -1330,6 +1536,11 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4 LLLogChat::saveHistory(getTitle(),histstr); } + + if (!isInVisibleChain()) + { + mNumUnreadMessages++; + } } @@ -1340,10 +1551,7 @@ void LLFloaterIMPanel::setVisible(BOOL b) LLMultiFloater* hostp = getHost(); if( b && hostp ) { - LLTabContainer* parent = (LLTabContainer*) getParent(); - - // When this tab is displayed, you can stop flashing. - parent->setTabPanelFlashing( this, FALSE ); + hostp->setFloaterFlashing(this, FALSE); /* Don't change containing floater title - leave it "Instant Message" JC LLUIString title = sTitleString; @@ -1392,7 +1600,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren else if ( KEY_ESCAPE == key ) { handled = TRUE; - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); // Close talk panel with escape if( !gSavedSettings.getBOOL("PinTalkViewOpen") ) @@ -1573,14 +1781,14 @@ void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata) } // static -void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ) +void LLFloaterIMPanel::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) { LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; self->mHistoryEditor->setCursorAndScrollToEnd(); } // static -void LLFloaterIMPanel::onInputEditorFocusLost(LLUICtrl* caller, void* userdata) +void LLFloaterIMPanel::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) { LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; self->setTyping(FALSE); @@ -1628,6 +1836,14 @@ void LLFloaterIMPanel::onClose(bool app_quitting) destroy(); } +void LLFloaterIMPanel::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility) + { + mNumUnreadMessages = 0; + } +} + void deliver_message(const std::string& utf8_text, const LLUUID& im_session_id, const LLUUID& other_participant_id, @@ -1739,19 +1955,37 @@ void LLFloaterIMPanel::sendMsg() mSentTypingState = TRUE; } -void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates) -{ - mSpeakers->processSpeakerListUpdate(speaker_updates); +void LLFloaterIMPanel::updateSpeakersList(const LLSD& speaker_updates) +{ + mSpeakers->updateSpeakers(speaker_updates); } -void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map) +void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) { - mSpeakers->processSpeakerMap(speaker_map); + if ( + session_update.has("moderated_mode") && + session_update["moderated_mode"].has("voice") ) + { + BOOL voice_moderated = session_update["moderated_mode"]["voice"]; + + if (voice_moderated) + { + setTitle(mSessionLabel + LLString(" ") + getFormattedUIString("moderated_chat_label")); + } + else + { + setTitle(mSessionLabel); + } + + + //update the speakers dropdown too + mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + } } -void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list) +void LLFloaterIMPanel::setSpeakers(const LLSD& speaker_list) { - mSpeakers->processSpeakerList(speaker_list); + mSpeakers->setSpeakers(speaker_list); } void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) @@ -1897,5 +2131,77 @@ void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata) //self->addHistoryLine(line, LLColor4::grey, FALSE); self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey); +} + +void LLFloaterIMPanel::showSessionStartError( + const std::string& error_string) +{ + //the error strings etc. should be really be static and local + //to this file instead of in the LLFloaterIM + //but they were in llimview.cpp first and unfortunately + //some translations into non English languages already occurred + //thus making it a tad harder to change over to a + //"correct" solution. The best solution + //would be to store all of the misc. strings into + //their own XML file which would be read in by any LLIMPanel + //post build function instead of repeating the same info + //in the group, adhoc and normal IM xml files. + LLString::format_map_t args; + args["[REASON]"] = + LLFloaterIM::sErrorStringsMap[error_string]; + args["[RECIPIENT]"] = getTitle(); + gViewerWindow->alertXml( + "ChatterBoxSessionStartError", + args, + onConfirmForceCloseError, + this); } + +void LLFloaterIMPanel::showSessionEventError( + const std::string& event_string, + const std::string& error_string) +{ + LLString::format_map_t args; + args["[REASON]"] = + LLFloaterIM::sErrorStringsMap[error_string]; + args["[EVENT]"] = + LLFloaterIM::sEventStringsMap[event_string]; + args["[RECIPIENT]"] = getTitle(); + + gViewerWindow->alertXml( + "ChatterBoxSessionEventError", + args); +} + +void LLFloaterIMPanel::showSessionForceClose( + const std::string& reason_string) +{ + LLString::format_map_t args; + + args["[NAME]"] = getTitle(); + args["[REASON]"] = LLFloaterIM::sForceCloseSessionMap[reason_string]; + + gViewerWindow->alertXml( + "ForceCloseChatterBoxSession", + args, + LLFloaterIMPanel::onConfirmForceCloseError, + this); + +} + +//static +void LLFloaterIMPanel::onKickSpeaker(void* user_data) +{ + +} + +void LLFloaterIMPanel::onConfirmForceCloseError(S32 option, void* data) +{ + //only 1 option really + LLFloaterIMPanel* floater = ((LLFloaterIMPanel*) data); + + if ( floater ) floater->close(FALSE); +} + + diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index c1ad18dd3c..ef36ff4a33 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -74,14 +74,20 @@ public: virtual void getChannelInfo(); virtual BOOL isActive(); virtual BOOL callStarted(); + + const LLUUID getSessionID() { return mSessionID; } EState getState() { return mState; } void updateSessionID(const LLUUID& new_session_id); + const LLString::format_map_t& getNotifyArgs() { return mNotifyArgs; } static LLVoiceChannel* getChannelByID(const LLUUID& session_id); static LLVoiceChannel* getChannelByURI(LLString uri); static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } static void initClass(); + + static void suspend(); + static void resume(); protected: virtual void setState(EState state); @@ -103,13 +109,14 @@ protected: static voice_channel_map_uri_t sVoiceChannelURIMap; static LLVoiceChannel* sCurrentVoiceChannel; + static LLVoiceChannel* sSuspendedVoiceChannel; + static BOOL sSuspended; }; class LLVoiceChannelGroup : public LLVoiceChannel { public: LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name); - virtual ~LLVoiceChannelGroup(); /*virtual*/ void handleStatusChange(EStatusType status); /*virtual*/ void handleError(EStatusType status); @@ -132,8 +139,7 @@ class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoice { public: LLVoiceChannelProximal(); - virtual ~LLVoiceChannelProximal(); - + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); /*virtual*/ void handleStatusChange(EStatusType status); /*virtual*/ void handleError(EStatusType status); @@ -147,7 +153,6 @@ class LLVoiceChannelP2P : public LLVoiceChannelGroup { public: LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id); - virtual ~LLVoiceChannelP2P(); /*virtual*/ void handleStatusChange(EStatusType status); /*virtual*/ void handleError(EStatusType status); @@ -156,9 +161,13 @@ public: void setSessionHandle(const LLString& handle); +protected: + virtual void setState(EState state); + private: LLString mSessionHandle; LLUUID mOtherUserID; + BOOL mReceivedCall; }; class LLFloaterIMPanel : public LLFloater @@ -170,15 +179,11 @@ public: // the default. For example, if you open a session though a // calling card, a new session id will be generated, but the // target_id will be the agent referenced by the calling card. - LLFloaterIMPanel(const std::string& name, - const LLRect& rect, - const std::string& session_label, + LLFloaterIMPanel(const std::string& session_label, const LLUUID& session_id, const LLUUID& target_id, EInstantMessage dialog); - LLFloaterIMPanel(const std::string& name, - const LLRect& rect, - const std::string& session_label, + LLFloaterIMPanel(const std::string& session_label, const LLUUID& session_id, const LLUUID& target_id, const LLDynamicArray<LLUUID>& ids, @@ -189,8 +194,8 @@ public: // Check typing timeout timer. /*virtual*/ void draw(); - /*virtual*/ void onClose(bool app_quitting = FALSE); + /*virtual*/ void onVisibilityChange(BOOL new_visibility); // add target ids to the session. // Return TRUE if successful, otherwise FALSE. @@ -209,14 +214,16 @@ public: void selectNone(); void setVisible(BOOL b); + S32 getNumUnreadMessages() { return mNumUnreadMessages; } + BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, LLString& tooltip_msg); - static void onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ); - static void onInputEditorFocusLost(LLUICtrl* caller, void* userdata); + static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); static void onCommitChat(LLUICtrl* caller, void* userdata); static void onTabClick( void* userdata ); @@ -229,16 +236,17 @@ public: static void onClickSend( void* userdata ); static void onClickToggleActiveSpeakers( void* userdata ); static void* createSpeakersPanel(void* data); + static void onKickSpeaker(void* user_data); //callbacks for P2P muting and volume control - static void onClickMuteVoice(LLUICtrl* source, void* user_data); + static void onClickMuteVoice(void* user_data); static void onVolumeChange(LLUICtrl* source, void* user_data); const LLUUID& getSessionID() const { return mSessionUUID; } const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; } - void updateSpeakersList(LLSD speaker_updates); - void setSpeakersListFromMap(LLSD speaker_list); - void setSpeakersList(LLSD speaker_list); + void updateSpeakersList(const LLSD& speaker_updates); + void processSessionUpdate(const LLSD& update); + void setSpeakers(const LLSD& speaker_list); LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } EInstantMessage getDialogType() const { return mDialog; } @@ -250,6 +258,15 @@ public: void processIMTyping(const LLIMInfo* im_info, BOOL typing); static void chatFromLogFile(LLString line, void* userdata); + //show error statuses to the user + void showSessionStartError(const std::string& error_string); + void showSessionEventError( + const std::string& event_string, + const std::string& error_string); + void showSessionForceClose(const std::string& reason); + + static void onConfirmForceCloseError(S32 option, void* data); + private: // called by constructors void init(const LLString& session_label); @@ -289,6 +306,7 @@ private: // 911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID() LLUUID mSessionUUID; + LLString mSessionLabel; LLVoiceChannel* mVoiceChannel; BOOL mSessionInitialized; @@ -318,6 +336,8 @@ private: // Where does the "Starting session..." line start? S32 mSessionStartMsgPos; + S32 mNumUnreadMessages; + BOOL mSentTypingState; BOOL mShowSpeakersOnConnect; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9c37f1f333..f93f5810c5 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -46,7 +46,6 @@ #include "llagent.h" #include "llcallingcard.h" #include "llchat.h" -#include "llviewerwindow.h" #include "llresmgr.h" #include "llfloaterchat.h" #include "llfloaterchatterbox.h" @@ -65,6 +64,7 @@ #include "llcallingcard.h" #include "lltoolbar.h" #include "llviewermessage.h" +#include "llviewerwindow.h" #include "llnotify.h" #include "llviewerregion.h" @@ -84,11 +84,12 @@ LLIMMgr* gIMMgr = NULL; //*FIXME: make these all either UIStrings or Strings static LLString sOnlyUserMessage; static LLUIString sOfflineMessage; - -static std::map<std::string,LLString> sEventStringsMap; -static std::map<std::string,LLString> sErrorStringsMap; -static std::map<std::string,LLString> sForceCloseSessionMap; static LLUIString sInviteMessage; + +std::map<std::string,LLString> LLFloaterIM::sEventStringsMap; +std::map<std::string,LLString> LLFloaterIM::sErrorStringsMap; +std::map<std::string,LLString> LLFloaterIM::sForceCloseSessionMap; + // // Helper Functions // @@ -160,22 +161,54 @@ BOOL LLFloaterIM::postBuild() sOnlyUserMessage = getFormattedUIString("only_user_message"); sOfflineMessage = getUIString("offline_message"); - sErrorStringsMap["generic"] = - getFormattedUIString("generic_request_error"); - sErrorStringsMap["unverified"] = - getFormattedUIString("insufficient_perms_error"); - sErrorStringsMap["no_user_911"] = - getFormattedUIString("user_no_help"); + sInviteMessage = getUIString("invite_message"); - sEventStringsMap["add"] = - getFormattedUIString("add_session_event"); - sEventStringsMap["message"] = - getFormattedUIString("message_session_event"); + if ( sErrorStringsMap.find("generic") == sErrorStringsMap.end() ) + { + sErrorStringsMap["generic"] = + getFormattedUIString("generic_request_error"); + } - sForceCloseSessionMap["removed"] = - getFormattedUIString("removed_from_group"); + if ( sErrorStringsMap.find("unverified") == + sErrorStringsMap.end() ) + { + sErrorStringsMap["unverified"] = + getFormattedUIString("insufficient_perms_error"); + } + + if ( sErrorStringsMap.end() == + sErrorStringsMap.find("does not exist") ) + { + sErrorStringsMap["does not exist"] = + getFormattedUIString("session_does_not_exist_error"); + } + + if ( sEventStringsMap.end() == sEventStringsMap.find("add") ) + { + sEventStringsMap["add"] = + getFormattedUIString("add_session_event"); + } + + if ( sEventStringsMap.end() == sEventStringsMap.find("message") ) + { + sEventStringsMap["message"] = + getFormattedUIString("message_session_event"); + } + + if ( sForceCloseSessionMap.end() == + sForceCloseSessionMap.find("removed") ) + { + sForceCloseSessionMap["removed"] = + getFormattedUIString("removed_from_group"); + } + + if ( sForceCloseSessionMap.end() == + sForceCloseSessionMap.find("no ability") ) + { + sForceCloseSessionMap["no ability"] = + getFormattedUIString("close_on_no_ability"); + } - sInviteMessage = getUIString("invite_message"); return TRUE; } @@ -205,21 +238,31 @@ protected: class LLIMMgr::LLIMSessionInvite { public: - LLIMSessionInvite(const LLUUID& session_id, const LLString& session_name, const LLUUID& caller_id,const LLString& caller_name, EInstantMessage type, const LLString& session_handle, const LLString& notify_box) : - mSessionID(session_id), - mSessionName(session_name), - mCallerID(caller_id), - mCallerName(caller_name), - mType(type), - mSessionHandle(session_handle), - mNotifyBox(notify_box) - {}; + LLIMSessionInvite( + const LLUUID& session_id, + const LLString& session_name, + const LLUUID& caller_id, + const LLString& caller_name, + EInstantMessage type, + EInvitationType inv_type, + const LLString& session_handle, + const LLString& notify_box) : + mSessionID(session_id), + mSessionName(session_name), + mCallerID(caller_id), + mCallerName(caller_name), + mType(type), + mInvType(inv_type), + mSessionHandle(session_handle), + mNotifyBox(notify_box) + {}; LLUUID mSessionID; LLString mSessionName; LLUUID mCallerID; LLString mCallerName; EInstantMessage mType; + EInvitationType mInvType; LLString mSessionHandle; LLString mNotifyBox; }; @@ -309,7 +352,7 @@ LLIMMgr::LLIMMgr() : LLFloaterIM* dummy_floater = new LLFloaterIM(); delete dummy_floater; - mPendingVoiceInvitations = LLSD::emptyMap(); + mPendingInvitations = LLSD::emptyMap(); mPendingAgentListUpdates = LLSD::emptyMap(); } @@ -413,7 +456,6 @@ void LLIMMgr::addMessage( if ( is_from_system ) // chat came from system { floater->addHistoryLine( - other_participant_id, msg, gSavedSettings.getColor4("SystemChatColor")); } @@ -521,9 +563,10 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, // the session, dialog specifies the type of session. If the session // exists, it is brought forward. Specifying id = NULL results in an // im session to everyone. Returns the uuid of the session. -LLUUID LLIMMgr::addSession(const std::string& name, - EInstantMessage dialog, - const LLUUID& other_participant_id) +LLUUID LLIMMgr::addSession( + const std::string& name, + EInstantMessage dialog, + const LLUUID& other_participant_id) { LLUUID session_id = computeSessionID(dialog, other_participant_id); @@ -533,15 +576,16 @@ LLUUID LLIMMgr::addSession(const std::string& name, LLDynamicArray<LLUUID> ids; ids.put(other_participant_id); - floater = createFloater(session_id, - other_participant_id, - name, - ids, - dialog, - TRUE); + floater = createFloater( + session_id, + other_participant_id, + name, + ids, + dialog, + TRUE); noteOfflineUsers(floater, ids); - LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater); + LLFloaterChatterBox::showInstance(session_id); } else { @@ -554,36 +598,43 @@ LLUUID LLIMMgr::addSession(const std::string& name, // Adds a session using the given session_id. If the session already exists // the dialog type is assumed correct. Returns the uuid of the session. -LLUUID LLIMMgr::addSession(const std::string& name, - EInstantMessage dialog, - const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids) +LLUUID LLIMMgr::addSession( + const std::string& name, + EInstantMessage dialog, + const LLUUID& other_participant_id, + const LLDynamicArray<LLUUID>& ids) { if (0 == ids.getLength()) { return LLUUID::null; } - LLUUID session_id = computeSessionID(dialog, - other_participant_id); + LLUUID session_id = computeSessionID( + dialog, + other_participant_id); LLFloaterIMPanel* floater = findFloaterBySession(session_id); if(!floater) { // On creation, use the first element of ids as the // "other_participant_id" - floater = createFloater(session_id, - other_participant_id, - name, - ids, - dialog, - TRUE); + floater = createFloater( + session_id, + other_participant_id, + name, + ids, + dialog, + TRUE); if ( !floater ) return LLUUID::null; noteOfflineUsers(floater, ids); + LLFloaterChatterBox::showInstance(session_id); + } + else + { + floater->open(); } - LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater); //mTabContainer->selectTabPanel(panel); floater->setInputFocus(TRUE); return floater->getSessionID(); @@ -599,6 +650,9 @@ void LLIMMgr::removeSession(const LLUUID& session_id) mFloaters.erase(floater->getHandle()); LLFloaterChatterBox::getInstance(LLSD())->removeFloater(floater); //mTabContainer->removeTabPanel(floater); + + clearPendingInviation(session_id); + clearPendingAgentListUpdates(session_id); } } @@ -608,9 +662,10 @@ void LLIMMgr::inviteToSession( const LLUUID& caller_id, const LLString& caller_name, EInstantMessage type, + EInvitationType inv_type, const LLString& session_handle) { - //ignore voice invites from voice-muted residents + //ignore invites from muted residents if (gMuteListp->isMuted(caller_id)) { return; @@ -621,17 +676,26 @@ void LLIMMgr::inviteToSession( BOOL ad_hoc_invite = FALSE; if(type == IM_SESSION_P2P_INVITE) { + //P2P is different...they only have voice invitations notify_box_type = "VoiceInviteP2P"; } - else if (gAgent.isInGroup(session_id)) + else if ( gAgent.isInGroup(session_id) ) { + //only really old school groups have voice invitations notify_box_type = "VoiceInviteGroup"; } - else + else if ( inv_type == INVITATION_TYPE_VOICE ) { + //else it's an ad-hoc + //and a voice ad-hoc notify_box_type = "VoiceInviteAdHoc"; ad_hoc_invite = TRUE; } + else if ( inv_type == INVITATION_TYPE_IMMEDIATE ) + { + notify_box_type = "InviteAdHoc"; + ad_hoc_invite = TRUE; + } LLIMSessionInvite* invite = new LLIMSessionInvite( session_id, @@ -639,6 +703,7 @@ void LLIMMgr::inviteToSession( caller_id, caller_name, type, + inv_type, session_handle, notify_box_type); @@ -666,7 +731,7 @@ void LLIMMgr::inviteToSession( } } - if ( !mPendingVoiceInvitations.has(session_id.asString()) ) + if ( !mPendingInvitations.has(session_id.asString()) ) { if (caller_name.empty()) { @@ -684,7 +749,7 @@ void LLIMMgr::inviteToSession( (void*)invite); } - mPendingVoiceInvitations[session_id.asString()] = LLSD(); + mPendingInvitations[session_id.asString()] = LLSD(); } } @@ -699,10 +764,11 @@ void LLIMMgr::onInviteNameLookup(const LLUUID& id, const char* first, const char LLString::format_map_t args; args["[NAME]"] = invite->mCallerName; - LLNotifyBox::showXml(invite->mNotifyBox, - args, - inviteUserResponse, - (void*)invite); + LLNotifyBox::showXml( + invite->mNotifyBox, + args, + inviteUserResponse, + (void*)invite); } class LLViewerChatterBoxInvitationAcceptResponder : @@ -711,10 +777,10 @@ class LLViewerChatterBoxInvitationAcceptResponder : public: LLViewerChatterBoxInvitationAcceptResponder( const LLUUID& session_id, - bool is_voice_invitation) + LLIMMgr::EInvitationType invitation_type) { mSessionID = session_id; - mIsVoiceInvitiation = is_voice_invitation; + mInvitiationType = invitation_type; } void result(const LLSD& content) @@ -738,48 +804,67 @@ public: //but unfortunately, our base that we are receiving here //may not be the most up to date. It was accurate at //some point in time though. - floaterp->setSpeakersList(content["agents"]); + floaterp->setSpeakers(content); //we now have our base of users in the session //that was accurate at some point, but maybe not now //so now we apply all of the udpates we've received //in case of race conditions - - //reapplying a user entrance will do nothing - //reapplying a user leaving will not have the user - //in our base. So it's all good floaterp->updateSpeakersList( gIMMgr->getPendingAgentListUpdates(mSessionID)); - if ( mIsVoiceInvitiation ) + if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE ) { floaterp->requestAutoConnect(); LLFloaterIMPanel::onClickStartCall(floaterp); // always open IM window when connecting to voice LLFloaterChatterBox::showInstance(TRUE); } + else if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE ) + { + LLFloaterChatterBox::showInstance(TRUE); + } } gIMMgr->clearPendingAgentListUpdates(mSessionID); - if ( mIsVoiceInvitiation ) - { - gIMMgr->clearPendingVoiceInviation(mSessionID); - } + gIMMgr->clearPendingInviation(mSessionID); } } void error(U32 statusNum, const std::string& reason) { + //throw something back to the viewer here? - if ( gIMMgr && mIsVoiceInvitiation ) + if ( gIMMgr ) { - gIMMgr->clearPendingVoiceInviation(mSessionID); + gIMMgr->clearPendingAgentListUpdates(mSessionID); + gIMMgr->clearPendingInviation(mSessionID); + + LLFloaterIMPanel* floaterp = + gIMMgr->findFloaterBySession(mSessionID); + + if (floaterp) + { + std::string error_string; + + if ( 404 == statusNum ) + { + error_string = "does not exist"; + } + else + { + error_string = "generic_request_error"; + } + + floaterp->showSessionStartError( + error_string); + } } } private: LLUUID mSessionID; - bool mIsVoiceInvitiation; + LLIMMgr::EInvitationType mInvitiationType; }; //static @@ -807,10 +892,11 @@ void LLIMMgr::inviteUserResponse(S32 option, void* user_data) im_floater->requestAutoConnect(); LLFloaterIMPanel::onClickStartCall(im_floater); // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(TRUE); + LLFloaterChatterBox::showInstance(invitep->mSessionID); } - - gIMMgr->clearPendingVoiceInviation(invitep->mSessionID); + + gIMMgr->clearPendingAgentListUpdates(invitep->mSessionID); + gIMMgr->clearPendingInviation(invitep->mSessionID); } else { @@ -830,34 +916,50 @@ void LLIMMgr::inviteUserResponse(S32 option, void* user_data) data, new LLViewerChatterBoxInvitationAcceptResponder( invitep->mSessionID, - true)); + invitep->mInvType)); } } break; case 2: // mute (also implies ignore, so this falls through to the "ignore" case below) + { + // mute the sender of this invite + if (!gMuteListp->isMuted(invitep->mCallerID)) { - // mute the sender of this invite - if (!gMuteListp->isMuted(invitep->mCallerID)) - { - LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT); - gMuteListp->add(mute); - } + LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT); + gMuteListp->add(mute); } + } /* FALLTHROUGH */ - case 1: // ignore + case 1: // decline + { + if (invitep->mType == IM_SESSION_P2P_INVITE) { - if (invitep->mType == IM_SESSION_P2P_INVITE) + if(gVoiceClient) { - if(gVoiceClient) - { - gVoiceClient->declineInvite(invitep->mSessionHandle); - } + gVoiceClient->declineInvite(invitep->mSessionHandle); } } - break; + else + { + std::string url = gAgent.getRegion()->getCapability( + "ChatSessionRequest"); + + LLSD data; + data["method"] = "decline invitation"; + data["session-id"] = invitep->mSessionID; + LLHTTPClient::post( + url, + data, + NULL); + } } + gIMMgr->clearPendingAgentListUpdates(invitep->mSessionID); + gIMMgr->clearPendingInviation(invitep->mSessionID); + break; + } + delete invitep; } @@ -869,11 +971,11 @@ void LLIMMgr::setFloaterOpen(BOOL set_open) { if (set_open) { - LLFloaterChatterBox::showInstance(LLSD()); + LLFloaterChatterBox::showInstance(); } else { - LLFloaterChatterBox::hideInstance(LLSD()); + LLFloaterChatterBox::hideInstance(); } } @@ -932,11 +1034,11 @@ BOOL LLIMMgr::hasSession(const LLUUID& session_id) return (findFloaterBySession(session_id) != NULL); } -void LLIMMgr::clearPendingVoiceInviation(const LLUUID& session_id) +void LLIMMgr::clearPendingInviation(const LLUUID& session_id) { - if ( mPendingVoiceInvitations.has(session_id.asString()) ) + if ( mPendingInvitations.has(session_id.asString()) ) { - mPendingVoiceInvitations.erase(session_id.asString()); + mPendingInvitations.erase(session_id.asString()); } } @@ -958,13 +1060,57 @@ void LLIMMgr::addPendingAgentListUpdates( { LLSD::map_const_iterator iter; - for ( iter = updates.beginMap(); - iter != updates.endMap(); - iter++) + if ( !mPendingAgentListUpdates.has(session_id.asString()) ) { - //we only want to include the last update for a given agent - mPendingAgentListUpdates[session_id.asString()][iter->first] = - iter->second; + //this is a new agent list update for this session + mPendingAgentListUpdates[session_id.asString()] = LLSD::emptyMap(); + } + + if ( + updates.has("agent_updates") && + updates["agent_updates"].isMap() && + updates.has("updates") && + updates["updates"].isMap() ) + { + //new school update + LLSD update_types = LLSD::emptyArray(); + LLSD::array_iterator array_iter; + + update_types.append("agent_updates"); + update_types.append("updates"); + + for ( + array_iter = update_types.beginArray(); + array_iter != update_types.endArray(); + ++array_iter) + { + //we only want to include the last update for a given agent + for ( + iter = updates[array_iter->asString()].beginMap(); + iter != updates[array_iter->asString()].endMap(); + ++iter) + { + mPendingAgentListUpdates[session_id.asString()][array_iter->asString()][iter->first] = + iter->second; + } + } + } + else if ( + updates.has("updates") && + updates["updates"].isMap() ) + { + //old school update where the SD contained just mappings + //of agent_id -> "LEAVE"/"ENTER" + + //only want to keep last update for each agent + for ( + iter = updates["updates"].beginMap(); + iter != updates["updates"].endMap(); + ++iter) + { + mPendingAgentListUpdates[session_id.asString()]["updates"][iter->first] = + iter->second; + } } } @@ -995,8 +1141,6 @@ LLFloaterIMPanel* LLIMMgr::createFloater( llinfos << "LLIMMgr::createFloater: from " << other_participant_id << " in session " << session_id << llendl; LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, - LLRect(), - session_label, session_id, other_participant_id, dialog); @@ -1022,8 +1166,6 @@ LLFloaterIMPanel* LLIMMgr::createFloater( llinfos << "LLIMMgr::createFloater: from " << other_participant_id << " in session " << session_id << llendl; LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, - LLRect(), - session_label, session_id, other_participant_id, ids, @@ -1034,8 +1176,9 @@ LLFloaterIMPanel* LLIMMgr::createFloater( return floater; } -void LLIMMgr::noteOfflineUsers(LLFloaterIMPanel* floater, - const LLDynamicArray<LLUUID>& ids) +void LLIMMgr::noteOfflineUsers( + LLFloaterIMPanel* floater, + const LLDynamicArray<LLUUID>& ids) { S32 count = ids.count(); if(count == 0) @@ -1099,14 +1242,6 @@ LLFloaterChatterBox* LLIMMgr::getFloater() return LLFloaterChatterBox::getInstance(LLSD()); } -void onConfirmForceCloseError(S32 option, void* data) -{ - //only 1 option really - LLFloaterIMPanel* floater = ((LLFloaterIMPanel*) data); - - if ( floater ) floater->close(FALSE); -} - class LLViewerChatterBoxSessionStartReply : public LLHTTPNode { public: @@ -1141,7 +1276,16 @@ public: LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); if (floaterp) { - floaterp->setSpeakersList(body["agents"]); + floaterp->setSpeakers(body); + + //apply updates we've possibly received previously + floaterp->updateSpeakersList( + gIMMgr->getPendingAgentListUpdates(session_id)); + + if ( body.has("session_info") ) + { + floaterp->processSessionUpdate(body["session_info"]); + } //aply updates we've possibly received previously floaterp->updateSpeakersList( @@ -1155,20 +1299,14 @@ public: //floater LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id); - if (floater) - { - LLString::format_map_t args; - args["[REASON]"] = - sErrorStringsMap[body["error"].asString()]; - args["[RECIPIENT]"] = floater->getTitle(); - - gViewerWindow->alertXml("ChatterBoxSessionStartError", - args, - onConfirmForceCloseError, - floater); + if ( floater ) + { + floater->showSessionStartError(body["error"].asString()); } } + + gIMMgr->clearPendingAgentListUpdates(session_id); } }; @@ -1200,17 +1338,12 @@ public: //throw an error dialog LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (floater) { - LLString::format_map_t args; - args["[REASON]"] = - sErrorStringsMap[body["error"].asString()]; - args["[EVENT]"] = - sEventStringsMap[body["event"].asString()]; - args["[RECIPIENT]"] = floater->getTitle(); - - gViewerWindow->alertXml("ChatterBoxSessionEventError", - args); + floater->showSessionEventError( + body["event"].asString(), + body["error"].asString()); } } } @@ -1234,15 +1367,7 @@ public: if ( floater ) { - LLString::format_map_t args; - - args["[NAME]"] = floater->getTitle(); - args["[REASON]"] = sForceCloseSessionMap[reason]; - - gViewerWindow->alertXml("ForceCloseChatterBoxSession", - args, - onConfirmForceCloseError, - floater); + floater->showSessionForceClose(reason); } } }; @@ -1258,7 +1383,8 @@ public: LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); if (floaterp) { - floaterp->updateSpeakersList(input["body"]["updates"]); + floaterp->updateSpeakersList( + input["body"]); } else { @@ -1267,11 +1393,28 @@ public: //a start or an acceptance of an invitation. Race condition. gIMMgr->addPendingAgentListUpdates( input["body"]["session_id"].asUUID(), - input["body"]["updates"]); + input["body"]); + } + } +}; + +class LLViewerChatterBoxSessionUpdate : public LLHTTPNode +{ +public: + virtual void post( + ResponsePtr responder, + const LLSD& context, + const LLSD& input) const + { + LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); + if (floaterp) + { + floaterp->processSessionUpdate(input["body"]["info"]); } } }; + class LLViewerChatterBoxInvitation : public LLHTTPNode { public: @@ -1281,6 +1424,8 @@ public: const LLSD& context, const LLSD& input) const { + //for backwards compatiblity reasons...we need to still + //check for 'text' or 'voice' invitations...bleh if ( input["body"].has("instantmessage") ) { LLString capability = input["body"]["capabilities"]["call"].asString(); @@ -1397,8 +1542,8 @@ public: url, data, new LLViewerChatterBoxInvitationAcceptResponder( - input["body"]["session_id"], - false)); + input["body"]["session_id"].asUUID(), + LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE)); } } //end if invitation has instant message else if ( input["body"].has("voice") ) @@ -1419,7 +1564,18 @@ public: input["body"]["session_name"].asString(), input["body"]["from_id"].asUUID(), input["body"]["from_name"].asString(), - IM_SESSION_INVITE); + IM_SESSION_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE); + } + else if ( input["body"].has("immediate") ) + { + gIMMgr->inviteToSession( + input["body"]["session_id"].asUUID(), + input["body"]["session_name"].asString(), + input["body"]["from_id"].asUUID(), + input["body"]["from_name"].asString(), + IM_SESSION_INVITE, + LLIMMgr::INVITATION_TYPE_IMMEDIATE); } } }; @@ -1440,6 +1596,10 @@ LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates> gHTTPRegistrationMessageChatterboxsessionagentlistupdates( "/message/ChatterBoxSessionAgentListUpdates"); +LLHTTPRegistration<LLViewerChatterBoxSessionUpdate> + gHTTPRegistrationMessageChatterBoxSessionUpdate( + "/message/ChatterBoxSessionUpdate"); + LLHTTPRegistration<LLViewerChatterBoxInvitation> gHTTPRegistrationMessageChatterBoxInvitation( "/message/ChatterBoxInvitation"); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index f5356ef926..11a6905a63 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -45,6 +45,13 @@ class LLFloaterIM; class LLIMMgr : public LLSingleton<LLIMMgr> { public: + enum EInvitationType + { + INVITATION_TYPE_INSTANT_MESSAGE = 0, + INVITATION_TYPE_VOICE = 1, + INVITATION_TYPE_IMMEDIATE = 2 + }; + LLIMMgr(); virtual ~LLIMMgr(); @@ -96,12 +103,14 @@ public: // deleted. void removeSession(const LLUUID& session_id); - void inviteToSession(const LLUUID& session_id, - const LLString& session_name, - const LLUUID& caller, - const LLString& caller_name, - EInstantMessage type, - const LLString& session_handle = LLString::null); + void inviteToSession( + const LLUUID& session_id, + const LLString& session_name, + const LLUUID& caller, + const LLString& caller_name, + EInstantMessage type, + EInvitationType inv_type, + const LLString& session_handle = LLString::null); //Updates a given session's session IDs. Does not open, //create or do anything new. If the old session doesn't @@ -147,7 +156,7 @@ public: static LLUUID computeSessionID(EInstantMessage dialog, const LLUUID& other_participant_id); - void clearPendingVoiceInviation(const LLUUID& session_id); + void clearPendingInviation(const LLUUID& session_id); LLSD getPendingAgentListUpdates(const LLUUID& session_id); void addPendingAgentListUpdates( @@ -155,6 +164,9 @@ public: const LLSD& updates); void clearPendingAgentListUpdates(const LLUUID& session_id); + //HACK: need a better way of enumerating existing session, or listening to session create/destroy events + const std::set<LLViewHandle>& getIMFloaterHandles() { return mFloaters; } + private: class LLIMSessionInvite; @@ -193,7 +205,7 @@ private: // An IM has been received that you haven't seen yet. BOOL mIMReceived; - LLSD mPendingVoiceInvitations; + LLSD mPendingInvitations; LLSD mPendingAgentListUpdates; }; @@ -203,6 +215,10 @@ class LLFloaterIM : public LLMultiFloater public: LLFloaterIM(); /*virtual*/ BOOL postBuild(); + + static std::map<std::string,LLString> sEventStringsMap; + static std::map<std::string,LLString> sErrorStringsMap; + static std::map<std::string,LLString> sForceCloseSessionMap; }; // Globals diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 9c9b1ad257..d8841afe22 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3165,7 +3165,7 @@ void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model std::string(), cb); } - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } else if ("detach" == action) { diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index ca65b879bc..7b27a830c4 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2606,7 +2606,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) // } // // // restore keyboard focus - // gFocusMgr.setKeyboardFocus(focus_view, callback); + // gFocusMgr.setKeyboardFocus(focus_view); //} } diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp index 20c4a25003..30106443c0 100644 --- a/indra/newview/lljoystickbutton.cpp +++ b/indra/newview/lljoystickbutton.cpp @@ -607,26 +607,26 @@ void LLJoystickCameraRotate::draw() { LLGLSUIDefault gls_ui; - gl_draw_image( 0, 0, mImageUnselected ); + mImageUnselected->draw( 0, 0 ); if( mInTop ) { - drawRotatedImage( mImageSelected, 0 ); + drawRotatedImage( mImageSelected->getImage(), 0 ); } if( mInRight ) { - drawRotatedImage( mImageSelected, 1 ); + drawRotatedImage( mImageSelected->getImage(), 1 ); } if( mInBottom ) { - drawRotatedImage( mImageSelected, 2 ); + drawRotatedImage( mImageSelected->getImage(), 2 ); } if( mInLeft ) { - drawRotatedImage( mImageSelected, 3 ); + drawRotatedImage( mImageSelected->getImage(), 3 ); } if (sDebugRects) @@ -801,7 +801,7 @@ void LLJoystickCameraZoom::draw() } else { - gl_draw_image( 0, 0, mImageUnselected ); + mImageUnselected->draw( 0, 0 ); } if (sDebugRects) diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 4d02af8fae..60d39b62bd 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -52,10 +52,11 @@ #include <boost/tokenizer.hpp> #include "llcrc.h" +#include "lldir.h" #include "lldispatcher.h" +#include "llsdserialize.h" #include "llxfermanager.h" #include "message.h" -#include "lldir.h" #include "llagent.h" #include "llfloatermute.h" @@ -67,6 +68,9 @@ LLMuteList* gMuteListp = NULL; +std::map<LLUUID, F32> LLMuteList::sUserVolumeSettings; + + // "emptymutelist" class LLDispatchEmptyMuteList : public LLDispatchHandler { @@ -168,6 +172,24 @@ LLMuteList::LLMuteList() : msg->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList); gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); + + // load per-resident voice volume information + // conceptually, this is part of the mute list information, although it is only stored locally + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "volume_settings.xml"); + + LLSD settings_llsd; + llifstream file; + file.open(filename.c_str()); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + sUserVolumeSettings.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal())); + } } //----------------------------------------------------------------------------- @@ -175,6 +197,17 @@ LLMuteList::LLMuteList() : //----------------------------------------------------------------------------- LLMuteList::~LLMuteList() { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "volume_settings.xml"); + LLSD settings_llsd; + + for(user_volume_map_t::iterator iter = sUserVolumeSettings.begin(); iter != sUserVolumeSettings.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; + } + + llofstream file; + file.open(filename.c_str()); + LLSDSerialize::toPrettyXML(settings_llsd, file); } BOOL LLMuteList::isLinden(const LLString& name) const @@ -588,6 +621,25 @@ void LLMuteList::cache(const LLUUID& agent_id) } } +void LLMuteList::setSavedResidentVolume(const LLUUID& id, F32 volume) +{ + // store new value in volume settings file + sUserVolumeSettings[id] = volume; +} + +F32 LLMuteList::getSavedResidentVolume(const LLUUID& id) +{ + const F32 DEFAULT_VOLUME = 0.5f; + + user_volume_map_t::iterator found_it = sUserVolumeSettings.find(id); + if (found_it != sUserVolumeSettings.end()) + { + return found_it->second; + } + //FIXME: assumes default, should get this from somewhere + return DEFAULT_VOLUME; +} + //----------------------------------------------------------------------------- // Static message handlers diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 23e0620ade..413a2933b3 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -112,6 +112,9 @@ public: // call this method on logout to save everything. void cache(const LLUUID& agent_id); + void setSavedResidentVolume(const LLUUID& id, F32 volume); + F32 getSavedResidentVolume(const LLUUID& id); + private: BOOL loadFromFile(const LLString& filename); BOOL saveToFile(const LLString& filename); @@ -155,6 +158,9 @@ private: BOOL mIsLoaded; friend class LLDispatchEmptyMuteList; + + typedef std::map<LLUUID, F32> user_volume_map_t; + static user_volume_map_t sUserVolumeSettings; }; class LLMuteListObserver diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index ba1280550f..1ca702b0a7 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -53,7 +53,7 @@ LLNameEditor::LLNameEditor(const std::string& name, const LLRect& rect, S32 max_text_length, void (*commit_callback)(LLUICtrl* caller, void* user_data), void (*keystroke_callback)(LLLineEditor* caller, void* user_data), - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data), + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data), void* userdata, LLLinePrevalidateFunc prevalidate_func, LLViewBorder::EBevel border_bevel, diff --git a/indra/newview/llnameeditor.h b/indra/newview/llnameeditor.h index 526b76b9c0..9c89454c33 100644 --- a/indra/newview/llnameeditor.h +++ b/indra/newview/llnameeditor.h @@ -53,7 +53,7 @@ public: S32 max_text_length = 254, void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL, void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL, - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data) = NULL, + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data) = NULL, void* userdata = NULL, LLLinePrevalidateFunc prevalidate_func = NULL, LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN, diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index adcc141bb1..a47721be9d 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -224,7 +224,7 @@ LLScrollListItem* LLNameListCtrl::addElement(const LLSD& value, EAddPosition pos LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex); ((LLScrollListText*)cell)->setText( fullname ); - updateMaxContentWidth(item); + calcMaxContentWidth(item); // this column is resizable LLScrollListColumn* columnp = getColumn(mNameColumnIndex); @@ -277,7 +277,7 @@ void LLNameListCtrl::refresh(const LLUUID& id, const char* first, cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex); ((LLScrollListText*)cell)->setText( fullname ); - updateMaxContentWidth(item); + calcMaxContentWidth(item); } } } diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 15c993e552..e7c313638b 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -39,6 +39,7 @@ #include "audioengine.h" #include "llagent.h" #include "llbutton.h" +#include "llchatbar.h" #include "llfocusmgr.h" #include "llimview.h" #include "llmediaengine.h" @@ -71,37 +72,14 @@ extern S32 MENU_BAR_HEIGHT; // -//static -void* LLOverlayBar::createMasterRemote(void* userdata) -{ - LLOverlayBar *self = (LLOverlayBar*)userdata; - self->mMasterRemote = new LLMediaRemoteCtrl ( "master_volume", - "volume", - LLRect(), - "panel_master_volume.xml"); - return self->mMasterRemote; -} void* LLOverlayBar::createMediaRemote(void* userdata) { LLOverlayBar *self = (LLOverlayBar*)userdata; - self->mMediaRemote = new LLMediaRemoteCtrl ( "media_remote", - "media", - LLRect(), - "panel_media_remote.xml"); + self->mMediaRemote = new LLMediaRemoteCtrl (); return self->mMediaRemote; } -void* LLOverlayBar::createMusicRemote(void* userdata) -{ - LLOverlayBar *self = (LLOverlayBar*)userdata; - self->mMusicRemote = new LLMediaRemoteCtrl ( "music_remote", - "music", - LLRect(), - "panel_music_remote.xml" ); - return self->mMusicRemote; -} - void* LLOverlayBar::createVoiceRemote(void* userdata) { LLOverlayBar *self = (LLOverlayBar*)userdata; @@ -109,13 +87,14 @@ void* LLOverlayBar::createVoiceRemote(void* userdata) return self->mVoiceRemote; } +void* LLOverlayBar::createChatBar(void* userdata) +{ + gChatBar = new LLChatBar(); + return gChatBar; +} - - -LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect) - : LLPanel(name, rect, FALSE), // not bordered - mMasterRemote(NULL), - mMusicRemote(NULL), +LLOverlayBar::LLOverlayBar() + : LLPanel(), mMediaRemote(NULL), mVoiceRemote(NULL), mMediaState(STOPPED), @@ -127,25 +106,27 @@ LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect) mBuilt = false; LLCallbackMap::map_t factory_map; - factory_map["master_volume"] = LLCallbackMap(LLOverlayBar::createMasterRemote, this); factory_map["media_remote"] = LLCallbackMap(LLOverlayBar::createMediaRemote, this); - factory_map["music_remote"] = LLCallbackMap(LLOverlayBar::createMusicRemote, this); factory_map["voice_remote"] = LLCallbackMap(LLOverlayBar::createVoiceRemote, this); + factory_map["chat_bar"] = LLCallbackMap(LLOverlayBar::createChatBar, this); gUICtrlFactory->buildPanel(this, "panel_overlaybar.xml", &factory_map); - +} + +BOOL LLOverlayBar::postBuild() +{ childSetAction("IM Received",onClickIMReceived,this); childSetAction("Set Not Busy",onClickSetNotBusy,this); childSetAction("Release Keys",onClickReleaseKeys,this); childSetAction("Mouselook",onClickMouselook,this); childSetAction("Stand Up",onClickStandUp,this); + childSetVisible("chat_bar", gSavedSettings.getBOOL("ChatVisible")); mIsFocusRoot = TRUE; mBuilt = true; - // make overlay bar conform to window size - setRect(rect); layoutButtons(); + return TRUE; } LLOverlayBar::~LLOverlayBar() @@ -176,146 +157,45 @@ void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent) void LLOverlayBar::layoutButtons() { - S32 width = mRect.getWidth(); - if (width > 1024) width = 1024; - - S32 count = getChildCount(); - const S32 PAD = gSavedSettings.getS32("StatusBarPad"); - - const S32 num_media_controls = 3; - S32 media_remote_width = mMediaRemote ? mMediaRemote->getRect().getWidth() : 0; - S32 music_remote_width = mMusicRemote ? mMusicRemote->getRect().getWidth() : 0; - S32 voice_remote_width = mVoiceRemote ? mVoiceRemote->getRect().getWidth() : 0; - S32 master_remote_width = mMasterRemote ? mMasterRemote->getRect().getWidth() : 0; - - // total reserved width for all media remotes - const S32 ENDPAD = 20; - S32 remote_total_width = media_remote_width + PAD + music_remote_width + PAD + voice_remote_width + PAD + master_remote_width + ENDPAD; + LLView* state_buttons_panel = getChildByName("state_buttons", TRUE); - // calculate button widths - F32 segment_width = (F32)(width - remote_total_width) / (F32)(count - num_media_controls); - - S32 btn_width = lltrunc(segment_width - PAD); - - // Evenly space all views - LLRect r; - S32 i = 0; - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) + if (state_buttons_panel) { - LLView *view = *child_iter; - r = view->getRect(); - r.mLeft = (width) - llround(remote_total_width + (i-num_media_controls+1)*segment_width); - r.mRight = r.mLeft + btn_width; - view->setRect(r); - i++; - } + LLViewQuery query; + LLWidgetTypeFilter widget_filter(WIDGET_TYPE_BUTTON); + query.addPreFilter(LLVisibleFilter::getInstance()); + query.addPreFilter(LLEnabledFilter::getInstance()); + query.addPreFilter(&widget_filter); - // Fix up remotes to have constant width because they can't shrink - S32 right = mRect.getWidth() - remote_total_width - PAD; - if (mMediaRemote) - { - r = mMediaRemote->getRect(); - r.mLeft = right + PAD; - right = r.mLeft + media_remote_width; - r.mRight = right; - mMediaRemote->setRect(r); - } - if (mMusicRemote) - { - r = mMusicRemote->getRect(); - r.mLeft = right + PAD; - right = r.mLeft + music_remote_width; - r.mRight = right; - mMusicRemote->setRect(r); - } - if (mVoiceRemote) - { - r = mVoiceRemote->getRect(); - r.mLeft = right + PAD; - right = r.mLeft + voice_remote_width; - r.mRight = right; - mVoiceRemote->setRect(r); - } - if (mMasterRemote) - { - r = mMasterRemote->getRect(); - r.mLeft = right + PAD; - right = r.mLeft + master_remote_width; - r.mRight = right; - mMasterRemote->setRect(r); - } - - updateRect(); -} + child_list_t button_list = query(state_buttons_panel); -void LLOverlayBar::draw() -{ - // retrieve rounded rect image - LLUUID image_id; - image_id.set(gViewerArt.getString("rounded_square.tga")); - LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + const S32 MAX_BAR_WIDTH = 600; + S32 bar_width = llclamp(state_buttons_panel->getRect().getWidth(), 0, MAX_BAR_WIDTH); - if (imagep) - { - LLGLSTexture texture_enabled; - LLViewerImage::bindTexture(imagep); + // calculate button widths + const S32 MAX_BUTTON_WIDTH = 150; + S32 segment_width = llclamp(lltrunc((F32)(bar_width) / (F32)button_list.size()), 0, MAX_BUTTON_WIDTH); + S32 btn_width = segment_width - gSavedSettings.getS32("StatusBarPad"); - const S32 PAD = gSavedSettings.getS32("StatusBarPad"); + // Evenly space all buttons, starting from left + S32 left = 0; + S32 bottom = 1; - // draw rounded rect tabs behind all children - LLRect r; - // focus highlights - LLColor4 color = gColors.getColor("FloaterFocusBorderColor"); - glColor4fv(color.mV); - if(gFocusMgr.childHasKeyboardFocus(gBottomPanel)) - { - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *view = *child_iter; - if(view->getEnabled() && view->getVisible()) - { - r = view->getRect(); - gl_segmented_rect_2d_tex(r.mLeft - PAD/3 - 1, - r.mTop + 3, - r.mRight + PAD/3 + 1, - r.mBottom, - imagep->getWidth(), - imagep->getHeight(), - 16, - ROUNDED_RECT_TOP); - } - } - } - - // main tabs - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) + for (child_list_reverse_iter_t child_iter = button_list.rbegin(); + child_iter != button_list.rend(); ++child_iter) { LLView *view = *child_iter; - if(view->getEnabled() && view->getVisible()) - { - r = view->getRect(); - // draw a nice little pseudo-3D outline - color = gColors.getColor("DefaultShadowDark"); - glColor4fv(color.mV); - gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 2, r.mRight + PAD/3, r.mBottom, - imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP); - color = gColors.getColor("DefaultHighlightLight"); - glColor4fv(color.mV); - gl_segmented_rect_2d_tex(r.mLeft - PAD/3, r.mTop + 2, r.mRight + PAD/3 - 3, r.mBottom, - imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP); - // here's the main background. Note that it overhangs on the bottom so as to hide the - // focus highlight on the bottom panel, thus producing the illusion that the focus highlight - // continues around the tabs - color = gColors.getColor("FocusBackgroundColor"); - glColor4fv(color.mV); - gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 1, r.mRight + PAD/3 - 1, r.mBottom - 1, - imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP); - } + LLRect r = view->getRect(); + r.setOriginAndSize(left, bottom, btn_width, r.getHeight()); + view->setRect(r); + left += segment_width; } } +} + +void LLOverlayBar::draw() +{ + childSetVisible("chat_bar", gSavedSettings.getBOOL("ChatVisible")); // draw children on top LLPanel::draw(); @@ -325,79 +205,74 @@ void LLOverlayBar::draw() // Per-frame updates of visibility void LLOverlayBar::refresh() { + BOOL buttons_changed = FALSE; + BOOL im_received = gIMMgr->getIMReceived(); - childSetVisible("IM Received", im_received); - childSetEnabled("IM Received", im_received); + LLButton* button = LLUICtrlFactory::getButtonByName(this, "IM Received"); + if (button && button->getVisible() != im_received) + { + button->setVisible(im_received); + sendChildToFront(button); + moveChildToBackOfTabGroup(button); + buttons_changed = TRUE; + } BOOL busy = gAgent.getBusy(); - childSetVisible("Set Not Busy", busy); - childSetEnabled("Set Not Busy", busy); + button = LLUICtrlFactory::getButtonByName(this, "Set Not Busy"); + if (button && button->getVisible() != busy) + { + button->setVisible(busy); + sendChildToFront(button); + moveChildToBackOfTabGroup(button); + buttons_changed = TRUE; + } BOOL controls_grabbed = gAgent.anyControlGrabbed(); + button = LLUICtrlFactory::getButtonByName(this, "Release Keys"); - childSetVisible("Release Keys", controls_grabbed); - childSetEnabled("Release Keys", controls_grabbed); - + if (button && button->getVisible() != controls_grabbed) + { + button->setVisible(controls_grabbed); + sendChildToFront(button); + moveChildToBackOfTabGroup(button); + buttons_changed = TRUE; + } BOOL mouselook_grabbed; mouselook_grabbed = gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_DOWN_INDEX) || gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_UP_INDEX); + button = LLUICtrlFactory::getButtonByName(this, "Mouselook"); - - childSetVisible("Mouselook", mouselook_grabbed); - childSetEnabled("Mouselook", mouselook_grabbed); + if (button && button->getVisible() != mouselook_grabbed) + { + button->setVisible(mouselook_grabbed); + sendChildToFront(button); + moveChildToBackOfTabGroup(button); + buttons_changed = TRUE; + } BOOL sitting = FALSE; if (gAgent.getAvatarObject()) { sitting = gAgent.getAvatarObject()->mIsSitting; - childSetVisible("Stand Up", sitting); - childSetEnabled("Stand Up", sitting); - } + button = LLUICtrlFactory::getButtonByName(this, "Stand Up"); - if ( mMusicRemote && gAudiop ) + if (button && button->getVisible() != sitting) { - LLParcel* parcel = gParcelMgr->getAgentParcel(); - if (!parcel - || parcel->getMusicURL().empty() - || !gSavedSettings.getBOOL("AudioStreamingMusic")) - { - mMusicRemote->setVisible(FALSE); - mMusicRemote->setEnabled(FALSE); - } - else - { - mMusicRemote->setVisible(TRUE); - mMusicRemote->setEnabled(TRUE); - } + button->setVisible(sitting); + sendChildToFront(button); + moveChildToBackOfTabGroup(button); + buttons_changed = TRUE; } - // if there is a url and a texture and media is enabled and available and media streaming is on... (phew!) - if ( mMediaRemote ) - { - if (LLMediaEngine::getInstance () && - LLMediaEngine::getInstance ()->getUrl ().length () && - LLMediaEngine::getInstance ()->getImageUUID ().notNull () && - LLMediaEngine::getInstance ()->isEnabled () && - LLMediaEngine::getInstance ()->isAvailable () && - gSavedSettings.getBOOL ( "AudioStreamingVideo" ) ) - { - // display remote control - mMediaRemote->setVisible ( TRUE ); - mMediaRemote->setEnabled ( TRUE ); - } - else - { - mMediaRemote->setVisible ( FALSE ); - mMediaRemote->setEnabled ( FALSE ); - } - } - if (mVoiceRemote) - { - mVoiceRemote->setVisible(LLVoiceClient::voiceEnabled()); - } - + // update "remotes" + childSetVisible("voice_remote_container", LLVoiceClient::voiceEnabled()); + enableMediaButtons(); + + moveChildToBackOfTabGroup(mMediaRemote); + moveChildToBackOfTabGroup(mVoiceRemote); + // turn off the whole bar in mouselook if (gAgent.cameraMouselook()) { @@ -407,6 +282,11 @@ void LLOverlayBar::refresh() { setVisible(TRUE); } + + if (buttons_changed) + { + layoutButtons(); + } } //----------------------------------------------------------------------- @@ -462,33 +342,25 @@ void LLOverlayBar::mediaPlay(void*) { return; } - gOverlayBar->mMediaState = PLAYING; // desired state - LLParcel* parcel = gParcelMgr->getAgentParcel(); - if (parcel) - { - LLString path(""); - LLMediaEngine::getInstance()->convertImageAndLoadUrl( true, false, path ); - } -} -//static -void LLOverlayBar::mediaPause(void*) -{ - if (!gOverlayBar) + + if (gOverlayBar->mMediaState != PLAYING) { - return; + gOverlayBar->mMediaState = PLAYING; // desired state + LLParcel* parcel = gParcelMgr->getAgentParcel(); + if (parcel) + { + LLString path(""); + LLMediaEngine::getInstance()->convertImageAndLoadUrl( true, false, path ); + } } - gOverlayBar->mMediaState = PAUSED; // desired state - LLMediaEngine::getInstance()->pause(); -} -//static -void LLOverlayBar::mediaStop(void*) -{ - if (!gOverlayBar) + else { - return; + gOverlayBar->mMediaState = PAUSED; // desired state + LLMediaEngine::getInstance()->pause(); } - gOverlayBar->mMediaState = STOPPED; // desired state - LLMediaEngine::getInstance()->stop(); + + //gOverlayBar->mMediaState = STOPPED; // desired state + //LLMediaEngine::getInstance()->stop(); } //static @@ -498,116 +370,75 @@ void LLOverlayBar::musicPlay(void*) { return; } - gOverlayBar->mMusicState = PLAYING; // desired state - if (gAudiop) + + if (gOverlayBar->mMusicState != PLAYING) { - LLParcel* parcel = gParcelMgr->getAgentParcel(); - if ( parcel ) + gOverlayBar->mMusicState = PLAYING; // desired state + if (gAudiop) { - // this doesn't work properly when crossing parcel boundaries - even when the - // stream is stopped, it doesn't return the right thing - commenting out for now. -// if ( gAudiop->isInternetStreamPlaying() == 0 ) + LLParcel* parcel = gParcelMgr->getAgentParcel(); + if ( parcel ) { - gAudiop->startInternetStream(parcel->getMusicURL().c_str()); + // this doesn't work properly when crossing parcel boundaries - even when the + // stream is stopped, it doesn't return the right thing - commenting out for now. + // if ( gAudiop->isInternetStreamPlaying() == 0 ) + { + gAudiop->startInternetStream(parcel->getMusicURL().c_str()); + } } } } -} -//static -void LLOverlayBar::musicPause(void*) -{ - if (!gOverlayBar) - { - return; - } - gOverlayBar->mMusicState = PAUSED; // desired state - if (gAudiop) - { - gAudiop->pauseInternetStream(1); - } -} -//static -void LLOverlayBar::musicStop(void*) -{ - if (!gOverlayBar) - { - return; - } - gOverlayBar->mMusicState = STOPPED; // desired state - if (gAudiop) + //else + //{ + // gOverlayBar->mMusicState = PAUSED; // desired state + // if (gAudiop) + // { + // gAudiop->pauseInternetStream(1); + // } + //} + else { - gAudiop->stopInternetStream(); + gOverlayBar->mMusicState = STOPPED; // desired state + if (gAudiop) + { + gAudiop->stopInternetStream(); + } } } -//static -void LLOverlayBar::enableMusicButtons(LLPanel* panel) -{ - BOOL play_enabled = FALSE; - BOOL play_visible = TRUE; - BOOL pause_visible = FALSE; - BOOL stop_enabled = FALSE; - if ( gAudiop && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingMusic")) +void LLOverlayBar::enableMediaButtons() +{ + if (mMediaRemote) { - play_enabled = TRUE; - S32 is_playing = gAudiop->isInternetStreamPlaying(); - if (is_playing == 1) + // Music + LLParcel* parcel = gParcelMgr->getAgentParcel(); + if (parcel + && gAudiop + && !parcel->getMusicURL().empty() + && gSavedSettings.getBOOL("AudioStreamingMusic")) { - play_visible = FALSE; - pause_visible = TRUE; - stop_enabled = TRUE; + mMediaRemote->childSetEnabled("music_play", TRUE); } - else if (is_playing == 2) + else { - play_visible = TRUE; - pause_visible = FALSE; - stop_enabled = TRUE; + mMediaRemote->childSetEnabled("music_play", FALSE); } - } - panel->childSetEnabled("music_play", play_enabled); - panel->childSetEnabled("music_pause", play_enabled); - panel->childSetVisible("music_play", play_visible); - panel->childSetVisible("music_pause", pause_visible); - panel->childSetEnabled("music_stop", stop_enabled); -} -//static -void LLOverlayBar::enableMediaButtons(LLPanel* panel) -{ - // Media - BOOL play_enabled = FALSE; - BOOL play_visible = TRUE; - BOOL pause_visible = FALSE; - BOOL stop_enabled = FALSE; - - if ( LLMediaEngine::getInstance() && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingVideo") ) - { - play_enabled = TRUE; - if (LLMediaEngine::getInstance()->getMediaRenderer()) + // Media + // if there is a url and a texture and media is enabled and available and media streaming is on... (phew!) + if (LLMediaEngine::getInstance() + && LLMediaEngine::getInstance()->getUrl ().length () + && LLMediaEngine::getInstance()->getImageUUID ().notNull () + && LLMediaEngine::getInstance()->isEnabled () + && LLMediaEngine::getInstance()->isAvailable () + && gSavedSettings.getBOOL ( "AudioStreamingVideo" ) ) { - if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPlaying() || - LLMediaEngine::getInstance()->getMediaRenderer()->isLooping() ) - { - play_visible = FALSE; - pause_visible = TRUE; - stop_enabled = TRUE; - } - else if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPaused() ) - { - play_visible = TRUE; - pause_visible = FALSE; - stop_enabled = TRUE; - } + mMediaRemote->childSetEnabled("media_play", TRUE); + } + else + { + mMediaRemote->childSetEnabled("media_play", FALSE); } } - panel->childSetEnabled("media_play", play_enabled); - panel->childSetEnabled("media_pause", play_enabled); - panel->childSetVisible("media_play", play_visible); - panel->childSetVisible("media_pause", pause_visible); - panel->childSetEnabled("media_stop", stop_enabled); } -void LLOverlayBar::toggleAudioVolumeFloater(void* user_data) -{ - LLFloaterAudioVolume::toggleInstance(LLSD()); -} diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h index 229346340a..18ff07e71b 100644 --- a/indra/newview/lloverlaybar.h +++ b/indra/newview/lloverlaybar.h @@ -54,7 +54,7 @@ class LLOverlayBar : public LLPanel { public: - LLOverlayBar(const std::string& name, const LLRect& rect ); + LLOverlayBar(); ~LLOverlayBar(); virtual EWidgetType getWidgetType() const; @@ -63,6 +63,7 @@ public: /*virtual*/ void refresh(); /*virtual*/ void draw(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); + /*virtual*/ BOOL postBuild(); void layoutButtons(); @@ -79,27 +80,16 @@ public: //static media helper functions static void mediaPlay(void*); - static void mediaPause(void*); - static void mediaStop(void*); - static void musicPlay(void*); - static void musicPause(void*); - static void musicStop(void*); - static void toggleAudioVolumeFloater(void*); - - static void enableMediaButtons(LLPanel* panel); - static void enableMusicButtons(LLPanel* panel); - protected: - static void* createMasterRemote(void* userdata); - static void* createMusicRemote(void* userdata); static void* createMediaRemote(void* userdata); static void* createVoiceRemote(void* userdata); + static void* createChatBar(void* userdata); + + void enableMediaButtons(); protected: - LLMediaRemoteCtrl* mMasterRemote; - LLMediaRemoteCtrl* mMusicRemote; LLMediaRemoteCtrl* mMediaRemote; LLVoiceRemoteCtrl* mVoiceRemote; bool mBuilt; // dialog constructed yet? diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index c090fd9749..46af3ec74f 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -1638,7 +1638,7 @@ void LLPanelAvatar::onClickMute(void *userdata) if (name_edit) { std::string agent_name = name_edit->getText(); - gFloaterMute->show(); + LLFloaterMute::showInstance(); if (gMuteListp->isMuted(agent_id)) { diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 7163a71bc3..7263b11d96 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -189,14 +189,14 @@ BOOL LLPanelClassified::postBuild() mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "given_name_editor"); mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN); mNameEditor->setCommitOnFocusLost(TRUE); - mNameEditor->setFocusReceivedCallback(onFocusReceived); + mNameEditor->setFocusReceivedCallback(onFocusReceived, this); mNameEditor->setCommitCallback(onCommitAny); mNameEditor->setCallbackUserData(this); mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII ); mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor"); mDescEditor->setCommitOnFocusLost(TRUE); - mDescEditor->setFocusReceivedCallback(onFocusReceived); + mDescEditor->setFocusReceivedCallback(onFocusReceived, this); mDescEditor->setCommitCallback(onCommitAny); mDescEditor->setCallbackUserData(this); mDescEditor->setTabToNextField(TRUE); @@ -929,10 +929,10 @@ void LLPanelClassified::onCommitAny(LLUICtrl* ctrl, void* data) } // static -void LLPanelClassified::onFocusReceived(LLUICtrl* ctrl, void* data) +void LLPanelClassified::onFocusReceived(LLFocusableElement* ctrl, void* data) { // allow the data to be saved - onCommitAny(ctrl, data); + onCommitAny((LLUICtrl*)ctrl, data); } diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index a2bb29b224..8884d1a25e 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -111,7 +111,7 @@ protected: static void onClickProfile(void* data); static void onClickSet(void* data); - static void onFocusReceived(LLUICtrl* ctrl, void* data); + static void onFocusReceived(LLFocusableElement* ctrl, void* data); static void onCommitAny(LLUICtrl* ctrl, void* data); void sendClassifiedClickMessage(const char* type); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 8dbe72fdfe..8ce2ae5d9c 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -870,7 +870,7 @@ void LLPanelFace::onClickApply(void* userdata) { LLPanelFace* self = (LLPanelFace*) userdata; - gFocusMgr.setKeyboardFocus( NULL, NULL ); + gFocusMgr.setKeyboardFocus( NULL ); //F32 repeats_per_meter = self->mCtrlRepeatsPerMeter->get(); F32 repeats_per_meter = (F32)self->childGetValue( "rptctrl" ).asReal();//self->mCtrlRepeatsPerMeter->get(); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 3ad65b5e68..31688256f5 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -114,8 +114,8 @@ BOOL LLPanelGroupGeneral::postBuild() if(mEditCharter) { mEditCharter->setCommitCallback(onCommitAny); - mEditCharter->setFocusReceivedCallback(onCommitAny); - mEditCharter->setFocusChangedCallback(onCommitAny); + mEditCharter->setFocusReceivedCallback(onFocusEdit, this); + mEditCharter->setFocusChangedCallback(onFocusEdit, this); mEditCharter->setCallbackUserData(this); } @@ -259,6 +259,14 @@ BOOL LLPanelGroupGeneral::postBuild() } // static +void LLPanelGroupGeneral::onFocusEdit(LLFocusableElement* ctrl, void* data) +{ + LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; + self->mChanged = TRUE; + self->notifyObservers(); +} + +// static void LLPanelGroupGeneral::onCommitAny(LLUICtrl* ctrl, void* data) { LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index e7a27b6c0b..b17a66d0a3 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -66,6 +66,7 @@ public: virtual void draw(); private: + static void onFocusEdit(LLFocusableElement* ctrl, void* data); static void onCommitAny(LLUICtrl* ctrl, void* data); static void onCommitUserOnly(LLUICtrl* ctrl, void* data); static void onCommitTitle(LLUICtrl* ctrl, void* data); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index 0152990798..dbe24d40e0 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -238,7 +238,7 @@ void LLPanelGroupLandMoney::impl::onMapButton() F32 global_x = 0.f; F32 global_y = 0.f; - sscanf(cellp->getText().c_str(), "%f %f", &global_x, &global_y); + sscanf(cellp->getValue().asString().c_str(), "%f %f", &global_x, &global_y); // Hack: Use the agent's z-height F64 global_z = gAgent.getPositionGlobal().mdV[VZ]; diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index ce824cbb6d..a67692afa6 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -790,7 +790,6 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, { row["columns"][column_index]["column"] = "checkbox"; row["columns"][column_index]["type"] = "checkbox"; - row["columns"][column_index]["value"] = (*ra_it)->mName; check_box_index = column_index; ++column_index; } @@ -1058,7 +1057,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() if (mi == gdatap->mMembers.end()) continue; LLGroupMemberData* member_data = (*mi).second; // Is the member an owner? - if ( member_data->isInRole(gdatap->mOwnerRole) ) + if ( member_data && member_data->isInRole(gdatap->mOwnerRole) ) { // Can't remove other owners. cb_enable = FALSE; @@ -1870,7 +1869,7 @@ BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root) mRoleDescription->setCommitOnFocusLost(TRUE); mRoleDescription->setCallbackUserData(this); mRoleDescription->setCommitCallback(onDescriptionCommit); - mRoleDescription->setFocusReceivedCallback(onDescriptionCommit); + mRoleDescription->setFocusReceivedCallback(onDescriptionFocus, this); setFooterEnabled(FALSE); @@ -2329,6 +2328,16 @@ void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_dat } // static +void LLPanelGroupRolesSubTab::onDescriptionFocus(LLFocusableElement* ctrl, void* user_data) +{ + LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data); + if (!self) return; + + self->mHasRoleChange = TRUE; + self->notifyObservers(); +} + +// static void LLPanelGroupRolesSubTab::onDescriptionCommit(LLUICtrl* ctrl, void* user_data) { LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data); diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index b0b635eb57..13d64d4edb 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -256,6 +256,7 @@ public: static void onPropertiesKey(LLLineEditor*, void*); static void onDescriptionCommit(LLUICtrl*, void*); + static void onDescriptionFocus(LLFocusableElement*, void*); static void onMemberVisibilityChange(LLUICtrl*, void*); void handleMemberVisibilityChange(bool value); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index bc297354f7..74936dce0c 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -496,7 +496,7 @@ void LLPanelLogin::show(const LLRect &rect, void (*callback)(S32 option, void* user_data), void* callback_data) { - new LLPanelLogin(rect, show_server, callback, callback_data); + new LLPanelLogin(rect, show_server, callback, callback_data); LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(sInstance, "login_html"); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index c3d949d3df..2463e62d7f 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -350,7 +350,7 @@ void LLPanelObject::getState( ) //forfeit focus if (gFocusMgr.childHasKeyboardFocus(this)) { - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } // Disable all text input fields diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index c288c6aaed..e7b5e79377 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -171,7 +171,7 @@ void LLPanelVolume::getState( ) //forfeit focus if (gFocusMgr.childHasKeyboardFocus(this)) { - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } // Disable all text input fields diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 9fa364a339..61bda9dfc0 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -1607,7 +1607,7 @@ void LLPreviewGesture::onClickAdd(void* data) if (!library_item) return; const LLScrollListCell* library_cell = library_item->getColumn(0); - const std::string& library_text = library_cell->getText(); + const std::string& library_text = library_cell->getValue().asString(); self->addStep(library_text); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 6c377009f2..06286927d4 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -960,7 +960,7 @@ void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) S32 row = 0; S32 column = 0; const LLScrollListCell* cell = item->getColumn(0); - LLString line(cell->getText()); + LLString line(cell->getValue().asString()); line.erase(0, 1); LLString::replaceChar(line, ',',' '); LLString::replaceChar(line, ')',' '); diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp index fae04871be..b92a6d58c8 100644 --- a/indra/newview/llpreviewsound.cpp +++ b/indra/newview/llpreviewsound.cpp @@ -106,7 +106,7 @@ void LLPreviewSound::auditionSound( void *userdata ) if(item && gAudiop) { LLVector3d lpos_global = gAgent.getPositionGlobal(); - F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX"); gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), volume, lpos_global); } } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index f12f5763ef..b0b9fd33d1 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2304,7 +2304,7 @@ BOOL idle_startup() audio_update_volume(); // reset keyboard focus to sane state of pointing at world - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); #if 0 // sjb: enable for auto-enabling timer display gDebugView->mFastTimerView->setVisible(TRUE); diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 4d49d33184..8c4fc833db 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -100,7 +100,6 @@ const LLColor4 SIM_WARN_COLOR(1.f, 1.f, 0.f, 1.f); const LLColor4 SIM_FULL_COLOR(1.f, 0.f, 0.f, 1.f); const F32 ICON_TIMER_EXPIRY = 3.f; // How long the balance and health icons should flash after a change. const F32 ICON_FLASH_FREQUENCY = 2.f; -const S32 GRAPHIC_FUDGE = 4; const S32 TEXT_HEIGHT = 18; static void onClickParcelInfo(void*); @@ -153,13 +152,13 @@ LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect) childSetAction("scriptout", onClickScriptDebug, this); childSetAction("health", onClickHealth, this); - childSetAction("fly", onClickFly, this); + childSetAction("no_fly", onClickFly, this); childSetAction("buyland", onClickBuyLand, this ); childSetAction("buycurrency", onClickBuyCurrency, this ); - childSetAction("build", onClickBuild, this ); - childSetAction("scripts", onClickScripts, this ); + childSetAction("no_build", onClickBuild, this ); + childSetAction("no_scripts", onClickScripts, this ); childSetAction("restrictpush", onClickPush, this ); - childSetAction("status_voice", onClickVoice, this ); + childSetAction("status_no_voice", onClickVoice, this ); childSetCommitCallback("search_editor", onCommitSearch, this); childSetAction("search_btn", onClickSearch, this); @@ -304,7 +303,7 @@ void LLStatusBar::refresh() // Health childGetRect( "health", buttonRect ); - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); childSetRect("health", r); x += buttonRect.getWidth(); @@ -324,27 +323,32 @@ void LLStatusBar::refresh() (parcel && !parcel->getAllowFly()) ) { // No Fly Zone - childGetRect( "fly", buttonRect ); - childSetVisible( "fly", true ); - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "fly", r ); + childGetRect( "no_fly", buttonRect ); + childSetVisible( "no_fly", true ); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); + childSetRect( "no_fly", r ); x += buttonRect.getWidth(); } else { - childSetVisible("fly", false); + // Fly Zone + childSetVisible("no_fly", false); } BOOL no_build = parcel && !parcel->getAllowModify(); - childSetVisible("build", no_build); if (no_build) { - childGetRect( "build", buttonRect ); + childSetVisible("no_build", TRUE); + childGetRect( "no_build", buttonRect ); // No Build Zone - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "build", r ); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); + childSetRect( "no_build", r ); x += buttonRect.getWidth(); } + else + { + childSetVisible("no_build", FALSE); + } BOOL no_scripts = FALSE; if((region @@ -354,35 +358,56 @@ void LLStatusBar::refresh() { no_scripts = TRUE; } - childSetVisible("scripts", no_scripts); if (no_scripts) { // No scripts - childGetRect( "scripts", buttonRect ); - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "scripts", r ); + childSetVisible("no_scripts", TRUE); + childGetRect( "no_scripts", buttonRect ); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); + childSetRect( "no_scripts", r ); x += buttonRect.getWidth(); } + else + { + // Yes scripts + childSetVisible("no_scripts", FALSE); + } BOOL no_region_push = (region && region->getRestrictPushObject()); BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject()); - childSetVisible("restrictpush", no_push); if (no_push) { + childSetVisible("restrictpush", TRUE); childGetRect( "restrictpush", buttonRect ); - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); childSetRect( "restrictpush", r ); x += buttonRect.getWidth(); } + else + { + childSetVisible("restrictpush", FALSE); + } + BOOL voice_enabled = gVoiceClient->voiceEnabled(); BOOL have_voice = parcel && parcel->getVoiceEnabled(); - childSetVisible("status_voice", have_voice); - if (have_voice) + if (!voice_enabled) { - childGetRect( "status_voice", buttonRect ); - r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "status_voice", r ); - x += buttonRect.getWidth(); + childSetVisible("status_no_voice", FALSE); + } + else + { + if (have_voice) + { + childSetVisible("status_no_voice", FALSE); + } + else if (!have_voice) + { + childSetVisible("status_no_voice", TRUE); + childGetRect( "status_no_voice", buttonRect ); + r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); + childSetRect( "status_no_voice", r ); + x += buttonRect.getWidth(); + } } BOOL canBuyLand = parcel @@ -512,7 +537,7 @@ void LLStatusBar::refresh() mTextParcelName->setText(location_name); // Adjust region name and parcel name - x += 4; + x += 8; const S32 PARCEL_RIGHT = llmin(mTextTime->getRect().mLeft, mTextParcelName->getTextPixelWidth() + x + 5); r.set(x+4, mRect.getHeight() - 2, PARCEL_RIGHT, 0); @@ -672,8 +697,7 @@ static void onClickPush(void* ) static void onClickVoice(void* ) { - LLNotifyBox::showXml("VoiceAvailablity"); - //LLFirstUse::useVoice(); + LLNotifyBox::showXml("NoVoice"); } static void onClickBuild(void*) @@ -708,7 +732,7 @@ static void onClickBuyLand(void*) void LLStatusBar::setupDate() { // fill the day array with what's in the xui - LLString day_list = childGetText("StatBarDaysOfWeek"); + LLString day_list = getFormattedUIString("StatBarDaysOfWeek"); size_t length = day_list.size(); // quick input check @@ -732,7 +756,7 @@ void LLStatusBar::setupDate() } // fill the day array with what's in the xui - LLString month_list = childGetText( "StatBarMonthsOfYear" ); + LLString month_list = getFormattedUIString( "StatBarMonthsOfYear" ); length = month_list.size(); // quick input check diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 06a4ea097f..465a40c1ef 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -373,7 +373,6 @@ public: { S32 line_height = (S32)(LLFontGL::sMonospace->getLineHeight() + .5f); setRect(LLRect(0,0,100,line_height * 4)); - updateRect(); } virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEX_MEM_BAR; }; diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp index e7a5445ef6..c80edd0eab 100644 --- a/indra/newview/lltoolbar.cpp +++ b/indra/newview/lltoolbar.cpp @@ -60,6 +60,11 @@ #include "llvieweruictrlfactory.h" #include "llviewerwindow.h" #include "lltoolgrab.h" +#include "llcombobox.h" +#include "llfloaterchat.h" +#include "llfloatermute.h" +#include "llimpanel.h" +#include "llscrolllistctrl.h" #if LL_DARWIN @@ -100,24 +105,20 @@ F32 LLToolBar::sInventoryAutoOpenTime = 1.f; // Functions // -LLToolBar::LLToolBar(const std::string& name, const LLRect& r) -: LLPanel(name, r, BORDER_NO) +LLToolBar::LLToolBar() +: LLPanel() #if LL_DARWIN , mResizeHandle(NULL) #endif // LL_DARWIN { setIsChrome(TRUE); - setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); - - gUICtrlFactory->buildPanel(this, "panel_toolbar.xml"); - mIsFocusRoot = TRUE; - + setFocusRoot(TRUE); } BOOL LLToolBar::postBuild() { - childSetAction("communicate_btn", onClickCommunicate, this); + childSetCommitCallback("communicate_btn", onClickCommunicate, this); childSetControlName("communicate_btn", "ShowCommunicate"); childSetAction("chat_btn", onClickChat, this); @@ -270,29 +271,47 @@ void LLToolBar::layoutButtons() } #endif // LL_DARWIN + LLButton* chat_button = LLUICtrlFactory::getButtonByName(this, "chat_btn"); + if (chat_button) + { + width -= chat_button->getRect().getWidth() + pad; + } + // We actually want to extend "pad" pixels off the right edge of the // screen, such that the rightmost button is aligned. - F32 segment_width = (F32)(width + pad) / (F32)count; - S32 btn_width = lltrunc(segment_width - pad); + S32 segment_width = llround((F32)(width) / ((F32)count - 1.f)); + S32 btn_width = segment_width - pad; // Evenly space all views S32 height = -1; S32 i = count - 1; - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) + S32 x = pad; + for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin(); + child_iter != getChildList()->rend(); ++child_iter) { LLView *btn_view = *child_iter; - if(btn_view->getWidgetType() == WIDGET_TYPE_BUTTON) + if(btn_view->getWidgetType() == WIDGET_TYPE_BUTTON || btn_view->getWidgetType() == WIDGET_TYPE_FLYOUT_BUTTON) { if (height < 0) { height = btn_view->getRect().getHeight(); } - S32 x = llround(i*segment_width); - S32 y = 0; - LLRect r; - r.setOriginAndSize(x, y, btn_width, height); - btn_view->setRect(r); + + LLRect r; + + if (btn_view->getName() == "chat_btn") + { + r.setOriginAndSize(x, 0, btn_view->getRect().getWidth(), height); + x += btn_view->getRect().getWidth() + pad; + } + else + { + r.setOriginAndSize(x, 0, btn_width, height); + x += segment_width; + } + + btn_view->setOrigin(r.mLeft, r.mBottom); + btn_view->reshape(r.getWidth(), r.getHeight()); i--; } } @@ -321,6 +340,7 @@ void LLToolBar::refresh() childSetEnabled("build_btn", gParcelMgr->agentCanBuild() ); + // Check to see if we're in build mode BOOL build_mode = gToolMgr->inEdit(); // And not just clicking on a scripted object @@ -329,13 +349,135 @@ void LLToolBar::refresh() build_mode = FALSE; } gSavedSettings.setBOOL("BuildBtnState", build_mode); + + updateCommunicateList(); +} + +void LLToolBar::updateCommunicateList() +{ + LLFlyoutButton* communicate_button = (LLFlyoutButton*)getChildByName("communicate_btn", TRUE); + if (communicate_button) + { + LLSD selected = communicate_button->getValue(); + + communicate_button->removeall(); + + LLFloater* frontmost_floater = LLFloaterChatterBox::getInstance()->getActiveFloater(); + LLScrollListItem* itemp = NULL; + + itemp = communicate_button->add(LLFloaterMyFriends::getInstance()->getShortTitle(), LLSD("contacts"), ADD_TOP); + if (LLFloaterMyFriends::getInstance() == frontmost_floater) + { + ((LLScrollListText*)itemp->getColumn(0))->setFontStyle(LLFontGL::BOLD); + // make sure current tab is selected in list + if (selected.isUndefined()) + { + selected = itemp->getValue(); + } + } + itemp = communicate_button->add(LLFloaterChat::getInstance()->getShortTitle(), LLSD("local chat"), ADD_TOP); + if (LLFloaterChat::getInstance() == frontmost_floater) + { + ((LLScrollListText*)itemp->getColumn(0))->setFontStyle(LLFontGL::BOLD); + if (selected.isUndefined()) + { + selected = itemp->getValue(); + } + } + communicate_button->addSeparator(ADD_TOP); + communicate_button->add(getUIString("Redock Windows"), LLSD("redock"), ADD_TOP); + communicate_button->addSeparator(ADD_TOP); + communicate_button->add(LLFloaterMute::getInstance()->getShortTitle(), LLSD("mute list"), ADD_TOP); + + std::set<LLViewHandle>::const_iterator floater_handle_it; + + if (gIMMgr->getIMFloaterHandles().size() > 0) + { + communicate_button->addSeparator(ADD_TOP); + } + + for(floater_handle_it = gIMMgr->getIMFloaterHandles().begin(); floater_handle_it != gIMMgr->getIMFloaterHandles().end(); ++floater_handle_it) + { + LLFloaterIMPanel* im_floaterp = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*floater_handle_it); + if (im_floaterp) + { + LLString floater_title = im_floaterp->getNumUnreadMessages() > 0 ? "*" : ""; + floater_title.append(im_floaterp->getShortTitle()); + itemp = communicate_button->add(floater_title, im_floaterp->getSessionID(), ADD_TOP); + if (im_floaterp == frontmost_floater) + { + ((LLScrollListText*)itemp->getColumn(0))->setFontStyle(LLFontGL::BOLD); + if (selected.isUndefined()) + { + selected = itemp->getValue(); + } + } + } + } + + communicate_button->setToggleState(gSavedSettings.getBOOL("ShowCommunicate")); + communicate_button->setValue(selected); + } } // static -void LLToolBar::onClickCommunicate(void* user_data) +void LLToolBar::onClickCommunicate(LLUICtrl* ctrl, void* user_data) { - LLFloaterChatterBox::toggleInstance(LLSD()); + LLToolBar* toolbar = (LLToolBar*)user_data; + LLFlyoutButton* communicate_button = (LLFlyoutButton*)toolbar->getChildByName("communicate_btn", TRUE); + + LLSD selected_option = communicate_button->getValue(); + + if (selected_option.asString() == "contacts") + { + LLFloaterMyFriends::showInstance(); + } + else if (selected_option.asString() == "local chat") + { + LLFloaterChat::showInstance(); + } + else if (selected_option.asString() == "redock") + { + LLFloaterChatterBox::getInstance()->addFloater(LLFloaterMyFriends::getInstance(), FALSE); + LLFloaterChatterBox::getInstance()->addFloater(LLFloaterChat::getInstance(), FALSE); + LLUUID session_to_show; + + std::set<LLViewHandle>::const_iterator floater_handle_it; + for(floater_handle_it = gIMMgr->getIMFloaterHandles().begin(); floater_handle_it != gIMMgr->getIMFloaterHandles().end(); ++floater_handle_it) + { + LLFloater* im_floaterp = LLFloater::getFloaterByHandle(*floater_handle_it); + if (im_floaterp) + { + if (im_floaterp->isFrontmost()) + { + session_to_show = ((LLFloaterIMPanel*)im_floaterp)->getSessionID(); + } + LLFloaterChatterBox::getInstance()->addFloater(im_floaterp, FALSE); + } + } + + LLFloaterChatterBox::showInstance(session_to_show); + } + else if (selected_option.asString() == "mute list") + { + LLFloaterMute::showInstance(); + } + else if (selected_option.isUndefined()) // user just clicked the communicate button, treat as toggle + { + if (LLFloaterChatterBox::getInstance()->getFloaterCount() == 0) + { + LLFloaterMyFriends::toggleInstance(); + } + else + { + LLFloaterChatterBox::toggleInstance(); + } + } + else // otherwise selection_option is a specific IM session id + { + LLFloaterChatterBox::showInstance(selected_option); + } } diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h index c43c80b095..85adba8c55 100644 --- a/indra/newview/lltoolbar.h +++ b/indra/newview/lltoolbar.h @@ -47,7 +47,7 @@ class LLToolBar : public LLPanel { public: - LLToolBar(const std::string& name, const LLRect& rect ); + LLToolBar(); ~LLToolBar(); /*virtual*/ BOOL postBuild(); @@ -70,7 +70,7 @@ public: void refresh(); // callbacks - static void onClickCommunicate(void*); + static void onClickCommunicate(LLUICtrl*, void*); static void onClickChat(void* data); static void onClickAppearance(void* data); static void onClickClothing(void* data); @@ -86,8 +86,13 @@ public: static F32 sInventoryAutoOpenTime; private: + void updateCommunicateList(); + + +private: BOOL mInventoryAutoOpen; LLFrameTimer mInventoryAutoOpenTimer; + S32 mNumUnreadIMs; #if LL_DARWIN LLFakeResizeHandle *mResizeHandle; #endif // LL_DARWIN diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp index d26bdab921..a27053faa3 100644 --- a/indra/newview/lltoolplacer.cpp +++ b/indra/newview/lltoolplacer.cpp @@ -216,7 +216,7 @@ BOOL LLToolPlacer::addObject( LLPCode pcode, S32 x, S32 y, U8 use_physics ) // Play creation sound if (gAudiop) { - F32 volume = gSavedSettings.getF32("AudioLevelUI"); + F32 volume = gSavedSettings.getBOOL("MuteUI") ? 0.f : gSavedSettings.getF32("AudioLevelUI"); gAudiop->triggerSound( LLUUID(gSavedSettings.getString("UISndObjectCreate")), gAgent.getID(), volume); } diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index ac90a06a57..8e742bd655 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -122,8 +122,10 @@ void audio_update_volume(bool force_update) if (gAudiop) { F32 music_volume = gSavedSettings.getF32("AudioLevelMusic"); + BOOL music_muted = gSavedSettings.getBOOL("MuteMusic"); music_volume = mute_volume * master_volume * (music_volume*music_volume); - gAudiop->setInternetStreamGain ( music_volume ); + gAudiop->setInternetStreamGain ( music_muted ? 0.f : music_volume ); + } // Streaming Media @@ -131,7 +133,8 @@ void audio_update_volume(bool force_update) { F32 media_volume = gSavedSettings.getF32("AudioLevelMedia"); media_volume = mute_volume * master_volume * (media_volume*media_volume); - LLMediaEngine::getInstance()->setVolume(media_volume); + BOOL media_muted = gSavedSettings.getBOOL("MuteMedia"); + LLMediaEngine::getInstance()->setVolume(media_muted ? 0.f : media_volume); } // Voice @@ -139,8 +142,9 @@ void audio_update_volume(bool force_update) { F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice"); voice_volume = mute_volume * master_volume * voice_volume; - gVoiceClient->setVoiceVolume(voice_volume); - gVoiceClient->setMicGain(gSavedSettings.getF32("AudioLevelMic")); + BOOL voice_mute = gSavedSettings.getBOOL("MuteVoice"); + gVoiceClient->setVoiceVolume(voice_mute ? 0.f : voice_volume); + gVoiceClient->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic")); if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) { @@ -206,7 +210,7 @@ void audio_update_wind(bool force_update) // don't use the setter setMaxWindGain() because we don't // want to screw up the fade-in on startup by setting actual source gain // outside the fade-in. - gAudiop->mMaxWindGain = gSavedSettings.getF32("AudioLevelAmbient"); + gAudiop->mMaxWindGain = gSavedSettings.getBOOL("MuteAmbient") ? 0.f : gSavedSettings.getF32("AudioLevelAmbient"); last_camera_water_height = camera_water_height; gAudiop->updateWind(gRelativeWindVec, camera_water_height); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2fccdcd153..fdc0047f95 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2103,7 +2103,7 @@ class LLObjectMute : public view_listener_t else { gMuteListp->add(mute); - gFloaterMute->show(); + LLFloaterMute::showInstance(); } return true; @@ -5309,7 +5309,7 @@ class LLShowFloater : public view_listener_t } else if (floater_name == "mute list") { - LLFloaterMute::toggle(NULL); + LLFloaterMute::toggleInstance(); } else if (floater_name == "camera controls") { @@ -5355,7 +5355,7 @@ class LLShowFloater : public view_listener_t } else if (floater_name == "about region") { - LLFloaterRegionInfo::show((void *)NULL); + LLFloaterRegionInfo::showInstance(); } else if (floater_name == "grid options") { @@ -5447,7 +5447,7 @@ class LLFloaterVisible : public view_listener_t } else if (floater_name == "mute list") { - new_value = LLFloaterMute::visible(NULL); + new_value = LLFloaterMute::instanceVisible(); } else if (floater_name == "camera controls") { @@ -6963,13 +6963,12 @@ void handle_grab_texture(void* data) if(view) { LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); - LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback(); view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO); view->getPanel()->openSelected(); //LLInventoryView::dumpSelectionInformation((void*)view); // restore keyboard focus - gFocusMgr.setKeyboardFocus(focus_ctrl, callback); + gFocusMgr.setKeyboardFocus(focus_ctrl); } } else diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index ebb0da016f..8a4cd16c08 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -888,10 +888,8 @@ void open_offer(const std::vector<LLUUID>& items, const std::string& from_name) //highlight item LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); - LLFocusMgr::FocusLostCallback callback; - callback = gFocusMgr.getFocusCallback(); view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); - gFocusMgr.setKeyboardFocus(focus_ctrl, callback); + gFocusMgr.setKeyboardFocus(focus_ctrl); } } @@ -920,7 +918,7 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id, LLMute mute(blocked_id, from_name, type); if (gMuteListp->add(mute)) { - gFloaterMute->show(); + LLFloaterMute::showInstance(); gFloaterMute->selectMute(blocked_id); } @@ -2192,9 +2190,13 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) BOOL is_linden = FALSE; if (gMuteListp) { - is_muted = gMuteListp->isMuted(from_id, from_name, LLMute::flagTextChat) - || gMuteListp->isMuted(owner_id, LLMute::flagTextChat); - is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && gMuteListp->isLinden(from_name); + is_muted = gMuteListp->isMuted( + from_id, + from_name, + LLMute::flagTextChat) + || gMuteListp->isMuted(owner_id, LLMute::flagTextChat); + is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && + gMuteListp->isLinden(from_name); } BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); @@ -2235,7 +2237,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) BOOL visible_in_chat_bubble = FALSE; std::string verb; - color.setVec(1,1,1,1); + color.setVec(1.f,1.f,1.f,1.f); msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, DB_CHAT_MSG_BUF_SIZE, mesg); BOOL ircstyle = FALSE; @@ -3332,7 +3334,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **) return; } - F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : (gain * gSavedSettings.getF32("AudioLevelSFX")); gAudiop->triggerSound(sound_id, owner_id, volume, pos_global); } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 4c6f27944f..9243263794 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2717,7 +2717,7 @@ BOOL LLViewerObject::updateLOD() // Update volume of looping sounds if (mAudioSourcep && mAudioSourcep->isLoop()) { - F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : (mAudioGain * gSavedSettings.getF32("AudioLevelSFX")); mAudioSourcep->setGain(volume); } return FALSE; @@ -4228,7 +4228,7 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow { BOOL queue = flags & LL_SOUND_FLAG_QUEUE; mAudioGain = gain; - F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : gain * gSavedSettings.getF32("AudioLevelSFX"); mAudioSourcep->setGain(volume); mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP); mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER); @@ -4267,7 +4267,7 @@ void LLViewerObject::adjustAudioGain(const F32 gain) if (mAudioSourcep) { mAudioGain = gain; - F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : mAudioGain * gSavedSettings.getF32("AudioLevelSFX"); mAudioSourcep->setGain(volume); } } diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index b0d1d3daca..0ffa37525f 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -266,23 +266,6 @@ void LLViewerParcelMgr::getDisplayInfo(S32* area_out, S32* claim_out, *dwell_out = dwell; } -void LLViewerParcelMgr::getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean) -{ - if (mSelected && mCurrentParcel) - { - sw_max = mCurrentParcel->getSimWideMaxPrimCapacity(); - sw_total = mCurrentParcel->getSimWidePrimCount(); - max = llround(mCurrentParcel->getMaxPrimCapacity()*mCurrentParcel->getParcelPrimBonus()); - total = mCurrentParcel->getPrimCount(); - owner = mCurrentParcel->getOwnerPrimCount(); - group = mCurrentParcel->getGroupPrimCount(); - other = mCurrentParcel->getOtherPrimCount(); - selected = mCurrentParcel->getSelectedPrimCount(); - parcel_object_bonus = mCurrentParcel->getParcelPrimBonus(); - other_clean = mCurrentParcel->getCleanOtherTime(); - } -} - S32 LLViewerParcelMgr::getSelectedArea() const { S32 rv = 0; diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 2dd4f28169..640c8c5c57 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -134,8 +134,6 @@ public: void getDisplayInfo(S32* area, S32* claim, S32* rent, BOOL* for_sale, F32* dwell); - void getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean); - // Returns selected area S32 getSelectedArea() const; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index ad4b6fd616..6001cd3e58 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -366,6 +366,11 @@ F32 LLViewerRegion::getWaterHeight() const return mLandp->getWaterHeight(); } +BOOL LLViewerRegion::isVoiceEnabled() const +{ + return (getRegionFlags() & REGION_FLAGS_ALLOW_VOICE); +} + void LLViewerRegion::setRegionFlags(U32 flags) { mRegionFlags = flags; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index a0953e561e..e42c0015df 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -109,6 +109,8 @@ public: void setWaterHeight(F32 water_level); F32 getWaterHeight() const; + BOOL isVoiceEnabled() const; + void setBillableFactor(F32 billable_factor) { mBillableFactor = billable_factor; } F32 getBillableFactor() const { return mBillableFactor; } diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 81de1eb9a8..18f07efb44 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -605,54 +605,50 @@ void LLViewerTextEditor::makePristine() BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) { - if (pointInView(x, y) && getVisible()) + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) { - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) + LLView *viewp = *child_iter; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) { - LLView *viewp = *child_iter; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) - { - return TRUE; - } + return TRUE; } + } + + if( mSegments.empty() ) + { + return TRUE; + } - if( mSegments.empty() ) + LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); + if( cur_segment ) + { + BOOL has_tool_tip = FALSE; + if( cur_segment->getStyle().getIsEmbeddedItem() ) { - return TRUE; + LLWString wtip; + has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip); + msg = wstring_to_utf8str(wtip); } - - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) + else { - BOOL has_tool_tip = FALSE; - if( cur_segment->getStyle().getIsEmbeddedItem() ) - { - LLWString wtip; - has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip); - msg = wstring_to_utf8str(wtip); - } - else - { - has_tool_tip = cur_segment->getToolTip( msg ); - } - if( has_tool_tip ) - { - // Just use a slop area around the cursor - // Convert rect local to screen coordinates - S32 SLOP = 8; - localPointToScreen( - x - SLOP, y - SLOP, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; - sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; - } + has_tool_tip = cur_segment->getToolTip( msg ); + } + if( has_tool_tip ) + { + // Just use a slop area around the cursor + // Convert rect local to screen coordinates + S32 SLOP = 8; + localPointToScreen( + x - SLOP, y - SLOP, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; + sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; } - return TRUE; } - return FALSE; + return TRUE; } BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) @@ -759,9 +755,9 @@ BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) handled = TRUE; } - if (mTakesFocus) + if (hasTabStop()) { - setFocus( TRUE ); + setFocus(TRUE); handled = TRUE; } @@ -1016,11 +1012,7 @@ BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) } } - if (mTakesFocus) - { - setFocus( TRUE ); - } - + setCursorAtLocalPos( x, y, FALSE ); deselect(); @@ -1390,7 +1382,7 @@ void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item ) const F32 SOUND_GAIN = 1.0f; if(gAudiop) { - F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : (SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX")); gAudiop->triggerSound(item->getAssetUUID(), gAgentID, volume, lpos_global); } showCopyToInvDialog( item ); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index a3611b2272..4c96083337 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -266,6 +266,8 @@ const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1; const F32 MIN_DISPLAY_SCALE = 0.85f; +const S32 CONSOLE_BOTTOM_PAD = 20; + #ifdef SABINRIG /// ALL RIG STUFF bool rigControl = false; @@ -663,18 +665,17 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask // Topmost view gets a chance before the hierarchy LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - BOOL mouse_over_top_ctrl = FALSE; if (top_ctrl) { S32 local_x, local_y; top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); if (top_ctrl->pointInView(local_x, local_y)) { - mouse_over_top_ctrl = TRUE; - if(top_ctrl->handleMouseDown(local_x, local_y, mask)) - { - return TRUE; - } + return top_ctrl->handleMouseDown(local_x, local_y, mask); + } + else + { + setTopCtrl(NULL); } } @@ -686,11 +687,6 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl; LLView::sMouseHandlerMessage = ""; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } return TRUE; } else if (LLView::sDebugMouseHandling) @@ -698,12 +694,6 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask llinfos << "Left Mouse Down not handled by view" << llendl; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } - if (gDisconnected) { return FALSE; @@ -716,7 +706,7 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask // This is necessary to force clicks in the world to cause edit // boxes that might have keyboard focus to relinquish it, and hence // cause a commit to update their value. JC - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); return TRUE; } } @@ -760,18 +750,17 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // Check for hit on UI. LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - BOOL mouse_over_top_ctrl = FALSE; if (top_ctrl) { S32 local_x, local_y; top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); if (top_ctrl->pointInView(local_x, local_y)) { - mouse_over_top_ctrl = TRUE; - if(top_ctrl->handleDoubleClick(local_x, local_y, mask)) - { - return TRUE; - } + return top_ctrl->handleDoubleClick(local_x, local_y, mask); + } + else + { + setTopCtrl(NULL); } } @@ -782,11 +771,6 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl; LLView::sMouseHandlerMessage = ""; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } return TRUE; } else if (LLView::sDebugMouseHandling) @@ -794,12 +778,6 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma llinfos << "Left Mouse Down not handled by view" << llendl; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } - // Why is this here? JC 9/3/2002 if (gNoRender) { @@ -970,18 +948,17 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK } LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - BOOL mouse_over_top_ctrl = FALSE; if (top_ctrl) { S32 local_x, local_y; top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); if (top_ctrl->pointInView(local_x, local_y)) { - mouse_over_top_ctrl = TRUE; - if(top_ctrl->handleRightMouseDown(local_x, local_y, mask)) - { - return TRUE; - } + return top_ctrl->handleRightMouseDown(local_x, local_y, mask); + } + else + { + setTopCtrl(NULL); } } @@ -992,11 +969,6 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK llinfos << "Right Mouse Down" << LLView::sMouseHandlerMessage << llendl; LLView::sMouseHandlerMessage = ""; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } return TRUE; } else if (LLView::sDebugMouseHandling) @@ -1004,12 +976,6 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK llinfos << "Right Mouse Down not handled by view" << llendl; } - if (top_ctrl && top_ctrl->hasFocus() && !mouse_over_top_ctrl) - { - // always defocus top view if we click off of it - top_ctrl->setFocus(FALSE); - } - if (gToolMgr) { if(gToolMgr->getCurrentTool()->handleRightMouseDown( x, y, mask ) ) @@ -1017,7 +983,7 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK // This is necessary to force clicks in the world to cause edit // boxes that might have keyboard focus to relinquish it, and hence // cause a commit to update their value. JC - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); return TRUE; } } @@ -1266,7 +1232,7 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) // JC - Leave keyboard focus, so if you're popping in and out editing // a script, you don't have to click in the editor again and again. - // gFocusMgr.setKeyboardFocus( NULL, NULL ); + // gFocusMgr.setKeyboardFocus( NULL ); gShowTextEditCursor = FALSE; // If losing focus while keys are down, reset them. @@ -1746,14 +1712,6 @@ void LLViewerWindow::initBase() gDebugView->setVisible(TRUE); mRootView->addChild(gDebugView); - // HUD elements just below floaters - LLRect hud_rect = full_window; - hud_rect.mTop -= 24; - hud_rect.mBottom += STATUS_BAR_HEIGHT; - gHUDView = new LLHUDView("hud_view", hud_rect); - gHUDView->setFollowsAll(); - mRootView->addChild(gHUDView); - // Add floater view at the end so it will be on top, and give it tab priority over others mRootView->addChild(gFloaterView, -1); mRootView->addChild(gSnapshotFloaterView); @@ -1871,27 +1829,10 @@ void LLViewerWindow::initWorldUI() S32 width = mRootView->getRect().getWidth(); LLRect full_window(0, height, width, 0); - if ( gToolBar == NULL ) // Don't re-enter if objects are alreay created + if ( gBottomPanel == NULL ) // Don't re-enter if objects are alreay created { - LLRect bar_rect(-1, STATUS_BAR_HEIGHT, width+1, -1); - gToolBar = new LLToolBar("toolbar", bar_rect); - - LLRect chat_bar_rect(-1,CHAT_BAR_HEIGHT, width+1, -1); - chat_bar_rect.translate(0, STATUS_BAR_HEIGHT-1); - gChatBar = new LLChatBar("chat", chat_bar_rect); - - bar_rect.translate(0, STATUS_BAR_HEIGHT-1); - bar_rect.translate(0, CHAT_BAR_HEIGHT-1); - gOverlayBar = new LLOverlayBar("overlay", bar_rect); - // panel containing chatbar, toolbar, and overlay, over floaters - LLRect bottom_rect(-1, 2*STATUS_BAR_HEIGHT + CHAT_BAR_HEIGHT, width+1, -1); - gBottomPanel = new LLBottomPanel("bottom panel", bottom_rect); - - // the order here is important - gBottomPanel->addChild(gChatBar); - gBottomPanel->addChild(gToolBar); - gBottomPanel->addChild(gOverlayBar); + gBottomPanel = new LLBottomPanel(mRootView->getRect()); mRootView->addChild(gBottomPanel); // View for hover information @@ -1933,8 +1874,7 @@ void LLViewerWindow::initWorldUI() mRootView->addChild(gMorphView); gMorphView->setVisible(FALSE); - gFloaterMute = new LLFloaterMute(); - gFloaterMute->setVisible(FALSE); + gFloaterMute = LLFloaterMute::getInstance(); LLWorldMapView::initClass(); @@ -2450,6 +2390,12 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if(LLMenuGL::getKeyboardMode()) LLMenuGL::setKeyboardMode(FALSE); + if (gFocusMgr.getTopCtrl()) + { + gFocusMgr.setTopCtrl(NULL); + return TRUE; + } + // *TODO: get this to play well with mouselook and hidden // cursor modes, etc, and re-enable. //if (gFocusMgr.getMouseCapture()) @@ -2979,23 +2925,9 @@ BOOL LLViewerWindow::handlePerFrameHover() } // Update rectangles for the various toolbars - if (gToolBar && gChatBar && gOverlayBar && gNotifyBoxView && gConsole) + if (gOverlayBar && gNotifyBoxView && gConsole) { LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1); - if (gToolBar->getVisible()) - { - gToolBar->setRect(bar_rect); - bar_rect.translate(0, STATUS_BAR_HEIGHT-1); - } - - if (gChatBar->getVisible()) - { - // fix up the height - LLRect chat_bar_rect = bar_rect; - chat_bar_rect.mTop = chat_bar_rect.mBottom + CHAT_BAR_HEIGHT + 1; - gChatBar->setRect(chat_bar_rect); - bar_rect.translate(0, CHAT_BAR_HEIGHT-1); - } LLRect notify_box_rect = gNotifyBoxView->getRect(); notify_box_rect.mBottom = bar_rect.mBottom; @@ -3015,42 +2947,16 @@ BOOL LLViewerWindow::handlePerFrameHover() if (gOverlayBar->getVisible()) { - LLRect overlay_rect = bar_rect; - overlay_rect.mTop = overlay_rect.mBottom + OVERLAY_BAR_HEIGHT; - - // Fitt's Law: Push buttons flush with bottom of screen if - // nothing else visible. - if (!gToolBar->getVisible() - && !gChatBar->getVisible()) - { - // *NOTE: this is highly depenent on the XML - // describing the position of the buttons - overlay_rect.translate(0, 0); - } - - gOverlayBar->setRect(overlay_rect); - gOverlayBar->updateRect(); - bar_rect.translate(0, gOverlayBar->getRect().getHeight()); - - gFloaterView->setSnapOffsetBottom(OVERLAY_BAR_HEIGHT); + gFloaterView->setSnapOffsetBottom(gHUDView->getRect().mBottom); } else { gFloaterView->setSnapOffsetBottom(0); } - // fix rectangle of bottom panel focus indicator - if(gBottomPanel && gBottomPanel->getFocusIndicator()) - { - LLRect focus_rect = gBottomPanel->getFocusIndicator()->getRect(); - focus_rect.mTop = (gToolBar->getVisible() ? STATUS_BAR_HEIGHT : 0) + - (gChatBar->getVisible() ? CHAT_BAR_HEIGHT : 0) - 2; - gBottomPanel->getFocusIndicator()->setRect(focus_rect); - } - // Always update console LLRect console_rect = gConsole->getRect(); - console_rect.mBottom = bar_rect.mBottom + 8; + console_rect.mBottom = gHUDView->getRect().mBottom + CONSOLE_BOTTOM_PAD; gConsole->reshape(console_rect.getWidth(), console_rect.getHeight()); gConsole->setRect(console_rect); } @@ -3628,13 +3534,6 @@ void LLViewerWindow::performPick() // if you are the parent parent = objectp; } - std::vector<LLPointer<LLViewerObject>,std::allocator<LLPointer<LLViewerObject> > > children = parent->getChildren(); - for( std::vector<LLPointer<LLViewerObject>,std::allocator<LLPointer<LLViewerObject> > >::iterator i= children.begin(); i!= children.end(); ++i ) - { - //go through - LLViewerObject* foo = *i; - foo->getRotation(); - } if (objectp->mbCanSelect) { te_offset = (te_offset == 16) ? NO_FACE : te_offset; @@ -4561,9 +4460,9 @@ void LLViewerWindow::drawMouselookInstructions() // These functions are here only because LLViewerWindow used to do the work that gFocusMgr does now. // They let other objects continue to work without change. -void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus)) +void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus) { - gFocusMgr.setKeyboardFocus( new_focus, on_focus_lost ); + gFocusMgr.setKeyboardFocus( new_focus ); } LLUICtrl* LLViewerWindow::getKeyboardFocus() @@ -5033,7 +4932,7 @@ BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, } mIgnoreActivate = FALSE; - gFocusMgr.setKeyboardFocus(keyboard_focus, NULL); + gFocusMgr.setKeyboardFocus(keyboard_focus); mWantFullscreen = mWindow->getFullscreen(); mShowFullscreenProgress = FALSE; @@ -5233,16 +5132,22 @@ LLAlertDialog* LLViewerWindow::alertXmlEditText(const std::string& xml_filename, //////////////////////////////////////////////////////////////////////////// -LLBottomPanel::LLBottomPanel(const LLString &name, const LLRect &rect) : - LLPanel(name, rect, FALSE), +LLBottomPanel::LLBottomPanel(const LLRect &rect) : + LLPanel("", rect, FALSE), mIndicator(NULL) { // bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay setFocusRoot(TRUE); - // don't capture mouse clicks that don't hit a child - setMouseOpaque(FALSE); - setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); + // flag this panel as chrome so buttons don't grab keyboard focus setIsChrome(TRUE); + + mFactoryMap["toolbar"] = LLCallbackMap(createToolBar, NULL); + mFactoryMap["overlay"] = LLCallbackMap(createOverlayBar, NULL); + mFactoryMap["hud"] = LLCallbackMap(createHUD, NULL); + gUICtrlFactory->buildPanel(this, "panel_bars.xml", &getFactoryMap()); + + setOrigin(rect.mLeft, rect.mBottom); + reshape(rect.getWidth(), rect.getHeight()); } void LLBottomPanel::setFocusIndicator(LLView * indicator) @@ -5260,3 +5165,25 @@ void LLBottomPanel::draw() } LLPanel::draw(); } + +void* LLBottomPanel::createHUD(void* data) +{ + delete gHUDView; + gHUDView = new LLHUDView(); + return gHUDView; +} + + +void* LLBottomPanel::createOverlayBar(void* data) +{ + delete gOverlayBar; + gOverlayBar = new LLOverlayBar(); + return gOverlayBar; +} + +void* LLBottomPanel::createToolBar(void* data) +{ + delete gToolBar; + gToolBar = new LLToolBar(); + return gToolBar; +} diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index faab518879..36225cb7d3 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -185,7 +185,7 @@ public: // Handle the application becoming active (frontmost) or inactive //BOOL handleActivate(BOOL activate); - void setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus)); // new_focus = NULL to release the focus. + void setKeyboardFocus(LLUICtrl* new_focus); // new_focus = NULL to release the focus. LLUICtrl* getKeyboardFocus(); BOOL hasKeyboardFocus( const LLUICtrl* possible_focus ) const; BOOL childHasKeyboardFocus( const LLView* parent ) const; @@ -363,10 +363,15 @@ protected: class LLBottomPanel : public LLPanel { public: - LLBottomPanel(const LLString& name, const LLRect& rect); + LLBottomPanel(const LLRect& rect); void setFocusIndicator(LLView * indicator); LLView * getFocusIndicator() { return mIndicator; } /*virtual*/ void draw(); + + static void* createHUD(void* data); + static void* createOverlayBar(void* data); + static void* createToolBar(void* data); + protected: LLView * mIndicator; }; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index fc159ddc48..c6a3ff192b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3426,7 +3426,7 @@ void LLVOAvatar::updateCharacter(LLAgent &agent) // AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED, // AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN ); - F32 gain = .30f * gSavedSettings.getF32("AudioLevelAmbient"); + F32 gain = gSavedSettings.getBOOL("MuteAmbient") ? 0.f : (.30f * gSavedSettings.getF32("AudioLevelAmbient")); LLUUID& step_sound_id = getStepSound(); LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent); @@ -4401,7 +4401,7 @@ BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL //else { LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping")); - F32 volume = gSavedSettings.getF32("AudioLevelSFX"); + F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : gSavedSettings.getF32("AudioLevelSFX"); gAudiop->triggerSound(sound_id, getID(), volume, char_pos_global); } } diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index cf6b13e74c..1cbb1adada 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -43,7 +43,6 @@ #include "llcallbacklist.h" #include "llviewerregion.h" #include "llviewernetwork.h" // for gGridChoice -#include "llfloateractivespeakers.h" // for LLSpeakerMgr #include "llbase64.h" #include "llviewercontrol.h" #include "llkeyboard.h" @@ -532,7 +531,7 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length) void LLVivoxProtocolParser::processResponse(std::string tag) { -// llinfos << tag << llendl; + //llinfos << tag << llendl; if (isEvent) { @@ -768,7 +767,7 @@ static HANDLE sGatewayHandle = 0; static bool isGatewayRunning() { bool result = false; - if(sGatewayHandle != 0) + if(sGatewayHandle != 0) { DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); if(waitresult != WAIT_OBJECT_0) @@ -854,7 +853,7 @@ LLVoiceClient::LLVoiceClient() setPTTKey(keyString); mPTTIsToggle = gSavedSettings.getBOOL("PushToTalkToggle"); mEarLocation = gSavedSettings.getS32("VoiceEarLocation"); - setVoiceVolume(gSavedSettings.getF32("AudioLevelVoice")); + setVoiceVolume(gSavedSettings.getBOOL("MuteVoice") ? 0.f : gSavedSettings.getF32("AudioLevelVoice")); std::string captureDevice = gSavedSettings.getString("VoiceInputAudioDevice"); setCaptureDevice(captureDevice); std::string renderDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); @@ -876,7 +875,6 @@ LLVoiceClient::LLVoiceClient() mTuningMicVolumeDirty = true; mTuningSpeakerVolume = 0; mTuningSpeakerVolumeDirty = true; - mTuningCaptureRunning = false; // gMuteListp isn't set up at this point, so we defer this until later. // gMuteListp->addObserver(&mutelist_listener); @@ -1138,14 +1136,15 @@ const char *LLVoiceClient::state2string(LLVoiceClient::state inState) CASE(stateConnectorStart); CASE(stateConnectorStarting); CASE(stateConnectorStarted); - CASE(stateMicTuningNoLogin); CASE(stateLoginRetry); CASE(stateLoginRetryWait); CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); CASE(stateNoChannel); - CASE(stateMicTuningLoggedIn); + CASE(stateMicTuningStart); + CASE(stateMicTuningRunning); + CASE(stateMicTuningStop); CASE(stateSessionCreate); CASE(stateSessionConnect); CASE(stateJoiningSession); @@ -1164,6 +1163,7 @@ const char *LLVoiceClient::state2string(LLVoiceClient::state inState) CASE(stateJoinSessionFailed); CASE(stateJoinSessionFailedWaiting); CASE(stateJail); + CASE(stateMicTuningNoLogin); } #undef CASE @@ -1483,7 +1483,8 @@ void LLVoiceClient::stateMachine() } else if(mTuningMode) { - setState(stateMicTuningNoLogin); + mTuningExitState = stateConnectorStart; + setState(stateMicTuningStart); } break; @@ -1515,24 +1516,63 @@ void LLVoiceClient::stateMachine() } break; - case stateMicTuningNoLogin: - case stateMicTuningLoggedIn: - { - // Both of these behave essentially the same. The only difference is where the exit transition goes to. - if(mTuningMode && mVoiceEnabled && !mSessionTerminateRequested) - { - if(!mTuningCaptureRunning) + case stateMicTuningStart: + if(mUpdateTimer.hasExpired()) + { + if(mCaptureDeviceDirty || mRenderDeviceDirty) + { + // These can't be changed while in tuning mode. Set them before starting. + std::ostringstream stream; + + if(mCaptureDeviceDirty) + { + buildSetCaptureDevice(stream); + } + + if(mRenderDeviceDirty) + { + buildSetRenderDevice(stream); + } + + mCaptureDeviceDirty = false; + mRenderDeviceDirty = false; + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + + // This will come around again in the same state and start the capture, after the timer expires. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + } + else { // duration parameter is currently unused, per Mike S. tuningCaptureStartSendMessage(10000); + + setState(stateMicTuningRunning); } - - if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty || mCaptureDeviceDirty || mRenderDeviceDirty) + } + + break; + + case stateMicTuningRunning: + if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty) + { + // All of these conditions make us leave tuning mode. + setState(stateMicTuningStop); + } + else + { + // process mic/speaker volume changes + if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) { std::ostringstream stream; if(mTuningMicVolumeDirty) { + llinfos << "setting tuning mic level to " << mTuningMicVolume << llendl; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">" << "<Level>" << mTuningMicVolume << "</Level>" @@ -1547,20 +1587,8 @@ void LLVoiceClient::stateMachine() << "</Request>\n\n\n"; } - if(mCaptureDeviceDirty) - { - buildSetCaptureDevice(stream); - } - - if(mRenderDeviceDirty) - { - buildSetRenderDevice(stream); - } - mTuningMicVolumeDirty = false; mTuningSpeakerVolumeDirty = false; - mCaptureDeviceDirty = false; - mRenderDeviceDirty = false; if(!stream.str().empty()) { @@ -1568,23 +1596,19 @@ void LLVoiceClient::stateMachine() } } } - else - { - // transition out of mic tuning - if(mTuningCaptureRunning) - { - tuningCaptureStopSendMessage(); - } - - if(getState() == stateMicTuningNoLogin) - { - setState(stateConnectorStart); - } - else - { - setState(stateNoChannel); - } - } + break; + + case stateMicTuningStop: + { + // transition out of mic tuning + tuningCaptureStopSendMessage(); + + setState(mTuningExitState); + + // if we exited just to change devices, this will keep us from re-entering too fast. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + } break; @@ -1654,7 +1678,8 @@ void LLVoiceClient::stateMachine() } else if(mTuningMode) { - setState(stateMicTuningLoggedIn); + mTuningExitState = stateNoChannel; + setState(stateMicTuningStart); } else if(!mNextSessionHandle.empty()) { @@ -1880,6 +1905,12 @@ void LLVoiceClient::stateMachine() case stateJail: // We have given up. Do nothing. break; + + case stateMicTuningNoLogin: + // *TODO: Implement me. + llwarns << "stateMicTuningNoLogin not handled" + << llendl; + break; } if(mParticipantMapChanged) @@ -2183,9 +2214,9 @@ bool LLVoiceClient::inTuningMode() bool result = false; switch(getState()) { - case stateMicTuningNoLogin: - case stateMicTuningLoggedIn: + case stateMicTuningRunning: result = true; + break; default: break; } @@ -2193,10 +2224,7 @@ bool LLVoiceClient::inTuningMode() } void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ - if(!inTuningMode()) - return; - +{ mTuningAudioFile = name; std::ostringstream stream; stream @@ -2210,9 +2238,6 @@ void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool l void LLVoiceClient::tuningRenderStopSendMessage() { - if(!inTuningMode()) - return; - std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" @@ -2224,9 +2249,8 @@ void LLVoiceClient::tuningRenderStopSendMessage() void LLVoiceClient::tuningCaptureStartSendMessage(int duration) { - if(!inTuningMode()) - return; - + llinfos << "sending CaptureAudioStart" << llendl; + std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">" @@ -2234,15 +2258,12 @@ void LLVoiceClient::tuningCaptureStartSendMessage(int duration) << "</Request>\n\n\n"; writeString(stream.str()); - - mTuningCaptureRunning = true; } void LLVoiceClient::tuningCaptureStopSendMessage() { - if(!inTuningMode()) - return; - + llinfos << "sending CaptureAudioStop" << llendl; + std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" @@ -2250,7 +2271,7 @@ void LLVoiceClient::tuningCaptureStopSendMessage() writeString(stream.str()); - mTuningCaptureRunning = false; + mTuningEnergy = 0.0f; } void LLVoiceClient::tuningSetMicVolume(float volume) @@ -2914,12 +2935,16 @@ void LLVoiceClient::sessionNewEvent( LLUUID caller_id; if(IDFromName(nameString, caller_id)) { - gIMMgr->inviteToSession(LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, caller_id), - LLString::null, - caller_id, - LLString::null, - IM_SESSION_P2P_INVITE, - eventSessionHandle); + gIMMgr->inviteToSession( + LLIMMgr::computeSessionID( + IM_SESSION_P2P_INVITE, + caller_id), + LLString::null, + caller_id, + LLString::null, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + eventSessionHandle); } else { @@ -2985,6 +3010,7 @@ void LLVoiceClient::participantPropertiesEvent( { participant->mPTT = !isLocallyMuted; participant->mIsSpeaking = isSpeaking; + participant->mIsModeratorMuted = isModeratorMuted; if (isSpeaking) { participant->mSpeakingTimeout.reset(); @@ -3022,7 +3048,7 @@ void LLVoiceClient::muteListChanged() ///////////////////////////// // Managing list of participants LLVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), mPTT(false), mIsSpeaking(false), mPower(0.0), mServiceType(serviceTypeUnknown), + mURI(uri), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), mPower(0.0), mServiceType(serviceTypeUnknown), mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false) { } @@ -3265,6 +3291,7 @@ void LLVoiceClient::switchChannel( { // Leave any channel we may be in llinfos << "leaving channel" << llendl; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); } else { @@ -3786,6 +3813,19 @@ BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) return result; } +BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; +} + F32 LLVoiceClient::getCurrentPower(const LLUUID& id) { F32 result = 0; diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index ed256b6f8c..5179bc099c 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -63,6 +63,7 @@ public: STATUS_JOINING, STATUS_JOINED, STATUS_LEFT_CHANNEL, + STATUS_VOICE_DISABLED, BEGIN_ERROR_STATUS, ERROR_CHANNEL_FULL, ERROR_CHANNEL_LOCKED, @@ -139,6 +140,7 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient> void tuningStart(); void tuningStop(); bool inTuningMode(); + bool inTuningStates(); void tuningRenderStartSendMessage(const std::string& name, bool loop); void tuningRenderStopSendMessage(); @@ -218,6 +220,7 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient> // Accessors for data related to nearby speakers BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar BOOL getIsSpeaking(const LLUUID& id); + BOOL getIsModeratorMuted(const LLUUID& id); F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... BOOL getPTTPressed(const LLUUID& id); // This is the inverse of the "locally muted" property. BOOL getOnMuteList(const LLUUID& id); @@ -242,6 +245,7 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient> std::string mDisplayName; bool mPTT; bool mIsSpeaking; + bool mIsModeratorMuted; LLFrameTimer mSpeakingTimeout; F32 mLastSpokeTimestamp; F32 mPower; @@ -316,7 +320,9 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient> stateLoggingIn, // waiting for account handle stateLoggedIn, // account handle received stateNoChannel, // - stateMicTuningLoggedIn, // mic tuning for a logged in user + stateMicTuningStart, + stateMicTuningRunning, + stateMicTuningStop, stateSessionCreate, // need to send Session.Create command stateSessionConnect, // need to send Session.Connect command stateJoiningSession, // waiting for session handle @@ -387,7 +393,7 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient> bool mTuningMicVolumeDirty; int mTuningSpeakerVolume; bool mTuningSpeakerVolumeDirty; - bool mTuningCaptureRunning; + state mTuningExitState; // state to return to when we leave tuning mode. std::string mSpatialSessionURI; diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index e6676639e2..39f2f63066 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -111,7 +111,7 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++) { mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime; - mSoundSymbol.mTexture [i] = gImageList.getUIImageByID(sound_level_img[i]); + mSoundSymbol.mTexture [i] = gImageList.getImageByID(sound_level_img[i]); mSoundSymbol.mWaveActive [i] = false; mSoundSymbol.mWaveOpacity [i] = 1.0f; mSoundSymbol.mWaveExpansion [i] = 1.0f; diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index dac693885f..ec277b1a1b 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -1117,11 +1117,6 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen ) { - if( !getVisible() || !pointInView( x, y ) ) - { - return FALSE; - } - LLVector3d pos_global = viewPosToGlobal(x, y); LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 7bb12f7853..5775af68a1 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -435,7 +435,7 @@ class DarwinManifest(ViewerManifest): self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) # Unmount the image - self.run_command('hdiutil detach "' + devfile + '"') + self.run_command('hdiutil detach -force "' + devfile + '"') print "Converting temp disk image to final disk image" self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) |