summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/contributions.txt2
-rw-r--r--indra/llcommon/llerror.cpp10
-rw-r--r--indra/llcommon/llerror.h2
-rw-r--r--indra/llcommon/llstring.h4
-rw-r--r--indra/llcommon/tests/llerror_test.cpp9
-rw-r--r--indra/llmessage/llcurl.cpp7
-rw-r--r--indra/llui/llaccordionctrl.cpp13
-rw-r--r--indra/llui/llaccordionctrl.h35
-rw-r--r--indra/llui/llaccordionctrltab.cpp2
-rw-r--r--indra/llui/llaccordionctrltab.h2
-rw-r--r--indra/llui/llfloater.cpp5
-rw-r--r--indra/llui/llkeywords.cpp54
-rw-r--r--indra/llui/llkeywords.h3
-rw-r--r--indra/llui/llmultifloater.cpp10
-rw-r--r--indra/llui/llmultifloater.h1
-rw-r--r--indra/llui/lltextbase.cpp17
-rw-r--r--indra/llui/lltextbase.h1
-rw-r--r--indra/newview/CMakeLists.txt4
-rw-r--r--indra/newview/app_settings/logcontrol.xml2
-rw-r--r--indra/newview/app_settings/settings.xml37
-rw-r--r--indra/newview/app_settings/settings_per_account.xml11
-rw-r--r--indra/newview/gpu_table.txt5
-rw-r--r--indra/newview/llagent.cpp4
-rw-r--r--indra/newview/llagentcamera.cpp1
-rw-r--r--indra/newview/llagentwearables.cpp79
-rw-r--r--indra/newview/llagentwearables.h6
-rw-r--r--indra/newview/llappearancemgr.cpp110
-rw-r--r--indra/newview/llappearancemgr.h14
-rw-r--r--indra/newview/llcallfloater.cpp23
-rw-r--r--indra/newview/llcallfloater.h18
-rw-r--r--indra/newview/llcofwearables.cpp8
-rw-r--r--indra/newview/llfloatervoiceeffect.cpp288
-rw-r--r--indra/newview/llfloatervoiceeffect.h78
-rw-r--r--indra/newview/llinventorybridge.cpp112
-rw-r--r--indra/newview/llinventorybridge.h2
-rw-r--r--indra/newview/llinventoryfunctions.h13
-rw-r--r--indra/newview/llinventorymodel.cpp19
-rw-r--r--indra/newview/llinventoryobserver.cpp54
-rw-r--r--indra/newview/llinventoryobserver.h16
-rw-r--r--indra/newview/llnamelistctrl.cpp4
-rw-r--r--indra/newview/llnamelistctrl.h4
-rw-r--r--indra/newview/lloutfitobserver.cpp15
-rw-r--r--indra/newview/lloutfitobserver.h14
-rw-r--r--indra/newview/lloutfitslist.cpp17
-rw-r--r--indra/newview/lloutfitslist.h18
-rw-r--r--indra/newview/llpaneleditwearable.cpp38
-rw-r--r--indra/newview/llpaneleditwearable.h5
-rw-r--r--indra/newview/llpaneloutfitedit.cpp39
-rw-r--r--indra/newview/llpaneloutfitsinventory.cpp10
-rw-r--r--indra/newview/llpanelvoiceeffect.cpp169
-rw-r--r--indra/newview/llpanelvoiceeffect.h73
-rw-r--r--indra/newview/llparticipantlist.cpp2
-rw-r--r--indra/newview/llscrollingpanelparam.cpp6
-rw-r--r--indra/newview/llscrollingpanelparam.h3
-rw-r--r--indra/newview/llsidepanelappearance.cpp7
-rw-r--r--indra/newview/llspeakingindicatormanager.cpp4
-rw-r--r--indra/newview/llstatusbar.cpp17
-rw-r--r--indra/newview/lltexlayer.cpp162
-rw-r--r--indra/newview/lltexlayer.h14
-rw-r--r--indra/newview/lltexturefetch.cpp27
-rw-r--r--indra/newview/lltextureview.cpp27
-rw-r--r--indra/newview/lltooldraganddrop.cpp19
-rw-r--r--indra/newview/lltoolmorph.cpp32
-rw-r--r--indra/newview/lltoolmorph.h5
-rw-r--r--indra/newview/llviewerfloaterreg.cpp4
-rw-r--r--indra/newview/llviewerinventory.cpp10
-rw-r--r--indra/newview/llviewerinventory.h6
-rw-r--r--indra/newview/llviewermenu.cpp9
-rw-r--r--indra/newview/llviewerregion.cpp5
-rw-r--r--indra/newview/llviewerregion.h2
-rw-r--r--indra/newview/llviewertexture.cpp30
-rw-r--r--indra/newview/llviewervisualparam.cpp2
-rw-r--r--indra/newview/llviewervisualparam.h2
-rw-r--r--indra/newview/llvoavatarself.cpp74
-rw-r--r--indra/newview/llvoicechannel.cpp6
-rw-r--r--indra/newview/llvoicechannel.h2
-rw-r--r--indra/newview/llvoiceclient.cpp45
-rw-r--r--indra/newview/llvoiceclient.h81
-rw-r--r--indra/newview/llvoicevivox.cpp1016
-rw-r--r--indra/newview/llvoicevivox.h192
-rw-r--r--indra/newview/llwearable.cpp2
-rw-r--r--indra/newview/llwearableitemslist.cpp4
-rw-r--r--indra/newview/skins/default/textures/avatar_thumb_bkgrnd.pngbin0 -> 17692 bytes
-rw-r--r--indra/newview/skins/default/textures/map_infohub.tgabin1068 -> 1068 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml2
-rw-r--r--indra/newview/skins/default/textures/widgets/buy_off.pngbin54754 -> 399 bytes
-rw-r--r--indra/newview/skins/default/textures/widgets/buy_over.pngbin54772 -> 415 bytes
-rw-r--r--indra/newview/skins/default/textures/widgets/buy_press.pngbin54861 -> 461 bytes
-rw-r--r--indra/newview/skins/default/xui/da/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/de/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/de/panel_bottomtray.xml2
-rw-r--r--indra/newview/skins/default/xui/de/panel_landmark_info.xml3
-rw-r--r--indra/newview/skins/default/xui/de/panel_place_profile.xml15
-rw-r--r--indra/newview/skins/default/xui/en/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_im_session.xml1
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_controls.xml53
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_effect.xml101
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml8
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml11
-rw-r--r--indra/newview/skins/default/xui/en/menu_wearable_list_item.xml7
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml67
-rw-r--r--indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml16
-rw-r--r--indra/newview/skins/default/xui/en/panel_bottomtray.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_clothing_list_item.xml26
-rw-r--r--indra/newview/skins/default/xui/en/panel_cof_wearables.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_deletable_wearable_list_item.xml8
-rw-r--r--indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml8
-rw-r--r--indra/newview/skins/default/xui/en/panel_edit_wearable.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_landmark_info.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_edit.xml8
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfits_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_people.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_place_profile.xml50
-rw-r--r--indra/newview/skins/default/xui/en/panel_status_bar.xml34
-rw-r--r--indra/newview/skins/default/xui/en/panel_voice_effect.xml33
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml2
-rw-r--r--indra/newview/skins/default/xui/es/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/fr/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/fr/panel_landmark_info.xml3
-rw-r--r--indra/newview/skins/default/xui/fr/panel_place_profile.xml15
-rw-r--r--indra/newview/skins/default/xui/it/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/ja/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/ja/panel_landmark_info.xml3
-rw-r--r--indra/newview/skins/default/xui/ja/panel_place_profile.xml15
-rw-r--r--indra/newview/skins/default/xui/pl/floater_buy_land.xml3
-rw-r--r--indra/newview/skins/default/xui/pt/floater_buy_land.xml3
-rwxr-xr-xindra/newview/viewer_manifest.py1
-rw-r--r--install.xml8
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 &region);
/// 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
new file mode 100644
index 0000000000..84cc2159c1
--- /dev/null
+++ b/indra/newview/skins/default/textures/avatar_thumb_bkgrnd.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_infohub.tga b/indra/newview/skins/default/textures/map_infohub.tga
index 545b8e532c..d0134fa5fe 100644
--- a/indra/newview/skins/default/textures/map_infohub.tga
+++ b/indra/newview/skins/default/textures/map_infohub.tga
Binary files differ
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
index 961ad071d4..ee5979046f 100644
--- a/indra/newview/skins/default/textures/widgets/buy_off.png
+++ b/indra/newview/skins/default/textures/widgets/buy_off.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/widgets/buy_over.png b/indra/newview/skins/default/textures/widgets/buy_over.png
index 0be19f8a31..93adb68c86 100644
--- a/indra/newview/skins/default/textures/widgets/buy_over.png
+++ b/indra/newview/skins/default/textures/widgets/buy_over.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/widgets/buy_press.png b/indra/newview/skins/default/textures/widgets/buy_press.png
index d6f587464d..3f442d6eaa 100644
--- a/indra/newview/skins/default/textures/widgets/buy_press.png
+++ b/indra/newview/skins/default/textures/widgets/buy_press.png
Binary files differ
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>