diff options
128 files changed, 3109 insertions, 733 deletions
diff --git a/doc/contributions.txt b/doc/contributions.txt index 3f33763ce0..5667b69c28 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -323,6 +323,8 @@ Khyota Wulluf VWR-8885 VWR-9256 VWR-9966 +Kitty Barnett + VWR-19699 Kunnis Basiat VWR-82 VWR-102 diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 41ff5849f4..d06d6baf85 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -954,7 +954,12 @@ namespace LLError std::string class_name = className(site.mClassInfo); std::string function_name = functionName(site.mFunction); +#if LL_LINUX + // gross, but typeid comparison seems to always fail here with gcc4.1 + if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name())) +#else if (site.mClassInfo != typeid(NoClassInfo)) +#endif // LL_LINUX { function_name = class_name + "::" + function_name; } @@ -1079,7 +1084,12 @@ namespace LLError #if LL_WINDOWS // DevStudio: __FUNCTION__ already includes the full class name #else + #if LL_LINUX + // gross, but typeid comparison seems to always fail here with gcc4.1 + if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name())) + #else if (site.mClassInfo != typeid(NoClassInfo)) + #endif // LL_LINUX { prefix << className(site.mClassInfo) << "::"; } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 09812de2b8..e64ee5e081 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -179,7 +179,7 @@ namespace LLError { return s; } // used to indicate the end of a message - class NoClassInfo { }; + class LL_COMMON_API NoClassInfo { }; // used to indicate no class info known for logging //LLCallStacks keeps track of call stacks and output the call stacks to log file diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index ad8f8632a2..8071c8aa2d 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -249,7 +249,7 @@ public: ///////////////////////////////////////////////////////////////////////////////////////// // Static Utility functions that operate on std::strings - static std::basic_string<T> null; + static const std::basic_string<T> null; typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims); @@ -371,7 +371,7 @@ private: LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens); }; -template<class T> std::basic_string<T> LLStringUtilBase<T>::null; +template<class T> const std::basic_string<T> LLStringUtilBase<T>::null; template<class T> std::string LLStringUtilBase<T>::sLocale; typedef LLStringUtilBase<char> LLStringUtil; diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 6785d0cf17..1558df231a 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -545,15 +545,6 @@ namespace tut // output order void ErrorTestObject::test<10>() { -#if LL_LINUX - skip("Fails on Linux, see comments"); -// on Linux: -// [error, 10] fail: 'order is time type location function message: expected -// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) : -// writeReturningLocationAndFunction: apple' actual -// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) : -// LLError::NoClassInfo::writeReturningLocationAndFunction: apple'' -#endif LLError::setPrintLocation(true); LLError::setTimeFunction(roswell); mRecorder.setWantsTime(true); diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 91e11b8c0d..36874a5d48 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -365,6 +365,13 @@ U32 LLCurl::Easy::report(CURLcode code) responseReason = strerror(code) + " : " + mErrorBuffer; } + if(responseCode >= 300 && responseCode < 400) //redirect + { + char new_url[512] ; + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_REDIRECT_URL, new_url); + responseReason = new_url ; //get the new URL. + } + if (mResponder) { mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput); diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 5f866c49e6..6bf1347514 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -66,6 +66,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) , mAutoScrolling( false ) , mAutoScrollRate( 0.f ) , mSelectedTab( NULL ) + , mTabComparator( NULL ) , mNoVisibleTabsHelpText(NULL) { initNoTabsWidget(params.empty_accordion_text); @@ -799,6 +800,18 @@ void LLAccordionCtrl::reset () mScrollbar->setDocPos(0); } +void LLAccordionCtrl::sort() +{ + if (!mTabComparator) + { + llwarns << "No comparator specified for sorting accordion tabs." << llendl; + return; + } + + std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator)); + arrange(); +} + void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) { LLStringUtil::format_map_t args; diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index 2f483eafb2..fc6f2d896c 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -57,6 +57,19 @@ private: public: + /** + * Abstract comparator for accordion tabs. + */ + class LLTabComparator + { + public: + LLTabComparator() {}; + virtual ~LLTabComparator() {}; + + /** Returns true if tab1 < tab2, false otherwise */ + virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0; + }; + struct Params : public LLInitParam::Block<Params, LLPanel::Params> { @@ -108,6 +121,9 @@ public: void reset (); + void setComparator(const LLTabComparator* comp) { mTabComparator = comp; } + void sort(); + /** * Sets filter substring as a search_term for help text when there are no any visible tabs. */ @@ -134,6 +150,21 @@ private: BOOL autoScroll (S32 x, S32 y); + /** + * An adaptor for LLTabComparator + */ + struct LLComparatorAdaptor + { + LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {}; + + bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) + { + return mComparator.compare(tab1, tab2); + } + + const LLTabComparator& mComparator; + }; + private: LLRect mInnerRect; LLScrollbar* mScrollbar; @@ -141,9 +172,11 @@ private: bool mFitParent; bool mAutoScrolling; F32 mAutoScrollRate; - LLAccordionCtrlTab* mSelectedTab; LLTextBox* mNoVisibleTabsHelpText; std::string mNoVisibleTabsOrigString; + + LLAccordionCtrlTab* mSelectedTab; + const LLTabComparator* mTabComparator; }; diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 83fcc77f2a..1bc8086a27 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -473,7 +473,7 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel) addChild(panel,0); } -std::string LLAccordionCtrlTab::getTitle() +std::string LLAccordionCtrlTab::getTitle() const { LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); if (header) diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index be8b464b8e..82e0234bfc 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -115,7 +115,7 @@ public: void setAccordionView(LLView* panel); LLView* getAccordionView() { return mContainerPanel; }; - std::string getTitle(); + std::string getTitle() const; // Set text and highlight substring in LLAccordionCtrlTabHeader void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index fad98e553f..341debc9a8 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -810,6 +810,11 @@ void LLFloater::applyTitle() { mDragHandle->setTitle ( mTitle ); } + + if (getHost()) + { + getHost()->updateFloaterTitle(this); + } } std::string LLFloater::getCurrentTitle() const diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 75342afbe2..e9614ea660 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -339,6 +339,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW { if( *cur == '\n' ) { + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(cur-base); + text_segment->setToken( 0 ); + insertSegment( *seg_list, text_segment, text_len, defaultColor, editor); cur++; if( !*cur || *cur == '\n' ) { @@ -378,9 +381,8 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW } S32 seg_end = cur - base; - LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor ); - text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, defaultColor, editor); + //create segments from seg_start to seg_end + insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor); line_done = TRUE; // to break out of second loop. break; } @@ -486,11 +488,12 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW seg_end = seg_start + between_delimiters + cur_delimiter->getLength(); } - + insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, defaultColor, editor); + /* LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor ); text_segment->setToken( cur_delimiter ); insertSegment( seg_list, text_segment, text_len, defaultColor, editor); - + */ // Note: we don't increment cur, since the end of one delimited seg may be immediately // followed by the start of another one. continue; @@ -519,10 +522,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW // llinfos << "Seg: [" << word.c_str() << "]" << llendl; - - LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor ); - text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, defaultColor, editor); + insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor); } cur += seg_len; continue; @@ -537,24 +537,50 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW } } -void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>* seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor ) +void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, const LLColor4 &defaultColor, LLTextEditor& editor ) +{ + std::string::size_type pos = wtext.find('\n',seg_start); + + while (pos!=-1 && pos < (std::string::size_type)seg_end) + { + if (pos!=seg_start) + { + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, pos, editor ); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); + } + + LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(pos); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); + + seg_start = pos+1; + pos = wtext.find('\n',seg_start); + } + + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor ); + text_segment->setToken( cur_token ); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); +} + +void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor ) { - LLTextSegmentPtr last = seg_list->back(); + LLTextSegmentPtr last = seg_list.back(); S32 new_seg_end = new_segment->getEnd(); if( new_segment->getStart() == last->getStart() ) { - seg_list->pop_back(); + seg_list.pop_back(); } else { last->setEnd( new_segment->getStart() ); } - seg_list->push_back( new_segment ); + seg_list.push_back( new_segment ); if( new_seg_end < text_len ) { - seg_list->push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) ); + seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) ); } } diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index e5b66dfa56..09378e408b 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -129,7 +129,8 @@ public: private: LLColor3 readColor(const std::string& s); - void insertSegment(std::vector<LLTextSegmentPtr> *seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, class LLTextEditor& editor); + void insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, class LLTextEditor& editor); + void insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* token, S32 text_len, S32 seg_start, S32 seg_end, const LLColor4 &defaultColor, LLTextEditor& editor); BOOL mLoaded; word_token_map_t mWordTokenMap; diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index 3aea648562..b1dbce0000 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -238,6 +238,16 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, moveResizeHandlesToFront(); } +void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp) +{ + S32 index = mTabContainer->getIndexForPanel(floaterp); + if (index != -1) + { + mTabContainer->setPanelTitle(index, floaterp->getShortTitle()); + } +} + + /** BOOL selectFloater(LLFloater* floaterp) diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h index bbf2c56fe7..24a841f64e 100644 --- a/indra/llui/llmultifloater.h +++ b/indra/llui/llmultifloater.h @@ -80,6 +80,7 @@ public: void onTabSelected(); virtual void updateResizeLimits(); + virtual void updateFloaterTitle(LLFloater* floaterp); protected: struct LLFloaterData diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 55dbf50fd7..72f3a14822 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1101,7 +1101,7 @@ S32 LLTextBase::getLeftOffset(S32 width) case LLFontGL::LEFT: return mHPad; case LLFontGL::HCENTER: - return mHPad + (mVisibleTextRect.getWidth() - width - mHPad) / 2; + return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2); case LLFontGL::RIGHT: return mVisibleTextRect.getWidth() - width; default: @@ -1207,11 +1207,6 @@ void LLTextBase::reflow() // grow line height as necessary based on reported height of this segment line_height = llmax(line_height, segment_height); remaining_pixels -= segment_width; - if (remaining_pixels < 0) - { - // getNumChars() and getDimensions() should return consistent results - remaining_pixels = 0; - } seg_offset += character_count; @@ -1893,7 +1888,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, { // Figure out which line we're nearest to. LLRect visible_region = getVisibleDocumentRect(); - + // binary search for line that starts before local_y line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mVisibleTextRect.mBottom + visible_region.mBottom, compare_bottom()); @@ -1903,7 +1898,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, } S32 pos = getLength(); - S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft; + S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft - visible_region.mLeft; segment_set_t::iterator line_seg_iter; S32 line_seg_offset; @@ -2748,6 +2743,12 @@ void LLInlineViewSegment::linkToDocument(LLTextBase* editor) editor->addDocumentChild(mView); } +LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1) +{ + LLStyleSP s( new LLStyle(LLStyle::Params().visible(true))); + + mFontHeight = llceil(s->getFont()->getLineHeight()); +} LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1) { mFontHeight = llceil(style->getFont()->getLineHeight()); diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 176308c61a..89ce5cdc8e 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -519,6 +519,7 @@ class LLLineBreakTextSegment : public LLTextSegment public: LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); + LLLineBreakTextSegment(S32 pos); ~LLLineBreakTextSegment(); bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3b1c49edd3..3c98cd5b85 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -215,6 +215,7 @@ set(viewer_SOURCE_FILES llfloaterurldisplay.cpp llfloaterurlentry.cpp llfloatervoicedevicesettings.cpp + llfloatervoiceeffect.cpp llfloaterwater.cpp llfloaterwhitelistentry.cpp llfloaterwindlight.cpp @@ -357,6 +358,7 @@ set(viewer_SOURCE_FILES llpanelprofileview.cpp llpanelteleporthistory.cpp llpaneltiptoast.cpp + llpanelvoiceeffect.cpp llpaneltopinfobar.cpp llpanelvolume.cpp llpanelvolumepulldown.cpp @@ -737,6 +739,7 @@ set(viewer_HEADER_FILES llfloaterurldisplay.h llfloaterurlentry.h llfloatervoicedevicesettings.h + llfloatervoiceeffect.h llfloaterwater.h llfloaterwhitelistentry.h llfloaterwindlight.h @@ -874,6 +877,7 @@ set(viewer_HEADER_FILES llpanelprofileview.h llpanelteleporthistory.h llpaneltiptoast.h + llpanelvoiceeffect.h llpaneltopinfobar.h llpanelvolume.h llpanelvolumepulldown.h diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 16e39fc1c4..937c4e4c6a 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -41,6 +41,8 @@ </array> <key>tags</key> <array> + <!-- sample entry for debugging a specific item --> +<!-- <string>Voice</string> --> </array> </map> </array> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f87f041d6f..52e987dac9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -583,7 +583,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>0</integer> + <integer>180</integer> </map> <key>AvatarSex</key> <map> @@ -3862,7 +3862,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>InBandwidth</key> <map> @@ -10759,6 +10759,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>VoiceEffectExpiryWarningTime</key> + <map> + <key>Comment</key> + <string>How much notice to give of Voice Morph subscriptions expiry, in seconds.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>259200</integer> + </map> + <key>VoiceMorphingEnabled</key> + <map> + <key>Comment</key> + <string>Whether or not to enable Voice Morphs and show the UI.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>AutoDisengageMic</key> <map> <key>Comment</key> @@ -11395,5 +11417,16 @@ <key>Value</key> <integer>1</integer> </map> + <key>OutfitOperationsTimeout</key> + <map> + <key>Comment</key> + <string>Timeout for outfit related operations.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>180</integer> + </map> </map> </llsd> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 3ce32a05b0..d4000e9253 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -99,6 +99,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>VoiceEffectDefault</key> + <map> + <key>Comment</key> + <string>Selected Voice Morph</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> <!-- Settings below are for back compatibility only. They are not used in current viewer anymore. But they can't be removed to avoid diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 5aad295cb1..62766f9229 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -204,8 +204,8 @@ NVIDIA GeForce 7200 .*NVIDIA.*GeForce 72.* 1 1 NVIDIA GeForce 7300 .*NVIDIA.*GeForce 73.* 1 1 NVIDIA GeForce 7500 .*NVIDIA.*GeForce 75.* 1 1 NVIDIA GeForce 7600 .*NVIDIA.*GeForce 76.* 1 1 -NVIDIA GeForce 7800 .*NVIDIA.*GeForce.*78.* 1 1 -NVIDIA GeForce 7900 .*NVIDIA.*GeForce.*79.* 1 1 +NVIDIA GeForce 7800 .*NVIDIA.*GeForce 78.* 1 1 +NVIDIA GeForce 7900 .*NVIDIA.*GeForce 79.* 1 1 NVIDIA GeForce 8100 .*NVIDIA.*GeForce 81.* 1 1 NVIDIA GeForce 8200 .*NVIDIA.*GeForce 82.* 1 1 NVIDIA GeForce 8300 .*NVIDIA.*GeForce 83.* 1 1 @@ -259,6 +259,7 @@ NVIDIA G84 .*G84.* 1 1 NVIDIA G92 .*G92.* 3 1 NVIDIA G94 .*G94.* 3 1 NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 +NVIDIA ION .*NVIDIA ION.* 1 1 NVIDIA NB9M .*GeForce NB9M.* 1 1 NVIDIA NB9P .*GeForce NB9P.* 1 1 NVIDIA GeForce PCX .*GeForce PCX.* 0 1 diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index d2e55f88a0..03efcadc98 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3589,10 +3589,10 @@ void LLAgent::sendAgentSetAppearance() if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLVOAvatarDefines::EBakedTextureIndex)baked_index)) { generate_valid_hash = FALSE; + llinfos << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << llendl; } - LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash); - + const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash); if (hash.notNull()) { ETextureIndex texture_index = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex) baked_index); diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 47f290ad3b..644363826a 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -2360,7 +2360,6 @@ void LLAgentCamera::changeCameraToCustomizeAvatar(BOOL avatar_animate, BOOL came mAnimationDuration = gSavedSettings.getF32("ZoomTime"); } } - setFocusGlobal(LLVector3d::zero); } else { diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index e0104eddf0..cfa0a4d729 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -64,6 +64,25 @@ BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE; using namespace LLVOAvatarDefines; +/////////////////////////////////////////////////////////////////////////////// + +// Callback to wear and start editing an item that has just been created. +class LLWearAndEditCallback : public LLInventoryCallback +{ + void fire(const LLUUID& inv_item) + { + if (inv_item.isNull()) return; + + // Request editing the item after it gets worn. + gAgentWearables.requestEditingWearable(inv_item); + + // Wear it. + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + // HACK: For EXT-3923: Pants item shows in inventory with skin icon and messes with "current look" // Some db items are corrupted, have inventory flags = 0, implying wearable type = shape, even though // wearable type stored in asset is some other value. @@ -1638,10 +1657,6 @@ LLUUID LLAgentWearables::computeBakedTextureHash(LLVOAvatarDefines::EBakedTextur { LLUUID asset_id = wearable->getAssetID(); hash.update((const unsigned char*)asset_id.mData, UUID_BYTES); - if (!generate_valid_hash) - { - hash.update((const unsigned char*)asset_id.mData, UUID_BYTES); - } hash_computed = true; } } @@ -1649,6 +1664,15 @@ LLUUID LLAgentWearables::computeBakedTextureHash(LLVOAvatarDefines::EBakedTextur if (hash_computed) { hash.update((const unsigned char*)baked_dict->mWearablesHashID.mData, UUID_BYTES); + + // Add some garbage into the hash so that it becomes invalid. + if (!generate_valid_hash) + { + if (isAgentAvatarValid()) + { + hash.update((const unsigned char*)gAgentAvatarp->getID().mData, UUID_BYTES); + } + } hash.finalize(); hash.raw_digest(hash_id.mData); } @@ -1985,7 +2009,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con LLWearable* wearable = LLWearableList::instance().createNewWearable(type); LLAssetType::EType asset_type = wearable->getAssetType(); LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; - LLPointer<LLInventoryCallback> cb = wear ? new WearOnAvatarCallback : NULL; + LLPointer<LLInventoryCallback> cb = wear ? new LLWearAndEditCallback : NULL; LLUUID folder_id; if (parent_id.notNull()) @@ -2008,17 +2032,44 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con // static void LLAgentWearables::editWearable(const LLUUID& item_id) { - LLViewerInventoryItem* item; - LLWearable* wearable; + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); + if (!item) + { + llwarns << "Failed to get linked item" << llendl; + return; + } - if ((item = gInventory.getLinkedItem(item_id)) && - (wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) && - gAgentWearables.isWearableModifiable(item->getUUID()) && - item->isFinished()) + LLWearable* wearable = gAgentWearables.getWearableFromItemID(item_id); + if (!wearable) + { + llwarns << "Cannot get wearable" << llendl; + return; + } + + if (!gAgentWearables.isWearableModifiable(item->getUUID())) + { + llwarns << "Cannot modify wearable" << llendl; + return; + } + + LLPanel* panel = LLSideTray::getInstance()->getPanel("sidepanel_appearance"); + LLSidepanelAppearance::editWearable(wearable, panel); +} + +// Request editing the item after it gets worn. +void LLAgentWearables::requestEditingWearable(const LLUUID& item_id) +{ + mItemToEdit = gInventory.getLinkedItemID(item_id); +} + +// Start editing the item if previously requested. +void LLAgentWearables::editWearableIfRequested(const LLUUID& item_id) +{ + if (mItemToEdit.notNull() && + mItemToEdit == gInventory.getLinkedItemID(item_id)) { - LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD()); - // copied from LLPanelOutfitEdit::onEditWearableClicked() - LLSidepanelAppearance::editWearable(wearable, panel->getParent()); + LLAgentWearables::editWearable(item_id); + mItemToEdit.setNull(); } } diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 679ecefa6f..a41b949be6 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -148,6 +148,12 @@ public: static void editWearable(const LLUUID& item_id); bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + void requestEditingWearable(const LLUUID& item_id); + void editWearableIfRequested(const LLUUID& item_id); + +private: + LLUUID mItemToEdit; + //-------------------------------------------------------------------- // Removing wearables //-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index e6f363028a..135629bb8d 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -38,11 +38,13 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llcommandhandler.h" +#include "lleventtimer.h" #include "llgesturemgr.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llnotificationsutil.h" +#include "lloutfitobserver.h" #include "llpaneloutfitsinventory.h" #include "llselectmgr.h" #include "llsidepanelappearance.h" @@ -55,6 +57,34 @@ char ORDER_NUMBER_SEPARATOR('@'); +class LLOutfitUnLockTimer: public LLEventTimer +{ +public: + LLOutfitUnLockTimer(F32 period) : LLEventTimer(period) + { + // restart timer on BOF changed event + LLOutfitObserver::instance().addBOFChangedCallback(boost::bind( + &LLOutfitUnLockTimer::reset, this)); + stop(); + } + + /*virtual*/ + BOOL tick() + { + if(mEventTimer.hasExpired()) + { + LLAppearanceMgr::instance().setOutfitLocked(false); + } + return FALSE; + } + void stop() { mEventTimer.stop(); } + void start() { mEventTimer.start(); } + void reset() { mEventTimer.reset(); } + BOOL getStarted() { return mEventTimer.getStarted(); } + + LLTimer& getEventTimer() { return mEventTimer;} +}; + // support for secondlife:///app/appearance SLapps class LLAppearanceHandler : public LLCommandHandler { @@ -656,15 +686,37 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up { if (item_id_to_wear.isNull()) return false; - //only the item from a user's inventory is allowed - if (!gInventory.isObjectDescendentOf(item_id_to_wear, gInventory.getRootFolderID())) return false; - LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); if (!item_to_wear) return false; + if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) + { + LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace); + copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb); + return false; + } + else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) + { + return false; // not in library and not in agent's inventory + } + else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) + { + LLNotificationsUtil::add("CannotWearTrash"); + return false; + } + switch (item_to_wear->getType()) { case LLAssetType::AT_CLOTHING: + if (gAgentWearables.areWearablesLoaded()) + { + S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); + if ((replace && wearable_count != 0) || + (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) + { + removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false); + } + } case LLAssetType::AT_BODYPART: // Don't wear anything until initial wearables are loaded, can // destroy clothing items. @@ -676,7 +728,7 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up // Remove the existing wearables of the same type. // Remove existing body parts anyway because we must not be able to wear e.g. two skins. - if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART) + if (item_to_wear->getType() == LLAssetType::AT_BODYPART) { removeCOFLinksOfType(item_to_wear->getWearableType(), false); } @@ -740,6 +792,27 @@ void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& respo } } +void LLAppearanceMgr::setOutfitLocked(bool locked) +{ + if (mOutfitLocked == locked) + { + return; + } + + mOutfitLocked = locked; + if (locked) + { + mUnlockOutfitTimer->reset(); + mUnlockOutfitTimer->start(); + } + else + { + mUnlockOutfitTimer->stop(); + } + + LLOutfitObserver::instance().notifyOutfitLockChanged(); +} + void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) { LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); @@ -1559,6 +1632,7 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update item_array, LLInventoryModel::EXCLUDE_TRASH); bool linked_already = false; + U32 count = 0; for (S32 i=0; i<item_array.count(); i++) { // Are these links to the same object? @@ -1576,15 +1650,21 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update } // Are these links to different items of the same body part // type? If so, new item will replace old. - // TODO: MULTI-WEARABLE: check for wearable limit for clothing types - else if (is_body_part && (vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) + else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) { - if (inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) + ++count; + if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) + { + gInventory.purgeObject(inv_item->getUUID()); + } + else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) { + // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE gInventory.purgeObject(inv_item->getUUID()); } } } + if (linked_already) { if (do_update) @@ -1781,6 +1861,14 @@ void LLAppearanceMgr::onFirstFullyVisible() bool LLAppearanceMgr::updateBaseOutfit() { + if (isOutfitLocked()) + { + // don't allow modify locked outfit + llassert(!isOutfitLocked()); + return false; + } + setOutfitLocked(true); + const LLUUID base_outfit_id = getBaseOutfitUUID(); if (base_outfit_id.isNull()) return false; @@ -2109,6 +2197,14 @@ LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), mOutfitIsDirty(false) { + LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); + + // unlock outfit on save operation completed + outfit_observer.addCOFSavedCallback(boost::bind( + &LLAppearanceMgr::setOutfitLocked, this, false)); + + mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32( + "OutfitOperationsTimeout"))); } LLAppearanceMgr::~LLAppearanceMgr() diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index f1beef5857..2227a43cd8 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -42,10 +42,12 @@ class LLWearable; class LLWearableHoldingPattern; class LLInventoryCallback; +class LLOutfitUnLockTimer; class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr> { friend class LLSingleton<LLAppearanceMgr>; + friend class LLOutfitUnLockTimer; public: typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t; @@ -162,6 +164,8 @@ public: // COF is processed if cat_id is not specified void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null); + bool isOutfitLocked() { return mOutfitLocked; } + protected: LLAppearanceMgr(); ~LLAppearanceMgr(); @@ -186,10 +190,20 @@ private: static void onOutfitRename(const LLSD& notification, const LLSD& response); + void setOutfitLocked(bool locked); + std::set<LLUUID> mRegisteredAttachments; bool mAttachmentInvLinkEnabled; bool mOutfitIsDirty; + /** + * Lock for blocking operations on outfit until server reply or timeout exceed + * to avoid unsynchronized outfit state or performing duplicate operations. + */ + bool mOutfitLocked; + + std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer; + ////////////////////////////////////////////////////////////////////////////////// // Item-specific convenience functions public: diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index dd99c6564c..60a2392d87 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -95,7 +95,7 @@ static void* create_non_avatar_caller(void*) return new LLNonAvatarCaller; } -LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL; +LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL; LLCallFloater::LLCallFloater(const LLSD& key) : LLTransientDockableFloater(NULL, false, key) @@ -113,7 +113,7 @@ LLCallFloater::LLCallFloater(const LLSD& key) mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay); mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL); - LLVoiceClient::getInstance()->addObserver(this); + LLVoiceClient::instance().addObserver(this); LLTransientFloaterMgr::getInstance()->addControlView(this); // force docked state since this floater doesn't save it between recreations @@ -158,7 +158,6 @@ BOOL LLCallFloater::postBuild() initAgentData(); - connectToChannel(LLVoiceChannel::getCurrentVoiceChannel()); setIsChrome(true); @@ -204,7 +203,7 @@ void LLCallFloater::draw() } // virtual -void LLCallFloater::onChange() +void LLCallFloater::onParticipantsChanged() { if (NULL == mParticipants) return; updateParticipantsVoiceState(); @@ -287,22 +286,22 @@ void LLCallFloater::updateSession() if (NULL == mSpeakerManager) { - // by default let show nearby chat participants + // By default show nearby chat participants mSpeakerManager = LLLocalSpeakerMgr::getInstance(); LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL; mVoiceType = VC_LOCAL_CHAT; } updateTitle(); - - //hide "Leave Call" button for nearby chat + + // Hide "Leave Call" button for nearby chat bool is_local_chat = mVoiceType == VC_LOCAL_CHAT; childSetVisible("leave_call_btn_panel", !is_local_chat); refreshParticipantList(); updateAgentModeratorState(); - //show floater for voice calls & only in CONNECTED to voice channel state + // Show floater for voice calls & only in CONNECTED to voice channel state if (!is_local_chat && voice_channel && LLVoiceChannel::STATE_CONNECTED == voice_channel->getState()) @@ -368,7 +367,7 @@ void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO // it sill be sent for the same channel again (when state is changed). // So, lets ignore this call. - if (channel == sCurrentVoiceCanel) return; + if (channel == sCurrentVoiceChannel) return; LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls"); @@ -715,9 +714,9 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel) { mVoiceChannelStateChangeConnection.disconnect(); - sCurrentVoiceCanel = channel; + sCurrentVoiceChannel = channel; - mVoiceChannelStateChangeConnection = sCurrentVoiceCanel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); + mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); updateState(channel->getState()); } @@ -737,7 +736,7 @@ void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state) { - LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceCanel->getSessionName() << LL_ENDL; + LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL; if (LLVoiceChannel::STATE_CONNECTED == new_state) { updateSession(); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 0a8ea7de39..e4341175e2 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -47,15 +47,15 @@ class LLSpeakerMgr; class LLSpeakersDelayActionsStorage; /** - * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button. - * It can be torn-off and freely positioned onscreen. + * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron + * on the Speak button. It can be torn-off and freely positioned onscreen. * - * When the Resident is engaged in Nearby Voice Chat, the Voice Control Panel provides control over - * the Resident's own microphone input volume, the audible volume of each of the other participants, - * the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording. + * When the Resident is engaged in Voice Chat, the Voice Control Panel provides control + * over the audible volume of each of the other participants, the Resident's own Voice + * Morphing settings (if she has subscribed to enable the feature), and Voice Recording. * - * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an - * 'Leave Call' button to allow the Resident to leave that voice channel. + * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel + * also provides a 'Leave Call' button to allow the Resident to leave that voice channel. */ class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipantObserver { @@ -75,7 +75,7 @@ public: * * Refreshes list to display participants not in voice as disabled. */ - /*virtual*/ void onChange(); + /*virtual*/ void onParticipantsChanged(); static void sOnCurrentChannelChanged(const LLUUID& session_id); @@ -259,7 +259,7 @@ private: * * @see sOnCurrentChannelChanged() */ - static LLVoiceChannel* sCurrentVoiceCanel; + static LLVoiceChannel* sCurrentVoiceChannel; /* virtual */ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 05046aca5a..916d53da3c 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -387,13 +387,7 @@ LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventory item_panel->childSetAction("btn_edit", mCOFCallbacks.mEditWearable); //turning on gray separator line for the last item in the items group of the same wearable type - if (last) - { - LLRect rect = item_panel->getRect(); - item_panel->reshape(rect.getWidth(), rect.getHeight() + - item_panel->getChild<LLView>("wearable_type_separator_icon")->getRect().getHeight()); - item_panel->childSetVisible("wearable_type_separator_icon", true); - } + item_panel->childSetVisible("wearable_type_separator_icon", last); return item_panel; } diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp new file mode 100644 index 0000000000..ca1f142760 --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -0,0 +1,288 @@ +/** + * @file llfloatervoiceeffect.cpp + * @author Aimee + * @brief Selection and preview of voice effect. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoiceeffect.h" + +#include "llscrolllistctrl.h" +#include "lltrans.h" +#include "llweb.h" + +LLFloaterVoiceEffect::LLFloaterVoiceEffect(const LLSD& key) + : LLFloater(key) +{ + mCommitCallbackRegistrar.add("VoiceEffect.Record", boost::bind(&LLFloaterVoiceEffect::onClickRecord, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Play", boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Stop", boost::bind(&LLFloaterVoiceEffect::onClickStop, this)); +// mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); +} + +// virtual +LLFloaterVoiceEffect::~LLFloaterVoiceEffect() +{ + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLFloaterVoiceEffect::postBuild() +{ + setDefaultBtn("record_btn"); + getChild<LLButton>("record_btn")->setFocus(true); + childSetTextArg("voice_morphing_link", "[URL]", LLTrans::getString("voice_morphing_url")); + + mVoiceEffectList = getChild<LLScrollListCtrl>("voice_effect_list"); + if (mVoiceEffectList) + { + mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); +// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + } + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + + // Disconnect from the current voice channel ready to record a voice sample for previewing + effect_interface->enablePreviewBuffer(true); + } + + refreshEffectList(); + updateControls(); + + return TRUE; +} + +// virtual +void LLFloaterVoiceEffect::onClose(bool app_quitting) +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->enablePreviewBuffer(false); + } +} + +void LLFloaterVoiceEffect::refreshEffectList() +{ + if (!mVoiceEffectList) + { + return; + } + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectList->setEnabled(false); + return; + } + + LL_DEBUGS("Voice")<< "Rebuilding Voice Morph list."<< LL_ENDL; + + // Preserve selected items and scroll position + S32 scroll_pos = mVoiceEffectList->getScrollPos(); + uuid_vec_t selected_items; + std::vector<LLScrollListItem*> items = mVoiceEffectList->getAllSelected(); + for(std::vector<LLScrollListItem*>::const_iterator it = items.begin(); it != items.end(); it++) + { + selected_items.push_back((*it)->getUUID()); + } + + mVoiceEffectList->deleteAllItems(); + + { + // Add the "No Voice Morph" entry + LLSD element; + + element["id"] = LLUUID::null; + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = getString("no_voice_effect"); + element["columns"][NAME_COLUMN]["font"]["style"] = "BOLD"; + + LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(LLFontGL::BOLD); + } + } + + // Add each Voice Morph template, if there are any (template list includes all usable effects) + const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList(); + if (!template_list.empty()) + { + for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it) + { + const LLUUID& effect_id = it->second; + std::string effect_name = it->first; + + LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id); + + // Tag the active effect. + if (effect_id == LLVoiceClient::instance().getVoiceEffectDefault()) + { + effect_name += " " + getString("active_voice_effect"); + } + + // Tag available effects that are new this session + if (effect_properties["is_new"].asBoolean()) + { + effect_name += " " + getString("new_voice_effect"); + } + + LLDate expiry_date = effect_properties["expiry_date"].asDate(); + bool is_template_only = effect_properties["template_only"].asBoolean(); + + std::string font_style = "NORMAL"; + if (!is_template_only) + { + font_style = "BOLD"; + } + + LLSD element; + element["id"] = effect_id; + + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = effect_name; + element["columns"][NAME_COLUMN]["font"]["style"] = font_style; + + element["columns"][1]["column"] = "expires"; + if (!is_template_only) + { + element["columns"][DATE_COLUMN]["value"] = expiry_date; + element["columns"][DATE_COLUMN]["type"] = "date"; + } + else { + element["columns"][DATE_COLUMN]["value"] = getString("unsubscribed_voice_effect"); + } +// element["columns"][DATE_COLUMN]["font"]["style"] = "NORMAL"; + + LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD; + dynamic_cast<LLScrollListText*>(sl_item->getColumn(0))->setFontStyle(style); + } + } + } + + // Re-select items that were selected before, and restore the scroll position + for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++) + { + mVoiceEffectList->selectByID(*it); + } + mVoiceEffectList->setScrollPos(scroll_pos); + mVoiceEffectList->setEnabled(true); +} + +void LLFloaterVoiceEffect::updateControls() +{ + bool recording = false; + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + recording = effect_interface->isPreviewRecording(); + } + + getChild<LLButton>("record_btn")->setVisible(!recording); + getChild<LLButton>("record_stop_btn")->setVisible(recording); +} + +// virtual +void LLFloaterVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + if (effect_list_updated) + { + refreshEffectList(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickRecord() +{ + LL_DEBUGS("Voice") << "Record clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->recordPreviewBuffer(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickPlay() +{ + LL_DEBUGS("Voice") << "Play clicked" << LL_ENDL; + if (!mVoiceEffectList) + { + return; + } + + const LLUUID& effect_id = mVoiceEffectList->getCurrentID(); + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->playPreviewBuffer(effect_id); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickStop() +{ + LL_DEBUGS("Voice") << "Stop clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->stopPreviewBuffer(); + } + updateControls(); +} + +//void LLFloaterVoiceEffect::onClickActivate() +//{ +// LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); +// if (effect_interface && mVoiceEffectList) +// { +// effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); +// } +//} + diff --git a/indra/newview/llfloatervoiceeffect.h b/indra/newview/llfloatervoiceeffect.h new file mode 100644 index 0000000000..fe207a05c3 --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.h @@ -0,0 +1,78 @@ +/** + * @file llfloatervoiceeffect.h + * @author Aimee + * @brief Selection and preview of voice effects. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEEFFECT_H +#define LL_LLFLOATERVOICEEFFECT_H + +#include "llfloater.h" +#include "llvoiceclient.h" + +class LLButton; +class LLScrollListCtrl; + +class LLFloaterVoiceEffect + : public LLFloater + , public LLVoiceEffectObserver +{ +public: + LOG_CLASS(LLFloaterVoiceEffect); + + LLFloaterVoiceEffect(const LLSD& key); + virtual ~LLFloaterVoiceEffect(); + + virtual BOOL postBuild(); + virtual void onClose(bool app_quitting); + +private: + enum ColumnIndex + { + NAME_COLUMN = 0, + DATE_COLUMN = 1, + }; + + void refreshEffectList(); + void updateControls(); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + void onClickRecord(); + void onClickPlay(); + void onClickStop(); +// void onClickActivate(); + + LLUUID mSelectedID; + LLScrollListCtrl* mVoiceEffectList; +}; + +#endif diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 0e63c21e16..3a9c5ba698 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -103,7 +103,6 @@ void dec_busy_count() } // Function declarations -void wear_add_inventory_item_on_avatar(LLInventoryItem* item); void remove_inventory_category_from_avatar(LLInventoryCategory* category); void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id); bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); @@ -4277,33 +4276,6 @@ LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, mInvType = inv_type; } -// *NOTE: hack to get from avatar inventory to avatar -void wear_inventory_item_on_avatar( LLInventoryItem* item ) -{ - if(item) - { - lldebugs << "wear_inventory_item_on_avatar( " << item->getName() - << " )" << llendl; - - LLAppearanceMgr::getInstance()->wearItemOnAvatar(item->getUUID(), true, false); - } -} - -void wear_add_inventory_item_on_avatar( LLInventoryItem* item ) -{ - if(item) - { - lldebugs << "wear_add_inventory_item_on_avatar( " << item->getName() - << " )" << llendl; - - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - LLWearableBridge::onWearAddOnAvatarArrived, - new LLUUID(item->getUUID())); - } -} - void remove_inventory_category_from_avatar( LLInventoryCategory* category ) { if(!category) return; @@ -4619,21 +4591,7 @@ void LLWearableBridge::wearOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar(item); - } + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); } } @@ -4650,21 +4608,7 @@ void LLWearableBridge::wearAddOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_add_inventory_item_on_avatar(item); - } + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, false); } } @@ -5189,41 +5133,7 @@ class LLWearableBridgeAction: public LLInvFVBridgeAction public: virtual void doIt() { - if(isItemInTrash()) - { - LLNotificationsUtil::add("CannotWearTrash"); - } - else if(isAgentInventory()) - { - if(!get_is_item_worn(mUUID)) - { - wearOnAvatar(); - } - } - else - { - // must be in the inventory library. copy it to our inventory - // and put it on right away. - LLViewerInventoryItem* item = getItem(); - if(item && item->isFinished()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else if(item) - { - // *TODO: We should fetch the item details, and then do - // the operation above. - LLNotificationsUtil::add("CannotWearInfoNotComplete"); - } - } - LLInvFVBridgeAction::doIt(); + wearOnAvatar(); } virtual ~LLWearableBridgeAction(){} protected: @@ -5262,21 +5172,7 @@ void LLWearableBridgeAction::wearOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar(item); - } + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); } } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 310fd7fb1d..a342a2da14 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -591,8 +591,6 @@ public: U32 flags = 0x00) const; }; -void wear_inventory_item_on_avatar(LLInventoryItem* item); - void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment); diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 33b52cfd5e..c82ebd1439 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -324,6 +324,19 @@ private: LLWearableType::EType mWearableType; }; +/** Filter out wearables-links */ +class LLFindActualWearablesOfType : public LLFindWearablesOfType +{ +public: + LLFindActualWearablesOfType(LLWearableType::EType type) : LLFindWearablesOfType(type) {} + virtual ~LLFindActualWearablesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (item && item->getIsLinkType()) return false; + return LLFindWearablesOfType::operator()(cat, item); + } +}; + // Find worn items. class LLFindWorn : public LLInventoryCollectFunctor { diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 2e1c5238d3..15760bf155 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "llinventorypanel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -720,8 +721,20 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) // Valid UUID; set the item UUID and rename it new_item->setCreator(id); std::string avatar_name; - // Fetch the currect name - gCacheName->get(id, FALSE, boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), _1, _2, _3)); + + if (gCacheName->getFullName(id, avatar_name)) + { + new_item->rename(avatar_name); + mask |= LLInventoryObserver::LABEL; + } + else + { + // Fetch the current name + gCacheName->get(id, FALSE, + boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), + _1, _2, _3)); + } + } } else if (new_item->getType() == LLAssetType::AT_GESTURE) @@ -2561,7 +2574,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLViewerInventoryItem* wearable_item; wearable_item = gInventory.getItem(wearable_ids[i]); - wear_inventory_item_on_avatar(wearable_item); + LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); } } diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index d2b402fe14..bd35259670 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -62,13 +62,9 @@ #include "llsdutil.h" #include <deque> -// If the viewer gets a notification, your observer assumes -// that that notification is for itself and then tries to process -// the results. The notification could be for something else (e.g. -// you're fetching an item and a notification gets triggered because -// you renamed some other item). This counter is to specify how many -// notification to wait for before giving up. -static const U32 MAX_NUM_NOTIFICATIONS_TO_PROCESS = 127; +const U32 LLInventoryFetchItemsObserver::MAX_NUM_ATTEMPTS_TO_PROCESS = 10; +const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 10.0f; + LLInventoryObserver::LLInventoryObserver() { @@ -149,7 +145,7 @@ void LLInventoryCompletionObserver::watchItem(const LLUUID& id) LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const LLUUID& item_id) : LLInventoryFetchObserver(item_id), - mNumTries(MAX_NUM_NOTIFICATIONS_TO_PROCESS) + mNumTries(MAX_NUM_ATTEMPTS_TO_PROCESS) { mIDs.clear(); mIDs.push_back(item_id); @@ -158,35 +154,47 @@ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const LLUUID& item_ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& item_ids) : LLInventoryFetchObserver(item_ids), - mNumTries(MAX_NUM_NOTIFICATIONS_TO_PROCESS) + mNumTries(MAX_NUM_ATTEMPTS_TO_PROCESS) { } void LLInventoryFetchItemsObserver::changed(U32 mask) { - BOOL any_items_missing = FALSE; - // scan through the incomplete items and move or erase them as // appropriate. if (!mIncomplete.empty()) { + // if period of an attempt expired... + if (mFetchingPeriod.hasExpired()) + { + // ...reset timer and reduce count of attempts + mFetchingPeriod.reset(); + mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY); + + --mNumTries; + + LL_INFOS("InventoryFetch") << "LLInventoryFetchItemsObserver: " << this << ", attempt(s) left: " << (S32)mNumTries << LL_ENDL; + } + + // do we still have any attempts? + bool timeout_expired = mNumTries <= 0; + for (uuid_vec_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) { const LLUUID& item_id = (*it); LLViewerInventoryItem* item = gInventory.getItem(item_id); if (!item) { - any_items_missing = TRUE; - if (mNumTries > 0) + if (timeout_expired) { - // Keep trying. - ++it; + // Just concede that this item hasn't arrived in reasonable time and continue on. + LL_WARNS("InventoryFetch") << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL; + it = mIncomplete.erase(it); } else { - // Just concede that this item hasn't arrived in reasonable time and continue on. - llwarns << "Fetcher timed out when fetching inventory item assetID:" << item_id << llendl; - it = mIncomplete.erase(it); + // Keep trying. + ++it; } continue; } @@ -198,14 +206,10 @@ void LLInventoryFetchItemsObserver::changed(U32 mask) } ++it; } - if (any_items_missing) - { - mNumTries--; - } if (mIncomplete.empty()) { - mNumTries = MAX_NUM_NOTIFICATIONS_TO_PROCESS; + mNumTries = MAX_NUM_ATTEMPTS_TO_PROCESS; done(); } } @@ -315,6 +319,10 @@ void LLInventoryFetchItemsObserver::startFetch() item_entry["item_id"] = (*it); items_llsd.append(item_entry); } + + mFetchingPeriod.resetWithExpiry(FETCH_TIMER_EXPIRY); + mNumTries = MAX_NUM_ATTEMPTS_TO_PROCESS; + fetch_items_from_llsd(items_llsd); } diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 6d5a86a6fc..72c13f55c6 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -110,6 +110,22 @@ public: /*virtual*/ void changed(U32 mask); private: S8 mNumTries; // Number of times changed() was called without success + LLFrameTimer mFetchingPeriod; + + /** + * If the viewer gets a notification, your observer assumes + * that that notification is for itself and then tries to process + * the results. The notification could be for something else (e.g. + * you're fetching an item and a notification gets triggered because + * you renamed some other item). This counter is to specify how many + * periods of time to wait for before giving up. + */ + static const U32 MAX_NUM_ATTEMPTS_TO_PROCESS; + + /** + * Period of waiting a notification when requested items get added into inventory. + */ + static const F32 FETCH_TIMER_EXPIRY; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index d605d4430e..d09f729943 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -72,7 +72,7 @@ LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p) // public void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, - BOOL enabled, std::string& suffix) + BOOL enabled, const std::string& suffix) { //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl; @@ -268,7 +268,7 @@ LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition p LLScrollListItem* LLNameListCtrl::addNameItemRow( const LLNameListCtrl::NameItem& name_item, EAddPosition pos, - std::string& suffix) + const std::string& suffix) { LLUUID id = name_item.value().asUUID(); LLNameListItem* item = NULL; diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index 1c26ee5db4..ee71db7540 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -90,11 +90,11 @@ public: // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. void addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM, - BOOL enabled = TRUE, std::string& suffix = LLStringUtil::null); + BOOL enabled = TRUE, const std::string& suffix = LLStringUtil::null); void addNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); /*virtual*/ LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); - LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, std::string& suffix = LLStringUtil::null); + LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, const std::string& suffix = LLStringUtil::null); // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp index 848b595613..5652a98981 100644 --- a/indra/newview/lloutfitobserver.cpp +++ b/indra/newview/lloutfitobserver.cpp @@ -56,9 +56,9 @@ void LLOutfitObserver::changed(U32 mask) if (!gInventory.isInventoryUsable()) return; - bool panel_updated = checkCOF(); + bool COF_changed = checkCOF(); - if (!panel_updated) + if (!COF_changed) { checkBaseOutfit(); } @@ -87,6 +87,7 @@ bool LLOutfitObserver::checkCOF() mCOFLastVersion = cof_version; + // dirtiness state should be updated before sending signal LLAppearanceMgr::getInstance()->updateIsDirty(); mCOFChanged(); @@ -120,6 +121,16 @@ void LLOutfitObserver::checkBaseOutfit() } LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance(); + // dirtiness state should be updated before sending signal app_mgr.updateIsDirty(); mBOFChanged(); + + if (mLastOutfitDirtiness != app_mgr.isOutfitDirty()) + { + if(!app_mgr.isOutfitDirty()) + { + mCOFSaved(); + } + mLastOutfitDirtiness = app_mgr.isOutfitDirty(); + } } diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h index 4cb40ead15..a4b5fbe04a 100644 --- a/indra/newview/lloutfitobserver.h +++ b/indra/newview/lloutfitobserver.h @@ -48,6 +48,8 @@ public: virtual void changed(U32 mask); + void notifyOutfitLockChanged() { mOutfitLockChanged(); } + typedef boost::signals2::signal<void (void)> signal_t; void addBOFReplacedCallback(const signal_t::slot_type& cb) { mBOFReplaced.connect(cb); } @@ -56,6 +58,10 @@ public: void addCOFChangedCallback(const signal_t::slot_type& cb) { mCOFChanged.connect(cb); } + void addCOFSavedCallback(const signal_t::slot_type& cb) { mCOFSaved.connect(cb); } + + void addOutfitLockChangedCallback(const signal_t::slot_type& cb) { mOutfitLockChanged.connect(cb); } + protected: LLOutfitObserver(); @@ -73,10 +79,18 @@ protected: S32 mBaseOutfitLastVersion; + bool mLastOutfitDirtiness; + private: signal_t mBOFReplaced; signal_t mBOFChanged; signal_t mCOFChanged; + signal_t mCOFSaved; + + /** + * Signal for changing state of outfit lock. + */ + signal_t mOutfitLockChanged; }; #endif /* LL_OUTFITOBSERVER_H */ diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index a4ae957c76..e20b2e26be 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -53,6 +53,20 @@ static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y); +static const LLOutfitTabNameComparator OUTFIT_TAB_NAME_COMPARATOR; + +/*virtual*/ +bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const +{ + std::string name1 = tab1->getTitle(); + std::string name2 = tab2->getTitle(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; +} + ////////////////////////////////////////////////////////////////////////// class OutfitContextMenu : public LLListContextMenu @@ -158,6 +172,7 @@ LLOutfitsList::~LLOutfitsList() BOOL LLOutfitsList::postBuild() { mAccordion = getChild<LLAccordionCtrl>("outfits_accordion"); + mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); return TRUE; } @@ -328,7 +343,7 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) updateOutfitTab(*items_iter); } - mAccordion->arrange(); + mAccordion->sort(); } void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl) diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 44f6ec908b..bb516446d2 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -32,18 +32,34 @@ #ifndef LL_LLOUTFITSLIST_H #define LL_LLOUTFITSLIST_H +#include "llaccordionctrl.h" #include "llpanel.h" // newview #include "llinventorymodel.h" #include "llinventoryobserver.h" -class LLAccordionCtrl; class LLAccordionCtrlTab; class LLWearableItemsList; class LLListContextMenu; /** + * @class LLOutfitTabNameComparator + * + * Comparator of outfit tabs. + */ +class LLOutfitTabNameComparator : public LLAccordionCtrl::LLTabComparator +{ + LOG_CLASS(LLOutfitTabNameComparator); + +public: + LLOutfitTabNameComparator() {}; + virtual ~LLOutfitTabNameComparator() {}; + + /*virtual*/ bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const; +}; + +/** * @class LLOutfitsList * * A list of agents's outfits from "My Outfits" inventory category diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 8d0e3d4647..a1a9300ec2 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -51,6 +51,7 @@ #include "llagentwearables.h" #include "llscrollingpanelparam.h" #include "llradiogroup.h" +#include "llnotificationsutil.h" #include "llcolorswatch.h" #include "lltexturectrl.h" @@ -624,6 +625,7 @@ BOOL LLPanelEditWearable::postBuild() mDescTitle = getChild<LLTextBox>("description_text"); getChild<LLRadioGroup>("sex_radio")->setCommitCallback(boost::bind(&LLPanelEditWearable::onCommitSexChange, this)); + getChild<LLButton>("save_as_button")->setCommitCallback(boost::bind(&LLPanelEditWearable::onSaveAsButtonClicked, this)); // The following panels will be shown/hidden based on what wearable we're editing // body parts @@ -744,6 +746,28 @@ void LLPanelEditWearable::onRevertButtonClicked(void* userdata) panel->revertChanges(); } +void LLPanelEditWearable::onSaveAsButtonClicked() +{ + LLSD args; + args["DESC"] = mTextEditor->getText(); + + LLNotificationsUtil::add("SaveWearableAs", args, LLSD(), boost::bind(&LLPanelEditWearable::saveAsCallback, this, _1, _2)); +} + +void LLPanelEditWearable::saveAsCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + std::string wearable_name = response["message"].asString(); + LLStringUtil::trim(wearable_name); + if( !wearable_name.empty() ) + { + mTextEditor->setText(wearable_name); + saveChanges(); + } + } +} void LLPanelEditWearable::onCommitSexChange() { @@ -991,8 +1015,14 @@ void LLPanelEditWearable::showWearable(LLWearable* wearable, BOOL show) // storage for ordered list of visual params value_map_t sorted_params; getSortedParams(sorted_params, edit_group); - - buildParamList(panel_list, sorted_params, tab); + + LLJoint* jointp = gAgentAvatarp->getJoint( subpart_entry->mTargetJoint ); + if (!jointp) + { + jointp = gAgentAvatarp->getJoint("mHead"); + } + + buildParamList(panel_list, sorted_params, tab, jointp); updateScrollingPanelUI(); } @@ -1229,7 +1259,7 @@ void LLPanelEditWearable::getSortedParams(value_map_t &sorted_params, const std: } } -void LLPanelEditWearable::buildParamList(LLScrollingPanelList *panel_list, value_map_t &sorted_params, LLAccordionCtrlTab *tab) +void LLPanelEditWearable::buildParamList(LLScrollingPanelList *panel_list, value_map_t &sorted_params, LLAccordionCtrlTab *tab, LLJoint* jointp) { // sorted_params is sorted according to magnitude of effect from // least to greatest. Adding to the front of the child list @@ -1243,7 +1273,7 @@ void LLPanelEditWearable::buildParamList(LLScrollingPanelList *panel_list, value { LLPanel::Params p; p.name("LLScrollingPanelParam"); - LLScrollingPanelParam* panel_param = new LLScrollingPanelParam( p, NULL, (*it).second, TRUE, this->getWearable()); + LLScrollingPanelParam* panel_param = new LLScrollingPanelParam( p, NULL, (*it).second, TRUE, this->getWearable(), jointp); height = panel_list->addPanel( panel_param ); } } diff --git a/indra/newview/llpaneleditwearable.h b/indra/newview/llpaneleditwearable.h index 6b376fe3d9..54f729fa7a 100644 --- a/indra/newview/llpaneleditwearable.h +++ b/indra/newview/llpaneleditwearable.h @@ -48,6 +48,7 @@ class LLViewerVisualParam; class LLVisualParamHint; class LLViewerJointMesh; class LLAccordionCtrlTab; +class LLJoint; class LLPanelEditWearable : public LLPanel { @@ -72,6 +73,8 @@ public: static void onRevertButtonClicked(void* userdata); void onCommitSexChange(); + void onSaveAsButtonClicked(); + void saveAsCallback(const LLSD& notification, const LLSD& response); private: @@ -81,7 +84,7 @@ private: void updateScrollingPanelUI(); LLPanel* getPanel(LLWearableType::EType type); void getSortedParams(value_map_t &sorted_params, const std::string &edit_group); - void buildParamList(LLScrollingPanelList *panel_list, value_map_t &sorted_params, LLAccordionCtrlTab *tab); + void buildParamList(LLScrollingPanelList *panel_list, value_map_t &sorted_params, LLAccordionCtrlTab *tab, LLJoint* jointp); // update bottom bar buttons ("Save", "Revert", etc) void updateVerbs(); diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ea7410502d..e07d5c064b 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -198,6 +198,7 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() LLOutfitObserver& observer = LLOutfitObserver::instance(); observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this)); observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); + observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::update, this)); mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES); @@ -280,7 +281,7 @@ BOOL LLPanelOutfitEdit::postBuild() childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); mWearableListMaskCollector = new LLFindNonLinksByMask(ALL_ITEMS_MASK); - mWearableListTypeCollector = new LLFindWearablesOfType(LLWearableType::WT_NONE); + mWearableListTypeCollector = new LLFindActualWearablesOfType(LLWearableType::WT_NONE); mWearableItemsPanel = getChild<LLPanel>("filtered_wearables_panel"); mWearableItemsList = getChild<LLInventoryItemsList>("filtered_wearables_list"); @@ -494,35 +495,10 @@ void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) void LLPanelOutfitEdit::onEditWearableClicked(void) { - LLUUID id_to_edit = mCOFWearables->getSelectedUUID(); - LLViewerInventoryItem * item_to_edit = gInventory.getItem(id_to_edit); - - if (item_to_edit) + LLUUID selected_item_id = mCOFWearables->getSelectedUUID(); + if (selected_item_id.notNull()) { - // returns null if not a wearable (attachment, etc). - LLWearable* wearable_to_edit = gAgentWearables.getWearableFromAssetID(item_to_edit->getAssetUUID()); - if(wearable_to_edit) - { - bool can_modify = false; - bool is_complete = item_to_edit->isFinished(); - // if item_to_edit is a link, its properties are not appropriate, - // lets get original item with actual properties - LLViewerInventoryItem* original_item = gInventory.getItem(wearable_to_edit->getItemID()); - if(original_item) - { - can_modify = original_item->getPermissions().allowModifyBy(gAgentID); - is_complete = original_item->isFinished(); - } - - if (can_modify && is_complete) - { - LLSidepanelAppearance::editWearable(wearable_to_edit, getParent()); - if (mEditWearableBtn->getVisible()) - { - mEditWearableBtn->setVisible(FALSE); - } - } - } + gAgentWearables.editWearable(selected_item_id); } } @@ -666,12 +642,13 @@ void LLPanelOutfitEdit::updateCurrentOutfitName() void LLPanelOutfitEdit::updateVerbs() { bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull(); - mSaveComboBtn->setSaveBtnEnabled(outfit_is_dirty); + mSaveComboBtn->setSaveBtnEnabled(!outfit_locked && outfit_is_dirty); childSetEnabled(REVERT_BTN, outfit_is_dirty && has_baseoutfit); - mSaveComboBtn->setMenuItemEnabled("save_outfit", outfit_is_dirty); + mSaveComboBtn->setMenuItemEnabled("save_outfit", !outfit_locked && outfit_is_dirty); mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing")); diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 8836672f91..8b451c156c 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -209,6 +209,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() : LLOutfitObserver& observer = LLOutfitObserver::instance(); observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); + observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); } LLPanelOutfitsInventory::~LLPanelOutfitsInventory() @@ -522,7 +523,7 @@ void LLPanelOutfitsInventory::updateListCommands() { bool trash_enabled = isActionEnabled("delete"); bool wear_enabled = isActionEnabled("wear"); - bool make_outfit_enabled = isActionEnabled("make_outfit"); + bool make_outfit_enabled = isActionEnabled("save_outfit"); mListCommands->childSetEnabled("trash_btn", trash_enabled); mListCommands->childSetEnabled("wear_btn", wear_enabled); @@ -668,9 +669,12 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) return FALSE; } } - if (command_name == "make_outfit") + if (command_name == "save_outfit") { - return LLAppearanceMgr::getInstance()->isOutfitDirty(); + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); + bool outfit_dirty =LLAppearanceMgr::getInstance()->isOutfitDirty(); + // allow save only if outfit isn't locked and is dirty + return !outfit_locked && outfit_dirty; } if (command_name == "edit" || diff --git a/indra/newview/llpanelvoiceeffect.cpp b/indra/newview/llpanelvoiceeffect.cpp new file mode 100644 index 0000000000..fd470798ee --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.cpp @@ -0,0 +1,169 @@ +/** + * @file llpanelvoiceeffect.cpp + * @author Aimee + * @brief Panel to select Voice Morphs. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelvoiceeffect.h" + +#include "llcombobox.h" +#include "llfloaterreg.h" +#include "llpanel.h" +#include "lltrans.h" +#include "lltransientfloatermgr.h" +#include "llvoiceclient.h" + +static LLRegisterPanelClassWrapper<LLPanelVoiceEffect> t_panel_voice_effect("panel_voice_effect"); + +LLPanelVoiceEffect::LLPanelVoiceEffect() + : mVoiceEffectCombo(NULL) +{ + mCommitCallbackRegistrar.add("Voice.CommitVoiceEffect", boost::bind(&LLPanelVoiceEffect::onCommitVoiceEffect, this)); +} + +LLPanelVoiceEffect::~LLPanelVoiceEffect() +{ + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->removeControlView(combo_list_view); + + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLPanelVoiceEffect::postBuild() +{ + mVoiceEffectCombo = getChild<LLComboBox>("voice_effect"); + + // Need to tell LLTransientFloaterMgr about the combo list, otherwise it can't + // be clicked while in a docked floater as it extends outside the floater area. + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->addControlView(combo_list_view); + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + } + + update(true); + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////// +/// PRIVATE SECTION +////////////////////////////////////////////////////////////////////////// + +void LLPanelVoiceEffect::onCommitVoiceEffect() +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectCombo->setEnabled(false); + return; + } + + LLSD value = mVoiceEffectCombo->getValue(); + if (value.asInteger() == PREVIEW_VOICE_EFFECTS) + { + // Open the Voice Morph preview floater + LLFloaterReg::showInstance("voice_effect"); + } + else if (value.asInteger() == GET_VOICE_EFFECTS) + { + // Open the voice morphing info web page + LLWeb::loadURL(LLTrans::getString("voice_morphing_url")); + } + else + { + effect_interface->setVoiceEffect(value.asUUID()); + } + + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); +} + +// virtual +void LLPanelVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + update(effect_list_updated); +} + +void LLPanelVoiceEffect::update(bool list_updated) +{ + if (mVoiceEffectCombo) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (list_updated) + { + // Add the default "No Voice Morph" entry. + mVoiceEffectCombo->removeall(); + mVoiceEffectCombo->add(getString("no_voice_effect"), LLUUID::null); + mVoiceEffectCombo->addSeparator(); + + // Add entries for each Voice Morph. + const voice_effect_list_t& effect_list = effect_interface->getVoiceEffectList(); + if (!effect_list.empty()) + { + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + mVoiceEffectCombo->add(it->first, it->second, ADD_BOTTOM); + } + + mVoiceEffectCombo->addSeparator(); + } + + // Add the fixed entries to go to the preview floater or marketing page. + mVoiceEffectCombo->add(getString("preview_voice_effects"), PREVIEW_VOICE_EFFECTS); + mVoiceEffectCombo->add(getString("get_voice_effects"), GET_VOICE_EFFECTS); + } + + if (effect_interface && LLVoiceClient::instance().isVoiceWorking()) + { + // Select the current Voice Morph. + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); + mVoiceEffectCombo->setEnabled(true); + } + else + { + // If voice isn't working or Voice Effects are not supported disable the control. + mVoiceEffectCombo->setValue(LLUUID::null); + mVoiceEffectCombo->setEnabled(false); + } + } +} diff --git a/indra/newview/llpanelvoiceeffect.h b/indra/newview/llpanelvoiceeffect.h new file mode 100644 index 0000000000..b5bf2f05a8 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.h @@ -0,0 +1,73 @@ +/** + * @file llpanelvoiceeffect.h + * @author Aimee + * @brief Panel to select Voice Effects. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_PANELVOICEEFFECT_H +#define LL_PANELVOICEEFFECT_H + +#include "llpanel.h" +#include "llvoiceclient.h" + +class LLComboBox; + +class LLPanelVoiceEffect + : public LLPanel + , public LLVoiceEffectObserver +{ +public: + LOG_CLASS(LLPanelVoiceEffect); + + LLPanelVoiceEffect(); + virtual ~LLPanelVoiceEffect(); + + virtual BOOL postBuild(); + +private: + void onCommitVoiceEffect(); + void update(bool list_updated); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + // Fixed entries in the Voice Morph list + typedef enum e_voice_effect_combo_items + { + NO_VOICE_EFFECT = 0, + PREVIEW_VOICE_EFFECTS = 1, + GET_VOICE_EFFECTS = 2 + } EVoiceEffectComboItems; + + LLComboBox* mVoiceEffectCombo; +}; + + +#endif //LL_PANELVOICEEFFECT_H diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index f020ad9bc2..a27afeab7c 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -94,7 +94,7 @@ public: mAvalineCallers.insert(avaline_caller_id); } - void onChange() + void onParticipantsChanged() { uuid_set_t participant_uuids; LLVoiceClient::getInstance()->getParticipantList(participant_uuids); diff --git a/indra/newview/llscrollingpanelparam.cpp b/indra/newview/llscrollingpanelparam.cpp index 242af6981c..6f5238f0a1 100644 --- a/indra/newview/llscrollingpanelparam.cpp +++ b/indra/newview/llscrollingpanelparam.cpp @@ -56,7 +56,7 @@ const S32 LLScrollingPanelParam::PARAM_HINT_HEIGHT = 128; S32 LLScrollingPanelParam::sUpdateDelayFrames = 0; LLScrollingPanelParam::LLScrollingPanelParam( const LLPanel::Params& panel_params, - LLViewerJointMesh* mesh, LLViewerVisualParam* param, BOOL allow_modify, LLWearable* wearable ) + LLViewerJointMesh* mesh, LLViewerVisualParam* param, BOOL allow_modify, LLWearable* wearable, LLJoint* jointp ) : LLScrollingPanel( panel_params ), mParam(param), mAllowModify(allow_modify), @@ -73,9 +73,9 @@ LLScrollingPanelParam::LLScrollingPanelParam( const LLPanel::Params& panel_param F32 min_weight = param->getMinWeight(); F32 max_weight = param->getMaxWeight(); - mHintMin = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, min_weight); + mHintMin = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, min_weight, jointp); pos_x = getChild<LLViewBorder>("right_border")->getRect().mLeft + left_border->getBorderWidth(); - mHintMax = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, max_weight ); + mHintMax = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, max_weight, jointp ); mHintMin->setAllowsUpdates( FALSE ); mHintMax->setAllowsUpdates( FALSE ); diff --git a/indra/newview/llscrollingpanelparam.h b/indra/newview/llscrollingpanelparam.h index fe4ce07166..3cdfd188a8 100644 --- a/indra/newview/llscrollingpanelparam.h +++ b/indra/newview/llscrollingpanelparam.h @@ -42,12 +42,13 @@ class LLViewerVisualParam; class LLWearable; class LLVisualParamHint; class LLViewerVisualParam; +class LLJoint; class LLScrollingPanelParam : public LLScrollingPanel { public: LLScrollingPanelParam( const LLPanel::Params& panel_params, - LLViewerJointMesh* mesh, LLViewerVisualParam* param, BOOL allow_modify, LLWearable* wearable ); + LLViewerJointMesh* mesh, LLViewerVisualParam* param, BOOL allow_modify, LLWearable* wearable, LLJoint* jointp ); virtual ~LLScrollingPanelParam(); virtual void draw(); diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index ef7286b7b4..e23643da0b 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -189,7 +189,7 @@ void LLSidepanelAppearance::onVisibilityChange(const LLSD &new_visibility) { if ((mOutfitEdit && mOutfitEdit->getVisible()) || (mEditWearable && mEditWearable->getVisible())) { - if (!gAgentCamera.cameraCustomizeAvatar()) + if (!gAgentCamera.cameraCustomizeAvatar() && gSavedSettings.getBOOL("AppearanceCameraMovement")) { gAgentCamera.changeCameraToCustomizeAvatar(); } @@ -197,9 +197,10 @@ void LLSidepanelAppearance::onVisibilityChange(const LLSD &new_visibility) } else { - if (gAgentCamera.cameraCustomizeAvatar()) + if (gAgentCamera.cameraCustomizeAvatar() && gSavedSettings.getBOOL("AppearanceCameraMovement")) { gAgentCamera.changeCameraToDefault(); + gAgentCamera.resetView(); } } } @@ -325,6 +326,7 @@ void LLSidepanelAppearance::toggleOutfitEditPanel(BOOL visible, BOOL disable_cam else if (!disable_camera_switch && gSavedSettings.getBOOL("AppearanceCameraMovement") ) { gAgentCamera.changeCameraToDefault(); + gAgentCamera.resetView(); } } @@ -364,6 +366,7 @@ void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLWearable *we if (!disable_camera_switch && gSavedSettings.getBOOL("AppearanceCameraMovement") ) { gAgentCamera.changeCameraToDefault(); + gAgentCamera.resetView(); } } } diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index 29237946d2..ea7601517d 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -107,7 +107,7 @@ private: * So, method does not calculate difference between these list it only switches off already * switched on indicators and switches on indicators of voice channel participants */ - void onChange(); + void onParticipantsChanged(); /** * Changes state of indicators specified by LLUUIDs @@ -205,7 +205,7 @@ void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_ mSwitchedIndicatorsOn.clear(); } -void SpeakingIndicatorManager::onChange() +void SpeakingIndicatorManager::onParticipantsChanged() { LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 5628205dd4..ac419d8dc7 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -394,18 +394,21 @@ void LLStatusBar::setBalance(S32 balance) { std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance ); - LLButton* btn_buy_currency = getChild<LLButton>("buycurrency"); + LLTextBox* balance_box = getChild<LLTextBox>("balance"); LLStringUtil::format_map_t string_args; string_args["[AMT]"] = llformat("%s", money_str.c_str()); std::string label_str = getString("buycurrencylabel", string_args); - btn_buy_currency->setLabel(label_str); + balance_box->setValue(label_str); - // Resize the balance button so that the label fits it, and the button expands to the left. - // *TODO: LLButton should have an option where to expand. + // Resize the L$ balance background to be wide enough for your balance plus the buy button { - S32 saved_right = btn_buy_currency->getRect().mRight; - btn_buy_currency->autoResize(); - btn_buy_currency->translate(saved_right - btn_buy_currency->getRect().mRight, 0); + const S32 HPAD = 24; + LLRect balance_rect = balance_box->getTextBoundingRect(); + LLRect buy_rect = getChildView("buyL")->getRect(); + LLView* balance_bg_view = getChildView("balance_bg"); + LLRect balance_bg_rect = balance_bg_view->getRect(); + balance_bg_rect.mLeft = balance_bg_rect.mRight - (buy_rect.getWidth() + balance_rect.getWidth() + HPAD); + balance_bg_view->setShape(balance_bg_rect); } if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold"))) diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 1d74a7be8c..5d51e32515 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -165,6 +165,7 @@ void LLTexLayerSetBuffer::dumpTotalByteCount() void LLTexLayerSetBuffer::requestUpdate() { + conditionalRestartUploadTimer(); mNeedsUpdate = TRUE; // If we're in the middle of uploading a baked texture, we don't care about it any more. // When it's downloaded, ignore it. @@ -173,17 +174,26 @@ void LLTexLayerSetBuffer::requestUpdate() void LLTexLayerSetBuffer::requestUpload() { + conditionalRestartUploadTimer(); + mNeedsUpload = TRUE; + mNumLowresUploads = 0; + mUploadPending = TRUE; +} + +void LLTexLayerSetBuffer::conditionalRestartUploadTimer() +{ // If we requested a new upload but haven't even uploaded // a low res version of our last upload request, then // keep the timer ticking instead of resetting it. if (mNeedsUpload && (mNumLowresUploads == 0)) { + mNeedsUploadTimer.unpause(); + } + else + { mNeedsUploadTimer.reset(); + mNeedsUploadTimer.start(); } - mNeedsUpload = TRUE; - mNumLowresUploads = 0; - mUploadPending = TRUE; - mNeedsUploadTimer.unpause(); } void LLTexLayerSetBuffer::cancelUpload() @@ -307,11 +317,26 @@ BOOL LLTexLayerSetBuffer::render() return success; } -bool LLTexLayerSetBuffer::isInitialized(void) const +BOOL LLTexLayerSetBuffer::isInitialized(void) const { return mGLTexturep.notNull() && mGLTexturep->isGLTextureCreated(); } +BOOL LLTexLayerSetBuffer::uploadPending() const +{ + return mUploadPending; +} + +BOOL LLTexLayerSetBuffer::uploadNeeded() const +{ + return mNeedsUpload; +} + +BOOL LLTexLayerSetBuffer::uploadInProgress() const +{ + return !mUploadID.isNull(); +} + BOOL LLTexLayerSetBuffer::isReadyToUpload() const { if (!mNeedsUpload) return FALSE; // Don't need to upload if we haven't requested one. @@ -353,40 +378,33 @@ BOOL LLTexLayerSetBuffer::updateImmediate() void LLTexLayerSetBuffer::readBackAndUpload() { - // pointers for storing data to upload - U8* baked_color_data = new U8[ mFullWidth * mFullHeight * 4 ]; - - glReadPixels(mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data ); - stop_glerror(); - - llinfos << "Baked " << mTexLayerSet->getBodyRegionName() << llendl; + llinfos << "Uploading baked " << mTexLayerSet->getBodyRegionName() << llendl; LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TEX_BAKES); - // We won't need our caches since we're baked now. (Techically, we won't - // really be baked until this image is sent to the server and the Avatar - // Appearance message is received.) + // Don't need caches since we're baked now. (note: we won't *really* be baked + // until this image is sent to the server and the Avatar Appearance message is received.) mTexLayerSet->deleteCaches(); - LLGLSUIDefault gls_ui; + // Get the COLOR information from our texture + U8* baked_color_data = new U8[ mFullWidth * mFullHeight * 4 ]; + glReadPixels(mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data ); + stop_glerror(); + // Get the MASK information from our texture + LLGLSUIDefault gls_ui; LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mFullWidth, mFullHeight, 1 ); U8* baked_mask_data = baked_mask_image->getData(); - mTexLayerSet->gatherMorphMaskAlpha(baked_mask_data, mFullWidth, mFullHeight); - // writes into baked_color_data - const char* comment_text = NULL; - S32 baked_image_components = 5; // red green blue [bump] clothing + // Create the baked image from our color and mask information + const S32 baked_image_components = 5; // red green blue [bump] clothing LLPointer<LLImageRaw> baked_image = new LLImageRaw( mFullWidth, mFullHeight, baked_image_components ); U8* baked_image_data = baked_image->getData(); - - comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask - S32 i = 0; - for( S32 u = 0; u < mFullWidth; u++ ) + for (S32 u=0; u < mFullWidth; u++) { - for( S32 v = 0; v < mFullHeight; v++ ) + for (S32 v=0; v < mFullHeight; v++) { baked_image_data[5*i + 0] = baked_color_data[4*i + 0]; baked_image_data[5*i + 1] = baked_color_data[4*i + 1]; @@ -399,21 +417,19 @@ void LLTexLayerSetBuffer::readBackAndUpload() LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C; compressedImage->setRate(0.f); - LLTransactionID tid; - LLAssetID asset_id; - tid.generate(); - asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - BOOL res = false; - if( compressedImage->encode(baked_image, comment_text)) + const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask) + if (compressedImage->encode(baked_image, comment_text)) { - res = LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(), - gVFS, asset_id, LLAssetType::AT_TEXTURE); - if (res) + LLTransactionID tid; + tid.generate(); + const LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + if (LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(), + gVFS, asset_id, LLAssetType::AT_TEXTURE)) { - LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; + // Read back the file and validate. BOOL valid = FALSE; - S32 file_size; + LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; + S32 file_size = 0; U8* data = LLVFile::readFile(gVFS, asset_id, LLAssetType::AT_TEXTURE, &file_size); if (data) { @@ -424,31 +440,26 @@ void LLTexLayerSetBuffer::readBackAndUpload() integrity_test->setLastError("Unable to read entire file"); } - if( valid ) + if (valid) { - // baked_upload_data is owned by the responder and deleted after the request completes + // Baked_upload_data is owned by the responder and deleted after the request completes. LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, this->mTexLayerSet, asset_id); mUploadID = asset_id; - const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); - - // upload the image - std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture"); + // Upload the image + const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture"); if(!url.empty() - && !LLPipeline::sForceOldBakedUpload) // Toggle the debug setting UploadBakedTexOld to change between the new caps method and old method + && !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method { - llinfos << "Baked texture upload via capability of " << mUploadID << " to " << url << llendl; - LLSD body = LLSD::emptyMap(); + // The responder will call LLTexLayerSetBuffer::onTextureUploadComplete() LLHTTPClient::post(url, body, new LLSendTexLayerResponder(body, mUploadID, LLAssetType::AT_TEXTURE, baked_upload_data)); - // Responder will call LLTexLayerSetBuffer::onTextureUploadComplete() + llinfos << "Baked texture upload via capability of " << mUploadID << " to " << url << llendl; } else { - llinfos << "Baked texture upload via Asset Store." << llendl; - // gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE); gAssetStorage->storeAssetData(tid, LLAssetType::AT_TEXTURE, LLTexLayerSetBuffer::onTextureUploadComplete, @@ -456,49 +467,53 @@ void LLTexLayerSetBuffer::readBackAndUpload() TRUE, // temp_file TRUE, // is_priority TRUE); // store_local + llinfos << "Baked texture upload via Asset Store." << llendl; } - if (gSavedSettings.getBOOL("DebugAvatarRezTime")) - { - std::string lod_str = highest_lod ? "HighRes" : "LowRes"; - LLSD args; - args["EXISTENCE"] = llformat("%d",(U32)mTexLayerSet->getAvatar()->debugGetExistenceTimeElapsedF32()); - args["TIME"] = llformat("%d",(U32)mNeedsUploadTimer.getElapsedTimeF32()); - args["BODYREGION"] = mTexLayerSet->getBodyRegionName(); - args["RESOLUTION"] = lod_str; - LLNotificationsUtil::add("AvatarRezSelfBakeNotification",args); - llinfos << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << llendl; - } - + const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); if (highest_lod) { - // Sending the final LOD for the baked texture. - // All done, pause the upload timer so we know how long it took. + // Sending the final LOD for the baked texture. All done, pause + // the upload timer so we know how long it took. mNeedsUpload = FALSE; mNeedsUploadTimer.pause(); } else { - // Sending a lower level LOD for the baked texture. - // Restart the upload timer. + // Sending a lower level LOD for the baked texture. Restart the upload timer. mNumLowresUploads++; mNeedsUploadTimer.unpause(); mNeedsUploadTimer.reset(); } + + // Print out notification that we uploaded this texture. + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + std::string lod_str = highest_lod ? "HighRes" : "LowRes"; + LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mTexLayerSet->getAvatar()->debugGetExistenceTimeElapsedF32()); + args["TIME"] = llformat("%d",(U32)mNeedsUploadTimer.getElapsedTimeF32()); + args["BODYREGION"] = mTexLayerSet->getBodyRegionName(); + args["RESOLUTION"] = lod_str; + LLNotificationsUtil::add("AvatarRezSelfBakeNotification",args); + llinfos << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << llendl; + } } else { + // The read back and validate operation failed. Remove the uploaded file. mUploadPending = FALSE; - llinfos << "unable to create baked upload file: corrupted" << llendl; LLVFile file(gVFS, asset_id, LLAssetType::AT_TEXTURE, LLVFile::WRITE); file.remove(); + llinfos << "Unable to create baked upload file (reason: corrupted)." << llendl; } } } - if (!res) + else { + // The VFS write file operation failed. mUploadPending = FALSE; - llinfos << "unable to create baked upload file" << llendl; + llinfos << "Unable to create baked upload file (reason: failed to write file)" << llendl; } delete [] baked_color_data; @@ -2277,10 +2292,15 @@ const std::string LLTexLayerSetBuffer::dumpTextureInfo() const const BOOL is_high_res = !mNeedsUpload; const U32 num_low_res = mNumLowresUploads; const U32 upload_time = (U32)mNeedsUploadTimer.getElapsedTimeF32(); - const BOOL is_uploaded = !mUploadPending; const std::string local_texture_info = gAgentAvatarp->debugDumpLocalTextureDataInfo(mTexLayerSet); - std::string text = llformat("[ HiRes:%d LoRes:%d Uploaded:%d ] [ Timer:%d ] %s", - is_high_res, num_low_res, is_uploaded, + + std::string status = "CREATING "; + if (!uploadNeeded()) status = "DONE "; + if (uploadInProgress()) status = "UPLOADING"; + + std::string text = llformat("[%s] [HiRes:%d LoRes:%d] [Elapsed:%d] %s", + status.c_str(), + is_high_res, num_low_res, upload_time, local_texture_info.c_str()); return text; diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h index 2ee609fe60..cb2e1faaa6 100644 --- a/indra/newview/lltexlayer.h +++ b/indra/newview/lltexlayer.h @@ -275,12 +275,16 @@ public: virtual void postRender(BOOL success); virtual BOOL render(); BOOL updateImmediate(); - bool isInitialized(void) const; + + BOOL isInitialized(void) const; + BOOL uploadPending() const; // We are expecting a new texture to be uploaded at some point + BOOL uploadNeeded() const; // We need to upload a new texture + BOOL uploadInProgress() const; // We have started uploading a new texture and are awaiting the result + /*virtual*/ BOOL needsRender(); void requestUpdate(); void requestUpload(); void cancelUpload(); - BOOL uploadPending() const { return mUploadPending; } BOOL render(S32 x, S32 y, S32 width, S32 height); void readBackAndUpload(); static void onTextureUploadComplete(const LLUUID& uuid, @@ -290,15 +294,19 @@ public: const std::string dumpTextureInfo() const; virtual void restoreGLTexture(); virtual void destroyGLTexture(); + + protected: void pushProjection() const; void popProjection() const; BOOL isReadyToUpload() const; + void conditionalRestartUploadTimer(); + private: LLTexLayerSet* const mTexLayerSet; BOOL mNeedsUpdate; // whether we need to update our baked textures BOOL mNeedsUpload; // whether we need to send our baked textures to the server - U32 mNumLowresUploads; // mumber of times we've sent a lowres version of our baked textures to the server + U32 mNumLowresUploads; // number of times we've sent a lowres version of our baked textures to the server BOOL mUploadPending; // whether we have received back the new baked textures LLUUID mUploadID; // the current upload process (null if none). Used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit. static S32 sGLByteCount; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index cf3bce2ec1..52d227f827 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -329,11 +329,7 @@ public: partial = true; } } - else - { - worker->setGetStatus(status, reason); -// llwarns << status << ": " << reason << llendl; - } + if (!success) { worker->setGetStatus(status, reason); @@ -753,17 +749,22 @@ bool LLTextureFetchWorker::doWork(S32 param) if (region) { - std::string http_url = region->getCapability("GetTexture"); + std::string http_url = region->getHttpUrl() ; if (!http_url.empty()) { mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. } + else + { + mCanUseHTTP = false ; + } } else { // This will happen if not logged in or if a region deoes not have HTTP Texture enabled //llwarns << "Region not found for host: " << mHost << llendl; + mCanUseHTTP = false; } } if (mCanUseHTTP && !mUrl.empty()) @@ -907,7 +908,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mGetStatus == HTTP_NOT_FOUND) { mHTTPFailCount = max_attempts = 1; // Don't retry - //llwarns << "Texture missing from server (404): " << mUrl << llendl; + llwarns << "Texture missing from server (404): " << mUrl << llendl; //roll back to try UDP if(mCanUseNET) @@ -927,6 +928,17 @@ bool LLTextureFetchWorker::doWork(S32 param) max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } + else if(mGetStatus >= HTTP_MULTIPLE_CHOICES && mGetStatus < HTTP_BAD_REQUEST) //http re-direct + { + ++mHTTPFailCount; + max_attempts = 5 ; //try at most 5 times to avoid infinite redirection loop. + + llwarns << "HTTP GET failed because of redirection: " << mUrl + << " Status: " << mGetStatus << " Reason: '" << mGetReason << llendl ; + + //assign to the new url + mUrl = mGetReason ; + } else { const S32 HTTP_MAX_RETRY_COUNT = 3; @@ -936,6 +948,7 @@ bool LLTextureFetchWorker::doWork(S32 param) << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } + if (mHTTPFailCount >= max_attempts) { if (cur_size > 0) diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 377449cc8b..8ea4dbeb04 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -418,10 +418,10 @@ void LLAvatarTexBar::draw() const S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); const S32 v_offset = 0; + const S32 l_offset = 3; //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; - LLColor4 text_color(1.f, 1.f, 1.f, 1.f); LLColor4 color; U32 line_num = 1; @@ -434,19 +434,36 @@ void LLAvatarTexBar::draw() if (!layerset) continue; const LLTexLayerSetBuffer *layerset_buffer = layerset->getComposite(); if (!layerset_buffer) continue; + + LLColor4 text_color = LLColor4::white; + + if (layerset_buffer->uploadNeeded()) + { + text_color = LLColor4::red; + } + if (layerset_buffer->uploadInProgress()) + { + text_color = LLColor4::magenta; + } std::string text = layerset_buffer->dumpTextureInfo(); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*line_num, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, l_offset, v_offset + line_height*line_num, text_color, LLFontGL::LEFT, LLFontGL::TOP); //, LLFontGL::BOLD, LLFontGL::DROP_SHADOW_SOFT); line_num++; } const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureTimeout"); const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); + LLColor4 header_color(1.f, 1.f, 1.f, 0.9f); + const std::string texture_timeout_str = texture_timeout ? llformat("%d",texture_timeout) : "Disabled"; const std::string override_tex_discard_level_str = override_tex_discard_level ? llformat("%d",override_tex_discard_level) : "Disabled"; std::string header_text = llformat("[ Timeout('AvatarBakedTextureTimeout'):%s ] [ LOD_Override('TextureDiscardLevel'):%s ]", texture_timeout_str.c_str(), override_tex_discard_level_str.c_str()); - LLFontGL::getFontMonospace()->renderUTF8(header_text, 0, 0, v_offset + line_height*line_num, - text_color, LLFontGL::LEFT, LLFontGL::TOP); //, LLFontGL::BOLD, LLFontGL::DROP_SHADOW_SOFT); + LLFontGL::getFontMonospace()->renderUTF8(header_text, 0, l_offset, v_offset + line_height*line_num, + header_color, LLFontGL::LEFT, LLFontGL::TOP); //, LLFontGL::BOLD, LLFontGL::DROP_SHADOW_SOFT); + line_num++; + std::string section_text = "Avatar Textures Information:"; + LLFontGL::getFontMonospace()->renderUTF8(section_text, 0, 0, v_offset + line_height*line_num, + header_color, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::BOLD, LLFontGL::DROP_SHADOW_SOFT); } BOOL LLAvatarTexBar::handleMouseDown(S32 x, S32 y, MASK mask) @@ -457,7 +474,7 @@ BOOL LLAvatarTexBar::handleMouseDown(S32 x, S32 y, MASK mask) LLRect LLAvatarTexBar::getRequiredRect() { LLRect rect; - rect.mTop = 85; + rect.mTop = 100; if (!gSavedSettings.getBOOL("DebugAvatarRezTime")) rect.mTop = 0; return rect; } diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index bc77ac5fd1..c862c02b82 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1878,24 +1878,7 @@ EAcceptance LLToolDragAndDrop::dad3dWearItem( LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded"); return ACCEPT_NO; } - - if (mSource == SOURCE_LIBRARY) - { - // create item based on that one, and put it on if that - // was a success. - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar( item ); - } + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(),true, !(mask & MASK_CONTROL)); } return ACCEPT_YES_MULTI; } diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index c1dc1de5e5..fa21b1a866 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -79,7 +79,8 @@ LLVisualParamHint::LLVisualParamHint( LLViewerJointMesh *mesh, LLViewerVisualParam *param, LLWearable *wearable, - F32 param_weight) + F32 param_weight, + LLJoint* jointp) : LLViewerDynamicTexture(width, height, 3, LLViewerDynamicTexture::ORDER_MIDDLE, TRUE ), mNeedsUpdate( TRUE ), @@ -91,11 +92,11 @@ LLVisualParamHint::LLVisualParamHint( mAllowsUpdates( TRUE ), mDelayFrames( 0 ), mRect( pos_x, pos_y + height, pos_x + width, pos_y ), - mLastParamWeight(0.f) + mLastParamWeight(0.f), + mCamTargetJoint(jointp) { LLVisualParamHint::sInstances.insert( this ); - mBackgroundp = LLUI::getUIImage("avatar_thumb_bkgrnd.j2c"); - + mBackgroundp = LLUI::getUIImage("avatar_thumb_bkgrnd.png"); llassert(width != 0); llassert(height != 0); @@ -196,21 +197,6 @@ BOOL LLVisualParamHint::render() mNeedsUpdate = FALSE; mIsVisible = TRUE; - LLViewerJointMesh* cam_target_joint = NULL; - const std::string& cam_target_mesh_name = mVisualParam->getCameraTargetName(); - if( !cam_target_mesh_name.empty() ) - { - cam_target_joint = (LLViewerJointMesh*)gAgentAvatarp->getJoint( cam_target_mesh_name ); - } - if( !cam_target_joint ) - { - cam_target_joint = (LLViewerJointMesh*)gMorphView->getCameraTargetJoint(); - } - if( !cam_target_joint ) - { - cam_target_joint = (LLViewerJointMesh*)gAgentAvatarp->getJoint("mHead"); - } - LLQuaternion avatar_rotation; LLJoint* root_joint = gAgentAvatarp->getRootJoint(); if( root_joint ) @@ -218,7 +204,7 @@ BOOL LLVisualParamHint::render() avatar_rotation = root_joint->getWorldRotation(); } - LLVector3 target_joint_pos = cam_target_joint->getWorldPosition(); + LLVector3 target_joint_pos = mCamTargetJoint->getWorldPosition(); LLVector3 target_offset( 0, 0, mVisualParam->getCameraElevation() ); LLVector3 target_pos = target_joint_pos + (target_offset * avatar_rotation); @@ -234,9 +220,9 @@ BOOL LLVisualParamHint::render() LLViewerCamera::getInstance()->setAspect((F32)mFullWidth / (F32)mFullHeight); LLViewerCamera::getInstance()->setOriginAndLookAt( - camera_pos, // camera - LLVector3(0.f, 0.f, 1.f), // up - target_pos ); // point of interest + camera_pos, // camera + LLVector3::z_axis, // up + target_pos ); // point of interest LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h index 3bffefaa55..cbab5e765f 100644 --- a/indra/newview/lltoolmorph.h +++ b/indra/newview/lltoolmorph.h @@ -47,6 +47,7 @@ class LLViewerJointMesh; class LLPolyMesh; class LLViewerObject; +class LLJoint; //----------------------------------------------------------------------------- // LLVisualParamHint @@ -63,7 +64,8 @@ public: LLViewerJointMesh *mesh, LLViewerVisualParam *param, LLWearable *wearable, - F32 param_weight); + F32 param_weight, + LLJoint* jointp); /*virtual*/ S8 getType() const ; @@ -96,6 +98,7 @@ protected: S32 mDelayFrames; // updates are blocked for this many frames LLRect mRect; F32 mLastParamWeight; + LLJoint* mCamTargetJoint; // joint to target with preview camera LLUIImagePtr mBackgroundp; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 49ea0348f9..efe59744bc 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -103,6 +103,7 @@ #include "llfloateruipreview.h" #include "llfloaterurldisplay.h" #include "llfloatervoicedevicesettings.h" +#include "llfloatervoiceeffect.h" #include "llfloaterwater.h" #include "llfloaterwhitelistentry.h" #include "llfloaterwindlight.h" @@ -255,7 +256,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("upload_sound", "floater_sound_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundPreview>, "upload"); LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLCallFloater>); - + LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterVoiceEffect>); + LLFloaterReg::add("whitelist_entry", "floater_whitelist_entry.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWhiteListEntry>); LLFloaterWindowSizeUtil::registerFloater(); LLFloaterReg::add("world_map", "floater_world_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWorldMap>); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d745356dcd..40f15fe86a 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -875,7 +875,7 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item) LLViewerInventoryItem *item = gInventory.getItem(inv_item); if (item) { - wear_inventory_item_on_avatar(item); + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item, true, mReplace); } } @@ -883,12 +883,8 @@ void ModifiedCOFCallback::fire(const LLUUID& inv_item) { LLAppearanceMgr::instance().updateAppearanceFromCOF(); - if (LLSideTray::getInstance()->isPanelActive("sidepanel_appearance")) - { - // *HACK: Edit the wearable that has just been worn - // only if the Appearance SP is currently opened. - LLAgentWearables::editWearable(inv_item); - } + // Start editing the item if previously requested. + gAgentWearables.editWearableIfRequested(inv_item); // TODO: camera mode may not be changed if a debug setting is tweaked if( gAgentCamera.cameraCustomizeAvatar() ) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 8ab7c9710d..d0d3ad693e 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -243,7 +243,13 @@ public: class WearOnAvatarCallback : public LLInventoryCallback { +public: + WearOnAvatarCallback(bool do_replace = false) : mReplace(do_replace) {} + void fire(const LLUUID& inv_item); + +protected: + bool mReplace; }; class ModifiedCOFCallback : public LLInventoryCallback diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a1b909c609..2c7ae539ce 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -7495,10 +7495,13 @@ class LLEditTakeOff : public view_listener_t else { LLWearableType::EType type = LLWearableType::typeNameToType(clothing); - if (type >= LLWearableType::WT_SHAPE && type < LLWearableType::WT_COUNT) + if (type >= LLWearableType::WT_SHAPE + && type < LLWearableType::WT_COUNT + && (gAgentWearables.getWearableCount(type) > 0)) { - // MULTI-WEARABLES - LLViewerInventoryItem *item = dynamic_cast<LLViewerInventoryItem*>(gAgentWearables.getWearableInventoryItem(type,0)); + // MULTI-WEARABLES: assuming user wanted to remove top shirt. + U32 wearable_index = gAgentWearables.getWearableCount(type) - 1; + LLViewerInventoryItem *item = dynamic_cast<LLViewerInventoryItem*>(gAgentWearables.getWearableInventoryItem(type,wearable_index)); LLWearableBridge::removeItemFromAvatar(item); } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index c48668df9a..da240cedbb 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -216,6 +216,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mColoName("unknown"), mProductSKU("unknown"), mProductName("unknown"), + mHttpUrl(""), mCacheLoaded(FALSE), mCacheEntriesCount(0), mCacheID(), @@ -1555,6 +1556,10 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u else { mCapabilities[name] = url; + if(name == "GetTexture") + { + mHttpUrl = url ; + } } } diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 5c4d5a61fd..a9e7ef771c 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -291,6 +291,7 @@ public: friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion ®ion); /// implements LLCapabilityProvider virtual std::string getDescription() const; + std::string getHttpUrl() const { return mHttpUrl ;} LLSpatialPartition* getSpatialPartition(U32 type); public: @@ -383,6 +384,7 @@ private: std::string mColoName; std::string mProductSKU; std::string mProductName; + std::string mHttpUrl ; // Maps local ids to cache entries. diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index d8a9ce9374..7e779986df 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1447,8 +1447,14 @@ void LLViewerFetchedTexture::setKnownDrawSize(S32 width, S32 height) //virtual void LLViewerFetchedTexture::processTextureStats() { - if(mFullyLoaded)//already loaded + if(mFullyLoaded) { + if(mDesiredDiscardLevel <= mMinDesiredDiscardLevel)//already loaded + { + return ; + } + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel) ; + mFullyLoaded = FALSE ; return ; } @@ -1482,6 +1488,7 @@ void LLViewerFetchedTexture::processTextureStats() mDesiredDiscardLevel = (S8)llmin(log((F32)mFullWidth / mKnownDrawWidth) / log_2, log((F32)mFullHeight / mKnownDrawHeight) / log_2) ; mDesiredDiscardLevel = llclamp(mDesiredDiscardLevel, (S8)0, (S8)getMaxDiscardLevel()) ; + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel) ; } mKnownDrawSizeChanged = FALSE ; @@ -1514,7 +1521,7 @@ F32 LLViewerFetchedTexture::calcDecodePriority() } if(mFullyLoaded && !mForceToSaveRawImage)//already loaded for static texture { - return -4.0f ; //alreay fetched + return -1.0f ; //alreay fetched } S32 cur_discard = getCurrentDiscardLevelForFetching(); @@ -1528,16 +1535,16 @@ F32 LLViewerFetchedTexture::calcDecodePriority() } else if(mDesiredDiscardLevel >= cur_discard && cur_discard > -1) { - priority = -1.0f ; + priority = -2.0f ; } else if(mCachedRawDiscardLevel > -1 && mDesiredDiscardLevel >= mCachedRawDiscardLevel) { - priority = -1.0f; + priority = -3.0f; } else if (mDesiredDiscardLevel > getMaxDiscardLevel()) { // Don't decode anything we don't need - priority = -1.0f; + priority = -4.0f; } else if ((mBoostLevel == LLViewerTexture::BOOST_UI || mBoostLevel == LLViewerTexture::BOOST_ICON) && !have_all_data) { @@ -1551,9 +1558,14 @@ F32 LLViewerFetchedTexture::calcDecodePriority() // Always want high boosted images priority = 1.f; } + else if(mForceToSaveRawImage) + { + //force to fetch the raw image. + priority = 1.f; + } else { - priority = -1.f; //stop fetching + priority = -5.f; //stop fetching } } else if (cur_discard < 0) @@ -1569,7 +1581,7 @@ F32 LLViewerFetchedTexture::calcDecodePriority() else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel)) { // larger mips are corrupted - priority = -3.0f; + priority = -6.0f; } else { @@ -2052,10 +2064,13 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() bool run_raw_callbacks = false; bool need_readback = false; + mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); iter != mLoadedCallbackList.end(); ) { LLLoadedCallbackEntry *entryp = *iter++; + mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)entryp->mDesiredDiscard) ; + if (entryp->mNeedsImageRaw) { if (mNeedsAux) @@ -2187,6 +2202,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() if (mLoadedCallbackList.empty()) { gTextureList.mCallbackList.erase(this); + mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; } // Done with any raw image data at this point (will be re-created if we still have callbacks) diff --git a/indra/newview/llviewervisualparam.cpp b/indra/newview/llviewervisualparam.cpp index 422e530dc6..1dc09a64ac 100644 --- a/indra/newview/llviewervisualparam.cpp +++ b/indra/newview/llviewervisualparam.cpp @@ -99,8 +99,6 @@ BOOL LLViewerVisualParamInfo::parseXml(LLXmlTreeNode *node) node->getFastAttributeF32( camera_angle_string, mCamAngle ); // in degrees static LLStdStringHandle camera_elevation_string = LLXmlTree::addAttributeString("camera_elevation"); node->getFastAttributeF32( camera_elevation_string, mCamElevation ); - static LLStdStringHandle camera_target_string = LLXmlTree::addAttributeString("camera_target"); - node->getFastAttributeString( camera_target_string, mCamTargetName ); mCamAngle += 180; diff --git a/indra/newview/llviewervisualparam.h b/indra/newview/llviewervisualparam.h index 1a3975eb99..f38c01fa6c 100644 --- a/indra/newview/llviewervisualparam.h +++ b/indra/newview/llviewervisualparam.h @@ -60,7 +60,6 @@ protected: F32 mCamDist; F32 mCamAngle; // degrees F32 mCamElevation; - std::string mCamTargetName; F32 mEditGroupDisplayOrder; BOOL mShowSimple; // show edit controls when in "simple ui" mode? F32 mSimpleMin; // when in simple UI, apply this minimum, range 0.f to 100.f @@ -104,7 +103,6 @@ public: F32 getCameraDistance() const { return getInfo()->mCamDist; } F32 getCameraAngle() const { return getInfo()->mCamAngle; } // degrees F32 getCameraElevation() const { return getInfo()->mCamElevation; } - const std::string& getCameraTargetName() const { return getInfo()->mCamTargetName; } BOOL getShowSimple() const { return getInfo()->mShowSimple; } F32 getSimpleMin() const { return getInfo()->mSimpleMin; } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 462c442954..eae92f8992 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1273,7 +1273,8 @@ BOOL LLVOAvatarSelf::isLocalTextureDataAvailable(const LLTexLayerSet* layerset) //----------------------------------------------------------------------------- BOOL LLVOAvatarSelf::isLocalTextureDataFinal(const LLTexLayerSet* layerset) const { - const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); + //const U32 desired_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); + const U32 desired_tex_discard_level = 0; // SERAPH hack to not bake textures on lower discard levels. for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { @@ -1289,7 +1290,7 @@ BOOL LLVOAvatarSelf::isLocalTextureDataFinal(const LLTexLayerSet* layerset) cons const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { - if (getLocalDiscardLevel(*local_tex_iter, wearable_index) > (S32)(override_tex_discard_level)) + if (getLocalDiscardLevel(*local_tex_iter, wearable_index) > (S32)(desired_tex_discard_level)) { return FALSE; } @@ -1304,7 +1305,8 @@ BOOL LLVOAvatarSelf::isLocalTextureDataFinal(const LLTexLayerSet* layerset) cons BOOL LLVOAvatarSelf::isAllLocalTextureDataFinal() const { - const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); + // const U32 desired_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); + const U32 desired_tex_discard_level = 0; // SERAPH hack to not bake textures on lower discard levels for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { @@ -1318,7 +1320,7 @@ BOOL LLVOAvatarSelf::isAllLocalTextureDataFinal() const const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { - if (getLocalDiscardLevel(*local_tex_iter, wearable_index) > (S32)(override_tex_discard_level)) + if (getLocalDiscardLevel(*local_tex_iter, wearable_index) > (S32)(desired_tex_discard_level)) { return FALSE; } @@ -1334,7 +1336,7 @@ BOOL LLVOAvatarSelf::isBakedTextureFinal(const LLVOAvatarDefines::EBakedTextureI if (!layerset) return FALSE; const LLTexLayerSetBuffer *layerset_buffer = layerset->getComposite(); if (!layerset_buffer) return FALSE; - return !layerset_buffer->uploadPending(); + return !layerset_buffer->uploadNeeded(); } BOOL LLVOAvatarSelf::isTextureDefined(LLVOAvatarDefines::ETextureIndex type, U32 index) const @@ -1882,7 +1884,7 @@ const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLTexLayer if (layerset == mBakedTextureDatas[baked_index].mTexLayerSet) { const LLVOAvatarDictionary::BakedEntry *baked_dict = baked_iter->second; - text += llformat("[%d] '%s' ( ",baked_index, baked_dict->mName.c_str()); + text += llformat("%d-%s ( ",baked_index, baked_dict->mName.c_str()); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) @@ -1975,37 +1977,47 @@ BOOL LLVOAvatarSelf::canGrabBakedTexture(EBakedTextureIndex baked_index) const ++iter) { const ETextureIndex t_index = (*iter); - lldebugs << "Checking index " << (U32) t_index << llendl; - // MULTI-WEARABLE: old method. replace. - const LLUUID& texture_id = getTEImage( t_index )->getID(); - if (texture_id != IMG_DEFAULT_AVATAR) - { - // Search inventory for this texture. - LLViewerInventoryCategory::cat_array_t cats; - LLViewerInventoryItem::item_array_t items; - LLAssetIDMatches asset_id_matches(texture_id); - gInventory.collectDescendentsIf(LLUUID::null, - cats, - items, - LLInventoryModel::INCLUDE_TRASH, - asset_id_matches); - - BOOL can_grab = FALSE; - lldebugs << "item count for asset " << texture_id << ": " << items.count() << llendl; - if (items.count()) + LLWearableType::EType wearable_type = LLVOAvatarDictionary::getTEWearableType(t_index); + U32 count = gAgentWearables.getWearableCount(wearable_type); + lldebugs << "Checking index " << (U32) t_index << " count: " << count << llendl; + + for (U32 wearable_index = 0; wearable_index < count; ++wearable_index) + { + LLWearable *wearable = gAgentWearables.getWearable(wearable_type, wearable_index); + if (wearable) { - // search for full permissions version - for (S32 i = 0; i < items.count(); i++) + const LLLocalTextureObject *texture = wearable->getLocalTextureObject((S32)t_index); + const LLUUID& texture_id = texture->getID(); + if (texture_id != IMG_DEFAULT_AVATAR) { - LLViewerInventoryItem* itemp = items[i]; - if (itemp->getIsFullPerm()) + // Search inventory for this texture. + LLViewerInventoryCategory::cat_array_t cats; + LLViewerInventoryItem::item_array_t items; + LLAssetIDMatches asset_id_matches(texture_id); + gInventory.collectDescendentsIf(LLUUID::null, + cats, + items, + LLInventoryModel::INCLUDE_TRASH, + asset_id_matches); + + BOOL can_grab = FALSE; + lldebugs << "item count for asset " << texture_id << ": " << items.count() << llendl; + if (items.count()) { - can_grab = TRUE; - break; + // search for full permissions version + for (S32 i = 0; i < items.count(); i++) + { + LLViewerInventoryItem* itemp = items[i]; + if (itemp->getIsFullPerm()) + { + can_grab = TRUE; + break; + } + } } + if (!can_grab) return FALSE; } } - if (!can_grab) return FALSE; } } diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 1b4471a9fe..070663e22f 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -897,9 +897,9 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s else { LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; - // In case of incoming AvaLine call generated URI will be differ from original one. - // This is because Avatar-2-Avatar URI is based on avatar UUID but Avaline is not. - // See LLVoiceClient::sessionAddedEvent() -> setUUIDFromStringHash() + // In the case of an incoming AvaLine call, the generated URI will be different from the + // original one. This is because the P2P URI is based on avatar UUID but Avaline is not. + // See LLVoiceClient::sessionAddedEvent() setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); } diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 573fab1f4f..074f9b8bba 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -113,7 +113,7 @@ protected: void doSetState(const EState& state); void setURI(std::string uri); - // there can be two directions ICOMING and OUTGOING + // there can be two directions INCOMING and OUTGOING EDirection mCallDirection; std::string mURI; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 42e44634b6..e8635d7f1a 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -35,6 +35,7 @@ #include "llviewerwindow.h" #include "llvoicevivox.h" #include "llviewernetwork.h" +#include "llcommandhandler.h" #include "llhttpnode.h" #include "llnotificationsutil.h" #include "llsdserialize.h" @@ -46,6 +47,39 @@ const F32 LLVoiceClient::VOLUME_MIN = 0.f; const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f; const F32 LLVoiceClient::VOLUME_MAX = 1.0f; + +// Support for secondlife:///app/voice SLapps +class LLVoiceHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLVoiceHandler() : LLCommandHandler("voice", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + if (params[0].asString() == "effects") + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + // If the voice client doesn't support voice effects, we can't handle effects SLapps + if (!effect_interface) + { + return false; + } + + // Support secondlife:///app/voice/effects/refresh to update the voice effect list with new effects + if (params[1].asString() == "refresh") + { + effect_interface->refreshVoiceEffectLists(false); + return true; + } + } + return false; + } +}; +LLVoiceHandler gVoiceHandler; + + + std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) { std::string result = "UNKNOWN"; @@ -77,13 +111,14 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv - /////////////////////////////////////////////////////////////////////////////////////////////// LLVoiceClient::LLVoiceClient() : mVoiceModule(NULL), - m_servicePump(NULL) + m_servicePump(NULL), + mVoiceEffectEnabled(LLCachedControl<bool>(gSavedSettings, "VoiceMorphingEnabled")), + mVoiceEffectDefault(LLCachedControl<std::string>(gSavedPerAccountSettings, "VoiceEffectDefault")) { } @@ -567,7 +602,7 @@ std::string LLVoiceClient::getDisplayName(const LLUUID& id) } } -bool LLVoiceClient::isVoiceWorking() +bool LLVoiceClient::isVoiceWorking() const { if (mVoiceModule) { @@ -710,6 +745,10 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id) } } +LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const +{ + return getVoiceEffectEnabled() ? dynamic_cast<LLVoiceEffectInterface*>(mVoiceModule) : NULL; +} /////////////////// // version checking diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index e08fed7ae9..0e3d9a5435 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -42,6 +42,7 @@ class LLVOAvatar; #include "llviewerregion.h" #include "llcallingcard.h" // for LLFriendObserver #include "llsecapi.h" +#include "llcontrol.h" // devices @@ -52,7 +53,7 @@ class LLVoiceClientParticipantObserver { public: virtual ~LLVoiceClientParticipantObserver() { } - virtual void onChange() = 0; + virtual void onParticipantsChanged() = 0; }; @@ -109,7 +110,7 @@ public: virtual void updateSettings()=0; // call after loading settings and whenever they change - virtual bool isVoiceWorking()=0; // connected to a voice server and voice channel + virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel virtual const LLVoiceVersionInfo& getVersion()=0; @@ -217,8 +218,6 @@ public: ////////////////////////// /// @name nearby speaker accessors //@{ - - virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar virtual std::string getDisplayName(const LLUUID& id)=0; virtual BOOL isOnlineSIP(const LLUUID &id)=0; @@ -261,6 +260,63 @@ public: }; +////////////////////////////////// +/// @class LLVoiceEffectObserver +class LLVoiceEffectObserver +{ +public: + virtual ~LLVoiceEffectObserver() { } + virtual void onVoiceEffectChanged(bool effect_list_updated) = 0; +}; + +typedef std::multimap<const std::string, const LLUUID, LLDictionaryLess> voice_effect_list_t; + +////////////////////////////////// +/// @class LLVoiceEffectInterface +/// @brief Voice effect module interface +/// +/// Voice effect modules should provide an implementation for this interface. +///////////////////////////////// + +class LLVoiceEffectInterface +{ +public: + LLVoiceEffectInterface() {} + virtual ~LLVoiceEffectInterface() {} + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id) = 0; + virtual const LLUUID getVoiceEffect() = 0; + virtual LLSD getVoiceEffectProperties(const LLUUID& id) = 0; + + virtual void refreshVoiceEffectLists(bool clear_lists) = 0; + virtual const voice_effect_list_t &getVoiceEffectList() const = 0; + virtual const voice_effect_list_t &getVoiceEffectTemplateList() const = 0; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer) = 0; + virtual void removeObserver(LLVoiceEffectObserver* observer) = 0; + //@} + + ////////////////////////////// + /// @name Preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable) = 0; + virtual void recordPreviewBuffer() = 0; + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) = 0; + virtual void stopPreviewBuffer() = 0; + + virtual bool isPreviewRecording() = 0; + virtual bool isPreviewPlaying() = 0; + //@} +}; + + class LLVoiceClient: public LLSingleton<LLVoiceClient> { LOG_CLASS(LLVoiceClient); @@ -281,7 +337,7 @@ public: void updateSettings(); // call after loading settings and whenever they change - bool isVoiceWorking(); // connected to a voice server and voice channel + bool isVoiceWorking() const; // connected to a voice server and voice channel // tuning void tuningStart(); @@ -403,10 +459,23 @@ public: void removeObserver(LLVoiceClientParticipantObserver* observer); std::string sipURIFromID(const LLUUID &id); - + + ////////////////////////// + /// @name Voice effects + //@{ + bool getVoiceEffectEnabled() const { return mVoiceEffectEnabled; }; + LLUUID getVoiceEffectDefault() const { return LLUUID(mVoiceEffectDefault); }; + + // Returns NULL if voice effects are not supported, or not enabled. + LLVoiceEffectInterface* getVoiceEffectInterface() const; + //@} + protected: LLVoiceModuleInterface* mVoiceModule; LLPumpIO *m_servicePump; + + LLCachedControl<bool> mVoiceEffectEnabled; + LLCachedControl<std::string> mVoiceEffectDefault; }; /** diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index c6c155f0f0..96bde129ee 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -60,6 +60,7 @@ #include "llviewerparcelmgr.h" //#include "llfirstuse.h" #include "llspeakers.h" +#include "lltrans.h" #include "llviewerwindow.h" #include "llviewercamera.h" @@ -67,15 +68,11 @@ #include "llviewernetwork.h" #include "llnotificationsutil.h" +#include "stringize.h" + // for base64 decoding #include "apr_base64.h" -// for SHA1 hash -#include "apr_sha1.h" - -// for MD5 hash -#include "llmd5.h" - #define USE_SESSION_GROUPS 0 const F32 VOLUME_SCALE_VIVOX = 0.01f; @@ -100,14 +97,15 @@ const int MAX_LOGIN_RETRIES = 12; // blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; +// How often to check for expired voice fonts in seconds +const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; +// Time of day at which Vivox expires voice font subscriptions. +// Used to replace the time portion of received expiry timestamps. +static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; + +// Maximum length of capture buffer recordings in seconds. +const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; -static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) -{ - LLMD5 md5_uuid; - md5_uuid.update((const unsigned char*)str.data(), str.size()); - md5_uuid.finalize(); - md5_uuid.raw_digest(uuid.mData); -} static int scale_mic_volume(float volume) { @@ -325,6 +323,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mBuddyListMapPopulated(false), mBlockRulesListReceived(false), mAutoAcceptRulesListReceived(false), + mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), @@ -348,10 +347,17 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceEnabled(false), mWriteInProgress(false), - mLipSyncEnabled(false) - + mLipSyncEnabled(false), + mVoiceFontsReceived(false), + mVoiceFontsNew(false), + mVoiceFontListDirty(false), + mCaptureBufferMode(false), + mCaptureBufferRecording(false), + mCaptureBufferRecorded(false), + mCaptureBufferPlaying(false), + mPlayRequestCount(0) { mSpeakerVolume = scale_speaker_volume(0); @@ -654,6 +660,11 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateMicTuningStart); CASE(stateMicTuningRunning); CASE(stateMicTuningStop); + CASE(stateCaptureBufferPaused); + CASE(stateCaptureBufferRecStart); + CASE(stateCaptureBufferRecording); + CASE(stateCaptureBufferPlayStart); + CASE(stateCaptureBufferPlaying); CASE(stateConnectorStart); CASE(stateConnectorStarting); CASE(stateConnectorStarted); @@ -662,6 +673,8 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); + CASE(stateVoiceFontsWait); + CASE(stateVoiceFontsReceived); CASE(stateCreatingSessionGroup); CASE(stateNoChannel); CASE(stateJoiningSession); @@ -775,8 +788,10 @@ void LLVivoxVoiceClient::stateMachine() // Clean up and reset everything. closeSocket(); deleteAllSessions(); - deleteAllBuddies(); - + deleteAllBuddies(); + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + mConnectorHandle.clear(); mAccountHandle.clear(); mAccountPassword.clear(); @@ -1126,8 +1141,97 @@ void LLVivoxVoiceClient::stateMachine() } break; - - //MARK: stateConnectorStart + + //MARK: stateCaptureBufferPaused + case stateCaptureBufferPaused: + if (!mCaptureBufferMode) + { + // Leaving capture mode. + + mCaptureBufferRecording = false; + mCaptureBufferRecorded = false; + mCaptureBufferPlaying = false; + + // Return to stateNoChannel to trigger reconnection to a channel. + setState(stateNoChannel); + } + else if (mCaptureBufferRecording) + { + setState(stateCaptureBufferRecStart); + } + else if (mCaptureBufferPlaying) + { + setState(stateCaptureBufferPlayStart); + } + break; + + //MARK: stateCaptureBufferRecStart + case stateCaptureBufferRecStart: + captureBufferRecordStartSendMessage(); + + // Flag that something is recorded to allow playback. + mCaptureBufferRecorded = true; + + // Start the timer, recording will be stopped when it expires. + mCaptureTimer.start(); + mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME); + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferRecording); + break; + + //MARK: stateCaptureBufferRecording + case stateCaptureBufferRecording: + if (!mCaptureBufferMode || !mCaptureBufferRecording || + mCaptureBufferPlaying || mCaptureTimer.hasExpired()) + { + // Stop recording + captureBufferRecordStopSendMessage(); + mCaptureBufferRecording = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPaused); + } + break; + + //MARK: stateCaptureBufferPlayStart + case stateCaptureBufferPlayStart: + captureBufferPlayStartSendMessage(mPreviewVoiceFont); + + // Store the voice font being previewed, so that we know to restart if it changes. + mPreviewVoiceFontLast = mPreviewVoiceFont; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPlaying); + break; + + //MARK: stateCaptureBufferPlaying + case stateCaptureBufferPlaying: + if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast) + { + // If the preview voice font changes, restart playing with the new font. + setState(stateCaptureBufferPlayStart); + } + else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording) + { + // Stop playing. + captureBufferPlayStopSendMessage(); + mCaptureBufferPlaying = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPaused); + } + break; + + //MARK: stateConnectorStart case stateConnectorStart: if(!mVoiceEnabled) { @@ -1222,6 +1326,18 @@ void LLVivoxVoiceClient::stateMachine() notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + // Request the set of available voice fonts. + setState(stateVoiceFontsWait); + refreshVoiceEffectLists(true); + } + else + { + // If voice effects are disabled, pretend we've received them and carry on. + setState(stateVoiceFontsReceived); + } + // request the current set of block rules (we'll need them when updating the friends list) accountListBlockRulesSendMessage(); @@ -1253,12 +1369,25 @@ void LLVivoxVoiceClient::stateMachine() writeString(stream.str()); } } + break; + + //MARK: stateVoiceFontsWait + case stateVoiceFontsWait: // Await voice font list + // accountGetSessionFontsResponse() will transition from here to + // stateVoiceFontsReceived, to ensure we have the voice font list + // before attempting to create a session. + break; + //MARK: stateVoiceFontsReceived + case stateVoiceFontsReceived: // Voice font list received + // Set up the timer to check for expiring voice fonts + mVoiceFontExpiryTimer.start(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + #if USE_SESSION_GROUPS // create the main session group - sessionGroupCreateSendMessage(); - setState(stateCreatingSessionGroup); + sessionGroupCreateSendMessage(); #else // Not using session groups -- skip the stateCreatingSessionGroup state. setState(stateNoChannel); @@ -1306,6 +1435,10 @@ void LLVivoxVoiceClient::stateMachine() mTuningExitState = stateNoChannel; setState(stateMicTuningStart); } + else if(mCaptureBufferMode) + { + setState(stateCaptureBufferPaused); + } else if(sessionNeedsRelog(mNextAudioSession)) { requestRelog(); @@ -1316,6 +1449,7 @@ void LLVivoxVoiceClient::stateMachine() sessionState *oldSession = mAudioSession; mAudioSession = mNextAudioSession; + mAudioSessionChanged = true; if(!mAudioSession->mReconnect) { mNextAudioSession = NULL; @@ -1478,6 +1612,13 @@ void LLVivoxVoiceClient::stateMachine() enforceTether(); } + // Do notifications for expiring Voice Fonts. + if (mVoiceFontExpiryTimer.hasExpired()) + { + expireVoiceFonts(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + } + // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often // -- the user can only click so fast) or every 10hz, whichever is sooner. // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. @@ -1547,6 +1688,8 @@ void LLVivoxVoiceClient::stateMachine() mAccountHandle.clear(); deleteAllSessions(); deleteAllBuddies(); + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); if(mVoiceEnabled && !mRelogRequested) { @@ -1627,15 +1770,15 @@ void LLVivoxVoiceClient::stateMachine() } - if(mAudioSession && mAudioSession->mParticipantsChanged) + if (mAudioSessionChanged) { - mAudioSession->mParticipantsChanged = false; - mAudioSessionChanged = true; + mAudioSessionChanged = false; + notifyParticipantObservers(); + notifyVoiceFontObservers(); } - - if(mAudioSessionChanged) + else if (mAudioSession && mAudioSession->mParticipantsChanged) { - mAudioSessionChanged = false; + mAudioSession->mParticipantsChanged = false; notifyParticipantObservers(); } } @@ -1751,8 +1894,11 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage() void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1776,10 +1922,11 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>" << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; } - + stream << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Name>" << mChannelName << "</Name>" << "</Request>\n\n\n"; writeString(stream.str()); @@ -1787,8 +1934,11 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1814,6 +1964,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session << "<Name>" << mChannelName << "</Name>" << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Password>" << password << "</Password>" << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>" << "</Request>\n\n\n" @@ -1824,7 +1975,10 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) { - LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; + LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; session->mMediaConnectInProgress = true; @@ -1834,6 +1988,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">" << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" << "<SessionHandle>" << session->mHandle << "</SessionHandle>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Media>Audio</Media>" << "</Request>\n\n\n"; @@ -3156,7 +3311,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( else { LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - setUUIDFromStringHash(session->mCallerID, session->mSIPURI); + session->mCallerID.generate(session->mSIPURI); session->mSynthesizedCallerID = true; // Can't look up the name in this case -- we have to extract it from the URI. @@ -3434,6 +3589,26 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( } } +void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +{ + if (mediaCompletionType == "AuxBufferAudioCapture") + { + mCaptureBufferRecording = false; + } + else if (mediaCompletionType == "AuxBufferAudioRender") + { + // Ignore all but the last stop event + if (--mPlayRequestCount <= 0) + { + mCaptureBufferPlaying = false; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + } +} + void LLVivoxVoiceClient::mediaStreamUpdatedEvent( std::string &sessionHandle, std::string &sessionGroupHandle, @@ -4142,8 +4317,8 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti else { // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This tells code in LLVivoxVoiceClient that the ID will not be in the name cache. - setUUIDFromStringHash(result->mAvatarID, uri); + // This indicates that the ID will not be in the name cache. + result->mAvatarID.generate(uri); } } @@ -4638,7 +4813,7 @@ BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) return result; } -bool LLVivoxVoiceClient::isVoiceWorking() +bool LLVivoxVoiceClient::isVoiceWorking() const { //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) // Condition with joining spatial num was added to take into account possible problems with connection to voice @@ -5659,7 +5834,12 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri result = new sessionState(); result->mSIPURI = uri; result->mHandle = handle; - + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + } + mSessions.insert(result); if(!result->mHandle.empty()) @@ -6084,8 +6264,8 @@ void LLVivoxVoiceClient::notifyParticipantObservers() ) { LLVoiceClientParticipantObserver* observer = *it; - observer->onChange(); - // In case onChange() deleted an entry. + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. it = mParticipantObservers.upper_bound(observer); } } @@ -6248,6 +6428,660 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string } } +bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) +{ + if (!mAudioSession) + { + return false; + } + + if (!id.isNull()) + { + if (mVoiceFontMap.empty()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + } + + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + + return true; +} + +const LLUUID LLVivoxVoiceClient::getVoiceEffect() +{ + return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; +} + +LLSD LLVivoxVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + +LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE), + mIsNew(false) +{ + mExpiryTimer.stop(); + mExpiryWarningTimer.stop(); +} + +LLVivoxVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLVivoxVoiceClient::refreshVoiceEffectLists(bool clear_lists) +{ + if (clear_lists) + { + mVoiceFontsReceived = false; + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + } + + accountGetSessionFontsSendMessage(); + accountGetTemplateFontsSendMessage(); +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectList() const +{ + return mVoiceFontList; +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectTemplateList() const +{ + return mVoiceFontTemplateList; +} + +void LLVivoxVoiceClient::addVoiceFont(const S32 font_index, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font) +{ + // Vivox SessionFontIDs are not guaranteed to remain the same between + // sessions or grids so use a UUID for the name. + + // If received name is not a UUID, fudge one by hashing the name and type. + LLUUID font_id; + if (LLUUID::validate(name)) + { + font_id = LLUUID(name); + } + else + { + font_id.generate(STRINGIZE(font_type << ":" << name)); + } + + voiceFontEntry *font = NULL; + + voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; + voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; + + // Check whether we've seen this font before. + voice_font_map_t::iterator iter = font_map.find(font_id); + bool new_font = (iter == font_map.end()); + + // Override the has_expired flag if we have passed the expiration_date as a double check. + if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) + { + has_expired = true; + } + + if (has_expired) + { + LL_DEBUGS("Voice") << "Expired " << (template_font ? "Template " : "") + << expiration_date.asString() << " " << font_id + << " (" << font_index << ") " << name << LL_ENDL; + + // Remove existing session fonts that have expired since we last saw them. + if (!new_font && !template_font) + { + deleteVoiceFont(font_id); + } + return; + } + + if (new_font) + { + // If it is a new font create a new entry. + font = new voiceFontEntry(font_id); + } + else + { + // Not a new font, update the existing entry + font = iter->second; + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" may be a UUID. + font->mName = description.empty() ? name : description; + font->mFontType = font_type; + font->mFontStatus = font_status; + + // If the font is new or the expiration date has changed the expiry timers need updating. + if (!template_font && (new_font || font->mExpirationDate != expiration_date)) + { + font->mExpirationDate = expiration_date; + + // Set the expiry timer to trigger a notification when the voice font can no longer be used. + font->mExpiryTimer.start(); + font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); + + // Set the warning timer to some interval before actual expiry. + S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + if (warning_time != 0) + { + font->mExpiryWarningTimer.start(); + F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); + font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); + } + else + { + // Disable the warning timer. + font->mExpiryWarningTimer.stop(); + } + + // Only flag new session fonts after the first time we have fetched the list. + if (mVoiceFontsReceived) + { + font->mIsNew = true; + mVoiceFontsNew = true; + } + } + + LL_DEBUGS("Voice") << (template_font ? "Template " : "") + << font->mExpirationDate.asString() << " " << font->mID + << " (" << font->mFontIndex << ") " << name << LL_ENDL; + + if (new_font) + { + font_map.insert(voice_font_map_t::value_type(font->mID, font)); + font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); + } + + mVoiceFontListDirty = true; + + // Debugging stuff + + if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font type: " << font_type << LL_ENDL; + } + if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font status: " << font_status << LL_ENDL; + } + } +} + +void LLVivoxVoiceClient::expireVoiceFonts() +{ + // *TODO: If we are selling voice fonts in packs, there are probably + // going to be a number of fonts with the same expiration time, so would + // be more efficient to just keep a list of expiration times rather + // than checking each font individually. + + bool have_expired = false; + bool will_expire = false; + bool expired_in_use = false; + + LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + voiceFontEntry* voice_font = iter->second; + LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; + LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; + + // Check for expired voice fonts + if (expiry_timer.getStarted() && expiry_timer.hasExpired()) + { + // Check whether it is the active voice font + if (voice_font->mID == current_effect) + { + // Reset to no voice effect. + setVoiceEffect(LLUUID::null); + expired_in_use = true; + } + + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; + deleteVoiceFont(voice_font->mID); + have_expired = true; + } + + // Check for voice fonts that will expire in less that the warning time + if (warning_timer.getStarted() && warning_timer.hasExpired()) + { + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; + will_expire = true; + warning_timer.stop(); + } + } + + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + + // Give a notification if any voice fonts have expired. + if (have_expired) + { + if (expired_in_use) + { + LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); + } + else + { + LLNotificationsUtil::add("VoiceEffectsExpired", args); + } + + // Refresh voice font lists in the UI. + notifyVoiceFontObservers(); + } + + // Give a warning notification if any voice fonts are due to expire. + if (will_expire) + { + S32 seconds = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + args["INTERVAL"] = llformat("%d", seconds / SEC_PER_DAY); + + LLNotificationsUtil::add("VoiceEffectsWillExpire", args); + } +} + +void LLVivoxVoiceClient::deleteVoiceFont(const LLUUID& id) +{ + // Remove the entry from the voice font list. + voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); + while (list_iter != mVoiceFontList.end()) + { + if (list_iter->second == id) + { + LL_DEBUGS("Voice") << "Removing " << id << " from the voice font list." << LL_ENDL; + mVoiceFontList.erase(list_iter++); + mVoiceFontListDirty = true; + } + else + { + ++list_iter; + } + } + + // Find the entry in the voice font map and erase its data. + voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); + if (map_iter != mVoiceFontMap.end()) + { + delete map_iter->second; + } + + // Remove the entry from the voice font map. + mVoiceFontMap.erase(map_iter); +} + +void LLVivoxVoiceClient::deleteAllVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLVivoxVoiceClient::deleteVoiceFontTemplates() +{ + mVoiceFontTemplateList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontTemplateMap.clear(); +} + +S32 LLVivoxVoiceClient::getVoiceFontIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); + if (it != mVoiceFontMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font list." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetSessionFonts.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font template list." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetTemplateFonts.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetVoiceFont.1\">" + << "<SessionHandle>" << session->mHandle << "</SessionHandle>" + << "<SessionFontID>" << font_index << "</SessionFontID>" + << "</Request>\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + if(getState() == stateVoiceFontsWait) + { + setState(stateVoiceFontsReceived); + } + + notifyVoiceFontObservers(); + mVoiceFontsReceived = true; +} + +void LLVivoxVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + notifyVoiceFontObservers(); +} +void LLVivoxVoiceClient::addObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyVoiceFontObservers() +{ + LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; + + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end(); + ) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(mVoiceFontListDirty); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } + mVoiceFontListDirty = false; + + // If new Voice Fonts have been added notify the user. + if (mVoiceFontsNew) + { + if(mVoiceFontsReceived) + { + LLNotificationsUtil::add("VoiceEffectsNew"); + } + mVoiceFontsNew = false; + } +} + +void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) +{ + mCaptureBufferMode = enable; + if(mCaptureBufferMode && getState() >= stateNoChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::recordPreviewBuffer() +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + mCaptureBufferRecording = true; +} + +void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + if (!mCaptureBufferRecorded) + { + // Can't play until we have something recorded! + mCaptureBufferPlaying = false; + return; + } + + mPreviewVoiceFont = effect_id; + mCaptureBufferPlaying = true; +} + +void LLVivoxVoiceClient::stopPreviewBuffer() +{ + mCaptureBufferRecording = false; + mCaptureBufferPlaying = false; +} + +bool LLVivoxVoiceClient::isPreviewRecording() +{ + return (mCaptureBufferMode && mCaptureBufferRecording); +} + +bool LLVivoxVoiceClient::isPreviewPlaying() +{ + return (mCaptureBufferMode && mCaptureBufferPlaying); +} + +void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() +{ if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; + + // Start capture + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.StartBufferCapture.1\">" + << "</Request>" + << "\n\n\n"; + + // Unmute the mic + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" + << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<Value>false</Value>" + << "</Request>\n\n\n"; + + // Dirty the PTT state so that it will get reset when we finishing previewing + mPTTDirty = true; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; + + // Mute the mic. PTT state was dirtied at recording start, so will be reset when finished previewing. + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" + << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<Value>true</Value>" + << "</Request>\n\n\n"; + + // Stop capture + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) +{ + if(!mAccountHandle.empty()) + { + // Track how may play requests are sent, so we know how many stop events to + // expect before play actually stops. + ++mPlayRequestCount; + + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; + + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); + LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.PlayAudioBuffer.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<TemplateFontID>" << font_index << "</TemplateFontID>" + << "<FontDelta />" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} LLVivoxProtocolParser::LLVivoxProtocolParser() { @@ -6466,7 +7300,30 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); } - + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("TemplateFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType.clear(); + } } } responseDepth++; @@ -6612,8 +7469,43 @@ void LLVivoxProtocolParser::EndTag(const char *tag) subscriptionHandle = string; else if (!stricmp("SubscriptionType", tag)) subscriptionType = string; - - + else if (!stricmp("SessionFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); + } + else if (!stricmp("TemplateFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); + } + else if (!stricmp("ID", tag)) + { + id = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDate = expiryTimeStampToLLDate(string); + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontType = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Status", tag)) + { + fontStatus = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType = string;; + } + textBuffer.clear(); accumulateText= false; @@ -6642,6 +7534,21 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length) // -------------------------------------------------------------------------------- +LLDate LLVivoxProtocolParser::expiryTimeStampToLLDate(const std::string& vivox_ts) +{ + // *HACK: Vivox reports the time incorrectly. LLDate also only parses a + // subset of valid ISO 8601 dates (only handles Z, not offsets). + // So just use the date portion and fix the time here. + std::string time_stamp = vivox_ts.substr(0, 10); + time_stamp += VOICE_FONT_EXPIRY_TIME; + + LL_DEBUGS("VivoxProtocolParser") << "Vivox timestamp " << vivox_ts << " modified to: " << time_stamp << LL_ENDL; + + return LLDate(time_stamp); +} + +// -------------------------------------------------------------------------------- + void LLVivoxProtocolParser::processResponse(std::string tag) { LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; @@ -6695,7 +7602,17 @@ void LLVivoxProtocolParser::processResponse(std::string tag) </Event> */ LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } + } + else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) + { + /* + <Event type="MediaCompletionEvent"> + <SessionGroupHandle /> + <MediaCompletionType>AuxBufferAudioCapture</MediaCompletionType> + </Event> + */ + LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); + } else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) { /* @@ -6756,6 +7673,9 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) { + // These are really spammy in tuning mode + squelchDebugOutput = true; + LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); } else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) @@ -6870,6 +7790,14 @@ void LLVivoxProtocolParser::processResponse(std::string tag) // We don't need to process these, but they're so spammy we don't want to log them. squelchDebugOutput = true; } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) + { + LLVivoxVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); + } /* else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) { diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 59fec8b954..f858f8f74e 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -57,15 +57,9 @@ class LLVivoxVoiceClientMuteListObserver; class LLVivoxVoiceClientFriendsObserver; -class LLVivoxVoiceClientParticipantObserver -{ -public: - virtual ~LLVivoxVoiceClientParticipantObserver() { } - virtual void onChange() = 0; -}; - - -class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public LLVoiceModuleInterface +class LLVivoxVoiceClient : public LLSingleton<LLVivoxVoiceClient>, + virtual public LLVoiceModuleInterface, + virtual public LLVoiceEffectInterface { LOG_CLASS(LLVivoxVoiceClient); public: @@ -84,7 +78,7 @@ public: virtual void updateSettings(); // call after loading settings and whenever they change // Returns true if vivox has successfully logged in and is not in error state - virtual bool isVoiceWorking(); + virtual bool isVoiceWorking() const; ///////////////////// /// @name Tuning @@ -232,15 +226,49 @@ public: virtual void removeObserver(LLFriendObserver* observer); virtual void addObserver(LLVoiceClientParticipantObserver* observer); virtual void removeObserver(LLVoiceClientParticipantObserver* observer); - - - //@} virtual std::string sipURIFromID(const LLUUID &id); //@} - + /// @name LLVoiceEffectInterface virtual implementations + /// @see LLVoiceEffectInterface + //@{ + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id); + virtual const LLUUID getVoiceEffect(); + virtual LLSD getVoiceEffectProperties(const LLUUID& id); + + virtual void refreshVoiceEffectLists(bool clear_lists); + virtual const voice_effect_list_t& getVoiceEffectList() const; + virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer); + virtual void removeObserver(LLVoiceEffectObserver* observer); + //@} + + ////////////////////////////// + /// @name Effect preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable); + virtual void recordPreviewBuffer(); + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); + virtual void stopPreviewBuffer(); + + virtual bool isPreviewRecording(); + virtual bool isPreviewPlaying(); + //@} + + //@} + + protected: ////////////////////// // Vivox Specific definitions @@ -278,14 +306,13 @@ protected: bool mIsSpeaking; bool mIsModeratorMuted; bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) - bool mVolumeSet; // true if incoming volume messages should not change the volume + bool mVolumeSet; // true if incoming volume messages should not change the volume bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) bool mAvatarIDValid; bool mIsSelf; }; typedef std::map<const std::string, participantState*> participantMap; - typedef std::map<const LLUUID, participantState*> participantUUIDMap; struct sessionState @@ -332,14 +359,17 @@ protected: bool mIncoming; bool mVoiceEnabled; bool mReconnect; // Whether we should try to reconnect to this session if it's dropped - // Set to true when the mute state of someone in the participant list changes. + + // Set to true when the volume/mute state of someone in the participant list changes. // The code will have to walk the list to find the changed participant(s). bool mVolumeDirty; - bool mMuteDirty; - + bool mMuteDirty; + bool mParticipantsChanged; participantMap mParticipantsByURI; participantUUIDMap mParticipantsByUUID; + + LLUUID mVoiceFontID; }; // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. @@ -356,6 +386,11 @@ protected: stateMicTuningStart, stateMicTuningRunning, stateMicTuningStop, + stateCaptureBufferPaused, + stateCaptureBufferRecStart, + stateCaptureBufferRecording, + stateCaptureBufferPlayStart, + stateCaptureBufferPlaying, stateConnectorStart, // connector needs to be started stateConnectorStarting, // waiting for connector handle stateConnectorStarted, // connector handle received @@ -364,6 +399,8 @@ protected: stateNeedsLogin, // send login request stateLoggingIn, // waiting for account handle stateLoggedIn, // account handle received + stateVoiceFontsWait, // Awaiting the list of voice fonts + stateVoiceFontsReceived, // List of voice fonts received stateCreatingSessionGroup, // Creating the main session group stateNoChannel, // stateJoiningSession, // waiting for session handle @@ -436,8 +473,6 @@ protected: void tuningCaptureStartSendMessage(int duration); void tuningCaptureStopSendMessage(); - bool inTuningStates(); - //---------------------------------- // devices void clearCaptureDevices(); @@ -464,6 +499,7 @@ protected: void connectorShutdownResponse(int statusCode, std::string &statusString); void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); @@ -591,8 +627,8 @@ protected: void deleteAllAutoAcceptRules(void); void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); void accountListBlockRulesResponse(int statusCode, const std::string &statusString); - void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); - + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + ///////////////////////////// // session control messages @@ -621,7 +657,21 @@ protected: void lookupName(const LLUUID &id); static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); void avatarNameResolved(const LLUUID &id, const std::string &name); - + + ///////////////////////////// + // Voice fonts + + void addVoiceFont(const S32 id, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font = false); + void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); + void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); + private: LLVoiceVersionInfo mVoiceVersion; @@ -804,6 +854,88 @@ private: typedef std::set<LLFriendObserver*> friend_observer_set_t; friend_observer_set_t mFriendObservers; void notifyFriendObservers(); + + // Voice Fonts + + void expireVoiceFonts(); + void deleteVoiceFont(const LLUUID& id); + void deleteAllVoiceFonts(); + void deleteVoiceFontTemplates(); + + S32 getVoiceFontIndex(const LLUUID& id) const; + S32 getVoiceFontTemplateIndex(const LLUUID& id) const; + + void accountGetSessionFontsSendMessage(); + void accountGetTemplateFontsSendMessage(); + void sessionSetVoiceFontSendMessage(sessionState *session); + + void notifyVoiceFontObservers(); + + typedef enum e_voice_font_type + { + VOICE_FONT_TYPE_NONE = 0, + VOICE_FONT_TYPE_ROOT = 1, + VOICE_FONT_TYPE_USER = 2, + VOICE_FONT_TYPE_UNKNOWN + } EVoiceFontType; + + typedef enum e_voice_font_status + { + VOICE_FONT_STATUS_NONE = 0, + VOICE_FONT_STATUS_FREE = 1, + VOICE_FONT_STATUS_NOT_FREE = 2, + VOICE_FONT_STATUS_UNKNOWN + } EVoiceFontStatus; + + struct voiceFontEntry + { + voiceFontEntry(LLUUID& id); + ~voiceFontEntry(); + + LLUUID mID; + S32 mFontIndex; + std::string mName; + LLDate mExpirationDate; + S32 mFontType; + S32 mFontStatus; + bool mIsNew; + + LLFrameTimer mExpiryTimer; + LLFrameTimer mExpiryWarningTimer; + }; + + bool mVoiceFontsReceived; + bool mVoiceFontsNew; + bool mVoiceFontListDirty; + voice_effect_list_t mVoiceFontList; + voice_effect_list_t mVoiceFontTemplateList; + + typedef std::map<const LLUUID, voiceFontEntry*> voice_font_map_t; + voice_font_map_t mVoiceFontMap; + voice_font_map_t mVoiceFontTemplateMap; + + typedef std::set<LLVoiceEffectObserver*> voice_font_observer_set_t; + voice_font_observer_set_t mVoiceFontObservers; + + LLFrameTimer mVoiceFontExpiryTimer; + + + // Audio capture buffer + + void captureBufferRecordStartSendMessage(); + void captureBufferRecordStopSendMessage(); + void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); + void captureBufferPlayStopSendMessage(); + + bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. + bool mCaptureBufferRecording; // A voice sample is being captured. + bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. + bool mCaptureBufferPlaying; // A voice sample is being played. + + LLTimer mCaptureTimer; + LLUUID mPreviewVoiceFont; + LLUUID mPreviewVoiceFontLast; + S32 mPlayRequestCount; }; /** @@ -890,7 +1022,13 @@ protected: int numberOfAliases; std::string subscriptionHandle; std::string subscriptionType; - + S32 id; + std::string descriptionString; + LLDate expirationDate; + bool hasExpired; + S32 fontType; + S32 fontStatus; + std::string mediaCompletionType; // Members for processing text between tags std::string textBuffer; @@ -907,11 +1045,9 @@ protected: void StartTag(const char *tag, const char **attr); void EndTag(const char *tag); void CharData(const char *buffer, int length); - + LLDate expiryTimeStampToLLDate(const std::string& vivox_ts); }; #endif //LL_VIVOX_VOICE_CLIENT_H - - diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp index 9e9b46473e..121e691710 100644 --- a/indra/newview/llwearable.cpp +++ b/indra/newview/llwearable.cpp @@ -656,7 +656,7 @@ void LLWearable::writeToAvatar() image_id = LLVOAvatarDictionary::getDefaultTextureImageID((ETextureIndex) te); } LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture( image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE ); - // MULTI-WEARABLE: replace hard-coded 0 + // MULTI-WEARABLE: assume index 0 will be used when writing to avatar. TODO: eliminate the need for this. gAgentAvatarp->setLocalTextureTE(te, image, 0); } } diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index edee30c2ed..6c410cf7a5 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -534,11 +534,13 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() const uuid_vec_t& ids = mUUIDs; // selected items IDs LLUUID selected_id = ids.front(); // ID of the first selected item - functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false); + functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, true); + functor_t add = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false); functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); // Register handlers common for all wearable types. registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids)); + registrar.add("Wearable.Add", boost::bind(handleMultiple, add, ids)); registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id)); registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); diff --git a/indra/newview/skins/default/textures/avatar_thumb_bkgrnd.png b/indra/newview/skins/default/textures/avatar_thumb_bkgrnd.png Binary files differnew file mode 100644 index 0000000000..84cc2159c1 --- /dev/null +++ b/indra/newview/skins/default/textures/avatar_thumb_bkgrnd.png diff --git a/indra/newview/skins/default/textures/map_infohub.tga b/indra/newview/skins/default/textures/map_infohub.tga Binary files differindex 545b8e532c..d0134fa5fe 100644 --- a/indra/newview/skins/default/textures/map_infohub.tga +++ b/indra/newview/skins/default/textures/map_infohub.tga diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index cee5f5cbca..85ccf0f564 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -669,7 +669,7 @@ with the same filename but different name <texture name="Progress_11" file_name="icons/Progress_11.png" preload="true" /> <texture name="Progress_12" file_name="icons/Progress_12.png" preload="true" /> - <texture name="bevel_background" file_name="widgets/bevel_background.png" preload="true" scale.left="12" scale.top="15" scale.right="120" scale.bottom="2"/> + <texture name="bevel_background" file_name="widgets/bevel_background.png" preload="true" scale.left="12" scale.top="15" scale.right="108" scale.bottom="2"/> <texture name="buy_off" file_name="widgets/buy_off.png" preload="true" scale.left="2" scale.top="15" scale.right="67" scale.bottom="4"/> <texture name="buy_over" file_name="widgets/buy_over.png" preload="true" scale.left="2" scale.top="15" scale.right="67" scale.bottom="4"/> <texture name="buy_press" file_name="widgets/buy_press.png" preload="true" scale.left="2" scale.top="15" scale.right="67" scale.bottom="4"/> diff --git a/indra/newview/skins/default/textures/widgets/buy_off.png b/indra/newview/skins/default/textures/widgets/buy_off.png Binary files differindex 961ad071d4..ee5979046f 100644 --- a/indra/newview/skins/default/textures/widgets/buy_off.png +++ b/indra/newview/skins/default/textures/widgets/buy_off.png diff --git a/indra/newview/skins/default/textures/widgets/buy_over.png b/indra/newview/skins/default/textures/widgets/buy_over.png Binary files differindex 0be19f8a31..93adb68c86 100644 --- a/indra/newview/skins/default/textures/widgets/buy_over.png +++ b/indra/newview/skins/default/textures/widgets/buy_over.png diff --git a/indra/newview/skins/default/textures/widgets/buy_press.png b/indra/newview/skins/default/textures/widgets/buy_press.png Binary files differindex d6f587464d..3f442d6eaa 100644 --- a/indra/newview/skins/default/textures/widgets/buy_press.png +++ b/indra/newview/skins/default/textures/widgets/buy_press.png diff --git a/indra/newview/skins/default/xui/da/floater_buy_land.xml b/indra/newview/skins/default/xui/da/floater_buy_land.xml index 970491b41f..f6ee78fa6d 100644 --- a/indra/newview/skins/default/xui/da/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/da/floater_buy_land.xml @@ -126,9 +126,6 @@ gennemført. <floater.string name="no_parcel_selected"> (intet parcel er valgt) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Region: </text> diff --git a/indra/newview/skins/default/xui/de/floater_buy_land.xml b/indra/newview/skins/default/xui/de/floater_buy_land.xml index 5708a3f80a..5369155cf9 100644 --- a/indra/newview/skins/default/xui/de/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/de/floater_buy_land.xml @@ -124,9 +124,6 @@ unterstützt [AMOUNT2] Objekte <floater.string name="no_parcel_selected"> (keine Parzelle ausgewählt) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Region: </text> diff --git a/indra/newview/skins/default/xui/de/panel_bottomtray.xml b/indra/newview/skins/default/xui/de/panel_bottomtray.xml index d52b8dcf4d..83f67344ca 100644 --- a/indra/newview/skins/default/xui/de/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/de/panel_bottomtray.xml @@ -9,7 +9,7 @@ <layout_stack name="toolbar_stack"> <layout_panel name="speak_panel"> <talk_button name="talk"> - <speak_button label="Sprechen" label_selected="Sprechen" name="speak_btn" halign="right" /> + <speak_button label="Sprechen" label_selected="Sprechen" name="speak_btn" /> </talk_button> </layout_panel> <layout_panel name="gesture_panel"> diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml index 9cef7b6d35..10cf34c170 100644 --- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml @@ -18,9 +18,6 @@ <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/de/panel_place_profile.xml b/indra/newview/skins/default/xui/de/panel_place_profile.xml index ed1421aa60..9d1a582b7c 100644 --- a/indra/newview/skins/default/xui/de/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/de/panel_place_profile.xml @@ -41,21 +41,6 @@ <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> - <string name="icon_Voice" value="parcel_drk_Voice"/> - <string name="icon_VoiceNo" value="parcel_drk_VoiceNo"/> - <string name="icon_Fly" value="parcel_drk_Fly"/> - <string name="icon_FlyNo" value="parcel_drk_FlyNo"/> - <string name="icon_Push" value="parcel_drk_Push"/> - <string name="icon_PushNo" value="parcel_drk_PushNo"/> - <string name="icon_Build" value="parcel_drk_Build"/> - <string name="icon_BuildNo" value="parcel_drk_BuildNo"/> - <string name="icon_Scripts" value="parcel_drk_Scripts"/> - <string name="icon_ScriptsNo" value="parcel_drk_ScriptsNo"/> - <string name="icon_Damage" value="parcel_drk_Damage"/> - <string name="icon_DamageNo" value="parcel_drk_DamageNo"/> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index df44b61632..acaa508792 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -174,12 +174,15 @@ supports [AMOUNT2] objects </floater.string> <floater.string name="icon_PG" + translate="false" value="Parcel_PG_Dark"/> <floater.string name="icon_M" + translate="false" value="Parcel_M_Dark"/> <floater.string name="icon_R" + translate="false" value="Parcel_R_Dark"/> <text type="string" diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index f537c81860..c9b013099b 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -33,7 +33,6 @@ name="panel_im_control_panel" layout="topleft" follows="left" - label="IM Control Panel" min_width="115" auto_resize="false" user_resize="true" /> diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml index 5b77f11d71..0569b4d515 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml @@ -3,7 +3,7 @@ can_resize="true" can_minimize="true" can_close="false" - height="202" + height="205" layout="topleft" min_height="124" min_width="190" @@ -50,7 +50,7 @@ user_resize="false" auto_resize="false" layout="topleft" - height="26" + height="20" name="my_panel"> <avatar_icon enabled="false" @@ -86,23 +86,38 @@ visible="true" width="20" /> </layout_panel> - <layout_panel - auto_resize="false" - user_resize="false" - follows="top|left" - height="26" - visible="true" - layout="topleft" - name="leave_call_btn_panel" - width="100"> - <button - follows="right|top" - height="23" - top_pad="0" - label="Leave Call" - name="leave_call_btn" - width="100" /> - </layout_panel> + <layout_stack + clip="true" + auto_resize="false" + follows="left|top|right" + height="26" + layout="topleft" + mouse_opaque="false" + name="voice_effect_and_leave_call_stack" + orientation="horizontal" + width="262"> + <panel + class="panel_voice_effect" + name="panel_voice_effect" + visiblity_control="VoiceMorphingEnabled" + filename="panel_voice_effect.xml" /> + <layout_panel + auto_resize="false" + user_resize="false" + follows="top|right" + height="23" + visible="true" + layout="topleft" + name="leave_call_btn_panel" + width="100"> + <button + follows="right|top" + height="23" + label="Leave Call" + name="leave_call_btn" + width="100" /> + </layout_panel> + </layout_stack> <layout_panel follows="all" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml new file mode 100644 index 0000000000..edc25348e4 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + can_resize="true" + height="420" + name="voice_effects" + help_topic="voice_effects" + title="PREVIEW VOICE MORPHING" + background_visible="true" + follows="all" + label="Places" + layout="topleft" + min_height="350" + min_width="330" + width="455"> + <string name="no_voice_effect"> + (No Voice Morph) + </string> + <string name="active_voice_effect"> + (Active) + </string> + <string name="unsubscribed_voice_effect"> + (Unsubscribed) + </string> + <string name="new_voice_effect"> + (New!) + </string> + <text + height="68" + word_wrap="true" + use_ellipses="true" + type="string" + follows="left|top|right" + layout="topleft" + left="10" + name="status_text" + right="-10" + top="25"> +To preview any of the Voice Morphing effects, click the Record button to record a short snippet of voice, then click any Voice Morph in the list to hear how it will sound. + +To reconnect to Nearby Voice simply close this window. + </text> + <button + follows="left|top" + height="23" + label="Record Sample" + layout="topleft" + left="10" + name="record_btn" + tool_tip="Record a sample of your voice." + top_pad="5" + width="150"> + <button.commit_callback + function="VoiceEffect.Record" /> + </button> + <button + follows="left|top" + height="23" + label="Stop" + layout="topleft" + left_delta="0" + name="record_stop_btn" + top_delta="0" + width="150"> + <button.commit_callback + function="VoiceEffect.Stop" /> + </button> + <text + height="18" + halign="right" + use_ellipses="true" + type="string" + follows="left|top|right" + layout="topleft" + left_pad="10" + name="voice_morphing_link" + right="-10" + top_delta="5"> + [[URL] Get Voice Morphing] + </text> + <scroll_list + bottom="-10" + draw_heading="true" + follows="all" + layout="topleft" + left="10" + multi_select="false" + name="voice_effect_list" + right="-10" + tool_tip="Record a sample of your voice, then click an effect to preview." + top="128"> + <scroll_list.columns + label="Voice Morph" + name="name" relative_width="0.41"/> + <scroll_list.columns + dynamic_width="true" + label="Expires" + name="expires" + relative_width="0.59" /> + </scroll_list> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 11459ad0e6..221457ac1f 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -662,6 +662,14 @@ parameter="wear" /> </menu_item_call> <menu_item_call + label="Add" + layout="topleft" + name="Wearable Add"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="wear_add" /> + </menu_item_call> + <menu_item_call label="Take Off" layout="topleft" name="Take Off"> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index aff8f7ddf4..2641ce4ee4 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -80,6 +80,17 @@ function="Floater.Toggle" parameter="gestures" /> </menu_item_check> + <menu_item_check + label="My Voice" + name="ShowVoice" + visibility_control="VoiceMorphingEnabled"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="voice_effect" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="voice_effect" /> + </menu_item_check> <menu label="My Status" name="Status" diff --git a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml index e645702f93..fa5ca60a19 100644 --- a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml @@ -9,6 +9,13 @@ function="Wearable.Wear" /> </menu_item_call> <menu_item_call + label="Add" + layout="topleft" + name="wear_add"> + <on_click + function="Wearable.Add" /> + </menu_item_call> + <menu_item_call label="Take Off / Detach" layout="topleft" name="take_off_or_detach"> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 5ead756d20..6d3d0f13bf 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2069,6 +2069,29 @@ Would you be my friend? </notification> <notification + icon="alertmodal.tga" + label="Save Wearable" + name="SaveWearableAs" + type="alertmodal"> + Save item to my inventory as: + <form name="form"> + <input name="message" type="text"> + [DESC] (new) + </input> + <button + default="true" + index="0" + name="Offer" + text="OK"/> + <button + index="1" + name="Cancel" + text="Cancel"/> + </form> + </notification> + + + <notification icon="alertmodal.tga" label="Rename Outfit" name="RenameOutfit" @@ -5981,6 +6004,50 @@ We are creating a voice channel for you. This may take up to one minute. </notification> <notification + icon="notify.tga" + name="VoiceEffectsExpired" + sound="UISndAlert" + persist="true" + type="notify"> +One or more of your subscribed Voice Morphs has expired. +[[URL] Click here] to renew your subscription. + <unique/> + </notification> + + <notification + icon="notify.tga" + name="VoiceEffectsExpiredInUse" + sound="UISndAlert" + persist="true" + type="notify"> +The active Voice Morph has expired, your normal voice settings have been applied. +[[URL] Click here] to renew your subscription. + <unique/> + </notification> + + <notification + icon="notify.tga" + name="VoiceEffectsWillExpire" + sound="UISndAlert" + persist="true" + type="notify"> +One or more of your Voice Morphs will expire in less than [INTERVAL] days. +[[URL] Click here] to renew your subscription. + <unique/> + </notification> + LLNotificationsUtil::add("VoiceEffectsNew"); + + <notification + icon="notify.tga" + name="VoiceEffectsNew" + sound="UISndAlert" + persist="true" + type="notify"> +New Voice Morphs are available! + <unique/> + </notification> + + <notification icon="notifytip.tga" name="Cannot enter parcel: not a group member" type="notifytip"> diff --git a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml index a0bbc8f2ee..4e5f594ffe 100644 --- a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel follows="top|right|left" - height="25" + height="23" layout="topleft" left="0" name="wearable_item" @@ -45,7 +45,7 @@ use_ellipses="true" name="item_name" text_color="white" - top="4" + top="5" value="..." width="359" /> <panel @@ -74,10 +74,10 @@ name="btn_edit_panel" layout="topleft" follows="top|right" - top="0" + top="1" left_pad="3" - height="24" - width="27" + height="23" + width="26" tab_stop="false"> <button name="btn_edit" @@ -86,8 +86,8 @@ image_overlay="Edit_Wrench" top="0" left="0" - height="24" - width="24" + height="23" + width="23" tab_stop="false" /> </panel> <icon @@ -97,7 +97,7 @@ layout="bottomleft" left="0" name="wearable_type_separator_icon" - top="3" + top="0" visible="true" width="380"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 82b2405ec9..4eff5bc48a 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -90,7 +90,7 @@ label="Speak" label_selected="Speak" name="speak_btn" - pad_right="22" + pad_right="20" tab_stop="true" use_ellipses="true" /> </talk_button> diff --git a/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml index e41141f6bd..5d81aebbd5 100644 --- a/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml @@ -33,7 +33,7 @@ follows="top|left" image_unselected="Toast_CloseBtn" image_selected="Toast_CloseBtn" - top="2" + top="3" left="0" height="18" width="18" @@ -56,7 +56,7 @@ use_ellipses="true" name="item_name" text_color="white" - top="4" + top="5" value="..." width="359" /> <button @@ -64,20 +64,20 @@ layout="topleft" follows="top|right" image_overlay="UpArrow_Off" - top="0" + top="1" left="0" - height="24" - width="24" + height="23" + width="23" tab_stop="false" /> <button name="btn_move_down" layout="topleft" follows="top|right" image_overlay="DownArrow_Off" - top="0" + top="1" left_pad="3" - height="24" - width="24" + height="23" + width="23" tab_stop="false" /> <panel background_visible="false" @@ -107,18 +107,18 @@ follows="top|right" top="0" left_pad="3" - height="24" - width="27" + height="23" + width="26" tab_stop="false"> <button name="btn_edit" layout="topleft" follows="top|right" image_overlay="Edit_Wrench" - top="0" + top="1" left="0" - height="24" - width="24" + height="23" + width="23" tab_stop="false" /> </panel> <icon diff --git a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml index 83040a745b..d36c2a4e6f 100644 --- a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml +++ b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml @@ -28,6 +28,7 @@ allow_select="true" follows="all" height="10" + item_pad="2" layout="topleft" left="0" multi_select="true" @@ -43,6 +44,7 @@ allow_select="true" follows="all" height="10" + item_pad="2" layout="topleft" left="0" multi_select="true" @@ -58,6 +60,7 @@ allow_select="true" follows="all" height="10" + item_pad="2" layout="topleft" left="0" multi_select="true" diff --git a/indra/newview/skins/default/xui/en/panel_deletable_wearable_list_item.xml b/indra/newview/skins/default/xui/en/panel_deletable_wearable_list_item.xml index b006d125ee..45031859f1 100644 --- a/indra/newview/skins/default/xui/en/panel_deletable_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_deletable_wearable_list_item.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel follows="top|right|left" - height="25" + height="23" layout="topleft" left="0" name="deletable_wearable_item" @@ -33,7 +33,7 @@ follows="top|left" image_unselected="Toast_CloseBtn" image_selected="Toast_CloseBtn" - top="2" + top="3" left="0" height="18" width="18" @@ -56,7 +56,7 @@ use_ellipses="true" name="item_name" text_color="white" - top="4" + top="5" value="..." width="359" /> <icon @@ -66,7 +66,7 @@ layout="bottomleft" left="0" name="wearable_type_separator_icon" - top="3" + top="0" visible="true" width="380"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml index 6c43635d49..20652df918 100644 --- a/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel follows="top|right|left" - height="25" + height="23" layout="topleft" left="0" name="dummy_clothing_item" @@ -56,8 +56,8 @@ image_overlay="AddItem_Off" top="0" left="0" - height="24" - width="24" + height="23" + width="23" tab_stop="false" /> <icon follows="left|right|top" @@ -66,7 +66,7 @@ layout="bottomleft" left="0" name="wearable_type_separator_icon" - top="3" + top="0" visible="true" width="380"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_edit_wearable.xml b/indra/newview/skins/default/xui/en/panel_edit_wearable.xml index 8604f42e75..645ee8a435 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_wearable.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_wearable.xml @@ -7,6 +7,7 @@ label="Wearable" layout="topleft" left="0" + help_topic="edit_wearable" name="panel_edit_wearable" top="0" width="333"> diff --git a/indra/newview/skins/default/xui/en/panel_landmark_info.xml b/indra/newview/skins/default/xui/en/panel_landmark_info.xml index 25674a1a39..bb73360e0b 100644 --- a/indra/newview/skins/default/xui/en/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/en/panel_landmark_info.xml @@ -46,12 +46,15 @@ <!-- Texture names for rating icons --> <string name="icon_PG" + translate="false" value="Parcel_PG_Dark" /> <string name="icon_M" + translate="false" value="Parcel_M_Dark" /> <string name="icon_R" + translate="false" value="Parcel_R_Dark" /> <button follows="top|right" diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index 40f60d50fb..c9802a269c 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -5,8 +5,8 @@ border="false" height="600" follows="all" - label="Outfit Edit" layout="topleft" + help_topic="edit_outfit" left="0" min_height="350" name="outfit_edit" @@ -85,7 +85,6 @@ bevel_style="none" follows="top|left|right" height="40" - label="bottom_panel" layout="topleft" left="6" name="header_panel" @@ -106,7 +105,6 @@ bevel_style="none" follows="top|right" height="38" - label="bottom_panel" layout="topleft" left_pad="5" name="outfit_name_and_status" @@ -160,7 +158,6 @@ It is calculated as border_size + 2*UIResizeBarOverlap <layout_panel layout="topleft" height="187" - label="IM Control Panel" min_height="100" name="outfit_wearables_panel" width="313" @@ -183,7 +180,6 @@ It is calculated as border_size + 2*UIResizeBarOverlap bg_alpha_color="DkGray2" layout="topleft" height="154" - label="add_button_and_combobox" name="add_button_and_combobox" width="311" user_resize="false" @@ -343,7 +339,6 @@ It is calculated as border_size + 2*UIResizeBarOverlap bevel_style="none" follows="bottom|left|right" height="27" - label="bottom_panel" layout="topleft" left="5" name="no_add_wearables_button_bar" @@ -378,7 +373,6 @@ It is calculated as border_size + 2*UIResizeBarOverlap bevel_style="none" follows="left|right|bottom" height="27" - label="add_wearables_button_bar" layout="topleft" left="5" name="add_wearables_button_bar" diff --git a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml index 13e1f5ba5c..de1f2cf31b 100644 --- a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml @@ -30,6 +30,7 @@ height="490" name="outfitslist_tab" background_visible="true" + help_topic="my_outfits_tab" follows="all" label="MY OUTFITS" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index b79ef1e287..da28773c74 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -173,6 +173,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M background_visible="true" bg_alpha_color="DkGray2" bg_opaque_color="DkGray2" + empty_accordion_text.value="" follows="all" height="356" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index 57ac79686d..59f1f6d638 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -2,7 +2,7 @@ <panel background_visible="true" follows="all" - height="570" + height="610" layout="topleft" left="0" min_height="350" @@ -95,48 +95,63 @@ <!-- Texture names for parcel permissions icons --> <string name="icon_PG" + translate="false" value="Parcel_PG_Dark" /> <string name="icon_M" + translate="false" value="Parcel_M_Dark" /> <string name="icon_R" + translate="false" value="Parcel_R_Dark" /> <string name="icon_Voice" + translate="false" value="Parcel_Voice_Dark" /> <string name="icon_VoiceNo" + translate="false" value="Parcel_VoiceNo_Dark" /> <string name="icon_Fly" + translate="false" value="Parcel_Fly_Dark" /> <string name="icon_FlyNo" + translate="false" value="Parcel_FlyNo_Dark" /> <string name="icon_Push" + translate="false" value="Parcel_Push_Dark" /> <string name="icon_PushNo" + translate="false" value="Parcel_PushNo_Dark" /> <string name="icon_Build" + translate="false" value="Parcel_Build_Dark" /> <string name="icon_BuildNo" + translate="false" value="Parcel_BuildNo_Dark" /> <string name="icon_Scripts" + translate="false" value="Parcel_Scripts_Dark" /> <string name="icon_ScriptsNo" + translate="false" value="Parcel_ScriptsNo_Dark" /> <string name="icon_Damage" + translate="false" value="Parcel_Damage_Dark" /> <string name="icon_DamageNo" + translate="false" value="Parcel_DamageNo_Dark" /> <button follows="top|right" @@ -166,7 +181,7 @@ <scroll_container color="DkGray2" follows="all" - height="532" + height="572" layout="topleft" left="9" name="place_scroll" @@ -176,7 +191,7 @@ <panel bg_alpha_color="DkGray2" follows="left|top|right" - height="540" + height="580" layout="topleft" left="0" min_height="300" @@ -322,21 +337,22 @@ <accordion fit_parent="true" follows="all" - height="223" + height="268" layout="topleft" single_expansion="true" left="0" name="advanced_info_accordion" - top_pad="10" + top_pad="5" width="313"> <accordion_tab - height="170" + fit_panel="false" + height="175" layout="topleft" name="parcel_characteristics_tab" title="Parcel"> <panel follows="all" - height="160" + height="175" layout="topleft" left="0" name="parcel_characteristics_panel" @@ -533,8 +549,8 @@ name="about_land_btn" right="-5" tab_stop="false" - top="138" - width="90"> + top_pad="2" + width="140"> <click_callback function="Floater.Show" parameter="about_land" /> @@ -543,7 +559,8 @@ </accordion_tab> <accordion_tab expanded="false" - height="150" + fit_panel="false" + height="125" layout="topleft" name="region_information_tab" title="Region"> @@ -662,7 +679,8 @@ name="region_info_btn" right="-5" tab_stop="false" - width="105"> + top_pad="2" + width="180"> <click_callback function="Floater.Show" parameter="region_info" /> @@ -671,13 +689,14 @@ </accordion_tab> <accordion_tab expanded="false" - height="190" + fit_panel="false" + height="180" layout="topleft" name="estate_information_tab" title="Estate"> <panel follows="all" - height="189" + height="180" layout="topleft" left="0" name="estate_information_panel" @@ -760,13 +779,14 @@ </accordion_tab> <accordion_tab expanded="false" - height="320" + fit_panel="false" + height="290" layout="topleft" name="sales_tab" title="For Sale"> <panel follows="all" - height="300" + height="290" layout="topleft" left="0" name="sales_panel" diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index 008aa1acc0..43513e1ab6 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -41,32 +41,35 @@ name="buycurrencylabel"> L$ [AMT] </panel.string> - <button + <panel + height="18" + left="-315" + width="95" + top="1" + follows="right|top" + name="balance_bg" + bg_visible="true" + background_opaque="true" + bg_opaque_image="bevel_background"> + <text auto_resize="true" halign="center" - enabled="false" font="SansSerifSmall" - follows="right|top" - image_overlay="" - image_selected="bevel_background" - image_unselected="bevel_background" - image_pressed="bevel_background" + follows="all" height="18" - right="-275" - label_shadow="false" - name="buycurrency" - label_color_disabled="ButtonLabelColor" - image_color_disabled="White" + left="0" + name="balance" tool_tip="My Balance" - pad_left="12" - pad_right="12" + v_pad="4" top="0" + wrap="false" + value="L$20" width="40" /> <button auto_resize="true" halign="center" font="SansSerifSmall" - follows="right|top" + follows="right|top|bottom" image_hover_unselected="buy_over" image_unselected="buy_off" image_pressed="buy_press" @@ -81,6 +84,7 @@ tool_tip="Click to buy more L$" top="0" width="55" /> + </panel> <text type="string" font="SansSerifSmall" diff --git a/indra/newview/skins/default/xui/en/panel_voice_effect.xml b/indra/newview/skins/default/xui/en/panel_voice_effect.xml new file mode 100644 index 0000000000..c575ca468c --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_voice_effect.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="all" + height="26" + layout="topleft" + name="panel_voice_effect" + width="200"> + <string name="no_voice_effect"> + No Voice Morph + </string> + <string name="preview_voice_effects"> + Preview Voice Morphing ▶ + </string> + <string name="get_voice_effects"> + Get Voice Morphing ▶ + </string> + <combo_box + enabled="false" + follows="left|top|right" + height="23" + name="voice_effect" + tool_tip="Select a Voice Morphing effect to change your voice." + top_pad="0" + width="200"> + <combo_box.item + label="No Voice Morph" + name="no_voice_effect" + top_pad="0" + value="0" /> + <combo_box.commit_callback + function="Voice.CommitVoiceEffect" /> + </combo_box> +</panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 790bc64a4a..0d14a6b3c8 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3093,6 +3093,8 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. The session initialization is timed out </string> + <string name="voice_morphing_url">http://secondlife.com/landing/voicemorphing</string> + <!-- Financial operations strings --> <string name="paid_you_ldollars">[NAME] paid you L$[AMOUNT]</string> <string name="you_paid_ldollars">You paid [NAME] L$[AMOUNT] [REASON].</string> diff --git a/indra/newview/skins/default/xui/es/floater_buy_land.xml b/indra/newview/skins/default/xui/es/floater_buy_land.xml index a40f65d5d0..74243a4d06 100644 --- a/indra/newview/skins/default/xui/es/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/es/floater_buy_land.xml @@ -126,9 +126,6 @@ para cubrir esta parcela. <floater.string name="no_parcel_selected"> (No se ha seleccionado una parcela) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Región: </text> diff --git a/indra/newview/skins/default/xui/fr/floater_buy_land.xml b/indra/newview/skins/default/xui/fr/floater_buy_land.xml index 7c9a31a4c3..b7f8f36f81 100644 --- a/indra/newview/skins/default/xui/fr/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/fr/floater_buy_land.xml @@ -124,9 +124,6 @@ prend en charge [AMOUNT2] objets <floater.string name="no_parcel_selected"> (aucune parcelle sélectionnée) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Région : </text> diff --git a/indra/newview/skins/default/xui/fr/panel_landmark_info.xml b/indra/newview/skins/default/xui/fr/panel_landmark_info.xml index 4001616034..bd29bd676c 100644 --- a/indra/newview/skins/default/xui/fr/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/fr/panel_landmark_info.xml @@ -18,9 +18,6 @@ <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> <button name="back_btn" tool_tip="Précédent"/> <text name="title" value="Profil du lieu"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/fr/panel_place_profile.xml b/indra/newview/skins/default/xui/fr/panel_place_profile.xml index 598e94166e..731e045019 100644 --- a/indra/newview/skins/default/xui/fr/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/fr/panel_place_profile.xml @@ -41,21 +41,6 @@ <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> - <string name="icon_Voice" value="parcel_drk_Voice"/> - <string name="icon_VoiceNo" value="parcel_drk_VoiceNo"/> - <string name="icon_Fly" value="parcel_drk_Fly"/> - <string name="icon_FlyNo" value="parcel_drk_FlyNo"/> - <string name="icon_Push" value="parcel_drk_Push"/> - <string name="icon_PushNo" value="parcel_drk_PushNo"/> - <string name="icon_Build" value="parcel_drk_Build"/> - <string name="icon_BuildNo" value="parcel_drk_BuildNo"/> - <string name="icon_Scripts" value="parcel_drk_Scripts"/> - <string name="icon_ScriptsNo" value="parcel_drk_ScriptsNo"/> - <string name="icon_Damage" value="parcel_drk_Damage"/> - <string name="icon_DamageNo" value="parcel_drk_DamageNo"/> <button name="back_btn" tool_tip="Précédent"/> <text name="title" value="Profil du lieu"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/it/floater_buy_land.xml b/indra/newview/skins/default/xui/it/floater_buy_land.xml index 2e78168209..f3b30f7048 100644 --- a/indra/newview/skins/default/xui/it/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/it/floater_buy_land.xml @@ -124,9 +124,6 @@ consente [AMOUNT2] oggetti <floater.string name="no_parcel_selected"> (nessun terreno selezionato) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Regione: </text> diff --git a/indra/newview/skins/default/xui/ja/floater_buy_land.xml b/indra/newview/skins/default/xui/ja/floater_buy_land.xml index a274e25326..34f9d38de1 100644 --- a/indra/newview/skins/default/xui/ja/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/ja/floater_buy_land.xml @@ -124,9 +124,6 @@ <floater.string name="no_parcel_selected"> (区画が選定されていません) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> 地域: </text> diff --git a/indra/newview/skins/default/xui/ja/panel_landmark_info.xml b/indra/newview/skins/default/xui/ja/panel_landmark_info.xml index 87477c2651..7fca66f90f 100644 --- a/indra/newview/skins/default/xui/ja/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/ja/panel_landmark_info.xml @@ -18,9 +18,6 @@ <string name="acquired_date"> [year,datetime,local] [mth,datetime,local] [day,datetime,local] [wkday,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> <button name="back_btn" tool_tip="戻る"/> <text name="title" value="場所のプロフィール"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/ja/panel_place_profile.xml b/indra/newview/skins/default/xui/ja/panel_place_profile.xml index 9de04f0d6a..b897e1d748 100644 --- a/indra/newview/skins/default/xui/ja/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/ja/panel_place_profile.xml @@ -41,21 +41,6 @@ <string name="acquired_date"> [year,datetime,local] [mth,datetime,local] [day,datetime,local] [wkday,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> - <string name="icon_PG" value="parcel_drk_PG"/> - <string name="icon_M" value="parcel_drk_M"/> - <string name="icon_R" value="parcel_drk_R"/> - <string name="icon_Voice" value="parcel_drk_Voice"/> - <string name="icon_VoiceNo" value="parcel_drk_VoiceNo"/> - <string name="icon_Fly" value="parcel_drk_Fly"/> - <string name="icon_FlyNo" value="parcel_drk_FlyNo"/> - <string name="icon_Push" value="parcel_drk_Push"/> - <string name="icon_PushNo" value="parcel_drk_PushNo"/> - <string name="icon_Build" value="parcel_drk_Build"/> - <string name="icon_BuildNo" value="parcel_drk_BuildNo"/> - <string name="icon_Scripts" value="parcel_drk_Scripts"/> - <string name="icon_ScriptsNo" value="parcel_drk_ScriptsNo"/> - <string name="icon_Damage" value="parcel_drk_Damage"/> - <string name="icon_DamageNo" value="parcel_drk_DamageNo"/> <button name="back_btn" tool_tip="戻る"/> <text name="title" value="場所のプロフィール"/> <scroll_container name="place_scroll"> diff --git a/indra/newview/skins/default/xui/pl/floater_buy_land.xml b/indra/newview/skins/default/xui/pl/floater_buy_land.xml index 3d01129d9b..7b4f459b4e 100644 --- a/indra/newview/skins/default/xui/pl/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/pl/floater_buy_land.xml @@ -125,9 +125,6 @@ używanie Posiadłości żeby sfinalizować ten zakup. <floater.string name="no_parcel_selected"> (Posiadłość nie została wybrana) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Region: </text> diff --git a/indra/newview/skins/default/xui/pt/floater_buy_land.xml b/indra/newview/skins/default/xui/pt/floater_buy_land.xml index 73b483acf2..5c5ee3b7a0 100644 --- a/indra/newview/skins/default/xui/pt/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/pt/floater_buy_land.xml @@ -124,9 +124,6 @@ contribuídas para cobrir este lote antes da aquisição se completar. <floater.string name="no_parcel_selected"> (nenhum lote selecionado) </floater.string> - <floater.string name="icon_PG" value="Parcel_PG_Dark"/> - <floater.string name="icon_M" value="Parcel_M_Dark"/> - <floater.string name="icon_R" value="Parcel_R_Dark"/> <text name="region_name_label"> Região: </text> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index c887097575..8c089c0b79 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -355,7 +355,6 @@ class WindowsManifest(ViewerManifest): self.path("qtwebkitd4.dll") self.path("qtxmlpatternsd4.dll") self.path("ssleay32.dll") - self.path("winmm.dll") # For WebKit/Qt plugin runtimes (image format plugins) if self.prefix(src="imageformats", dst="imageformats"): diff --git a/install.xml b/install.xml index ccebe2fe50..8387372400 100644 --- a/install.xml +++ b/install.xml @@ -111,9 +111,9 @@ <key>darwin</key> <map> <key>md5sum</key> - <string>d9ce3ace9235249b7edf6f564de83395</string> + <string>cdb2f5c4a5a1f9ecd75bc1dbdd4db8e9</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.4.0-darwin-20080731b.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.7.1-darwin-20100606.tar.bz2</uri> </map> <key>linux</key> <map> @@ -233,9 +233,9 @@ <key>darwin</key> <map> <key>md5sum</key> - <string>1bece4ebdc5fc3818854bc3df14b511b</string> + <string>752e295ccb17f0dcb7c0167db3ad1e69</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.16.4a-darwin-20090304.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.20.1-darwin-20100606.tar.bz2</uri> </map> <key>linux</key> <map> |