summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorangela <angela@lindenlab.com>2010-02-23 11:44:56 +0800
committerangela <angela@lindenlab.com>2010-02-23 11:44:56 +0800
commite98d6a910a39671225f1af627570aee58517ea7f (patch)
tree9d0d28c80be1fd23166061e20bfce3d15a924e33 /indra
parent0eea585ef13426bf343fb5bcc3642cdfc769c022 (diff)
parent37291bea8d9b5a626b449d984856e280e159ac5c (diff)
merg and resolve conflicts
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/Boost.cmake6
-rw-r--r--indra/cmake/PulseAudio.cmake28
-rw-r--r--indra/llui/llaccordionctrl.cpp97
-rw-r--r--indra/llui/llaccordionctrl.h3
-rw-r--r--indra/media_plugins/webkit/CMakeLists.txt14
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher.cpp483
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc21
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc6
-rw-r--r--indra/media_plugins/webkit/media_plugin_webkit.cpp34
-rw-r--r--indra/newview/linux_tools/client-readme.txt9
-rw-r--r--indra/newview/llagentwearables.cpp31
-rw-r--r--indra/newview/llappearancemgr.cpp406
-rw-r--r--indra/newview/llappearancemgr.h12
-rw-r--r--indra/newview/llinventorybridge.cpp6
-rw-r--r--indra/newview/llpanelgroup.cpp4
-rw-r--r--indra/newview/llpanelgroupnotices.cpp3
-rw-r--r--indra/newview/llpanelnearbymedia.cpp68
-rw-r--r--indra/newview/llstatusbar.cpp16
-rw-r--r--indra/newview/llviewermedia.cpp5
-rw-r--r--indra/newview/llviewermenu.cpp3
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_general.xml5
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml161
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_land_money.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_notices.xml5
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_roles.xml5
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_status_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/en/widgets/tab_container.xml6
28 files changed, 1204 insertions, 241 deletions
diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake
index 3652508b6a..7ce57a5572 100644
--- a/indra/cmake/Boost.cmake
+++ b/indra/cmake/Boost.cmake
@@ -38,9 +38,9 @@ else (STANDALONE)
debug libboost_signals-vc80-mt-gd-${BOOST_VERSION})
endif (MSVC71)
elseif (DARWIN)
- set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt)
- set(BOOST_REGEX_LIBRARY boost_regex-mt)
- set(BOOST_SIGNALS_LIBRARY boost_signals-mt)
+ set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-xgcc40-mt)
+ set(BOOST_REGEX_LIBRARY boost_regex-xgcc40-mt)
+ set(BOOST_SIGNALS_LIBRARY boost_signals-xgcc40-mt)
elseif (LINUX)
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-gcc41-mt)
set(BOOST_REGEX_LIBRARY boost_regex-gcc41-mt)
diff --git a/indra/cmake/PulseAudio.cmake b/indra/cmake/PulseAudio.cmake
new file mode 100644
index 0000000000..f8087a8083
--- /dev/null
+++ b/indra/cmake/PulseAudio.cmake
@@ -0,0 +1,28 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+if (STANDALONE)
+ include(FindPkgConfig)
+
+ pkg_check_modules(PULSEAUDIO REQUIRED libpulse-mainloop-glib)
+
+elseif (LINUX)
+ use_prebuilt_binary(pulseaudio)
+ set(PULSEAUDIO_FOUND ON FORCE BOOL)
+ set(PULSEAUDIO_INCLUDE_DIRS
+ ${LIBS_PREBUILT_DIR}/include
+ )
+ # We don't need to explicitly link against pulseaudio itself, because
+ # the viewer probes for the system's copy at runtime.
+ set(PULSEAUDIO_LIBRARIES
+ # none needed!
+ )
+endif (STANDALONE)
+
+if (PULSEAUDIO_FOUND)
+ set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.")
+endif (PULSEAUDIO_FOUND)
+
+if (PULSEAUDIO)
+ add_definitions(-DLL_PULSEAUDIO_ENABLED=1)
+endif (PULSEAUDIO)
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index d0c73fbfbc..aa69dfe0cc 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -103,6 +103,13 @@ void LLAccordionCtrl::draw()
LLLocalClipRect clip(local_rect);
LLPanel::draw();
+ /*
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ gl_rect_2d(0, 0 , width - 1 ,height - 1,LLColor4::green,true);
+ gl_line_2d(0, 0 , width - 1 ,height - 1,LLColor4::black);
+ */
}
@@ -338,36 +345,55 @@ void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
}
-
-void LLAccordionCtrl::arrange()
+void LLAccordionCtrl::arrangeSinge()
{
- if( mAccordionTabs.size() == 0)
- {
- //We do not arrange if we do not have what should be arranged
- return;
- }
-
- //Calculate params
S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+ S32 panel_height;
-
- if(mAccordionTabs.size() == 1)
+ S32 collapsed_height = 0;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
{
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
-
- LLRect panel_rect = accordion_tab->getRect();
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
+ }
+ }
- ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+ S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
- return;
-
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ else
+ {
+ panel_height = expanded_height;
+ }
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top-=mAccordionTabs[i]->getRect().getHeight();
}
+}
+
+void LLAccordionCtrl::arrangeMultiple()
+{
+ S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+ //Calculate params
for(size_t i = 0; i < mAccordionTabs.size(); i++ )
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
@@ -415,7 +441,40 @@ void LLAccordionCtrl::arrange()
show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
updateLayout(getRect().getWidth(),getRect().getHeight());
+}
+
+
+void LLAccordionCtrl::arrange()
+{
+ if( mAccordionTabs.size() == 0)
+ {
+ //We do not arrange if we do not have what should be arranged
+ return;
+ }
+
+ if(mAccordionTabs.size() == 1)
+ {
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
+
+ LLRect panel_rect = accordion_tab->getRect();
+
+ S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+
+ ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+
+ show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
+ return;
+
+ }
+
+ if(mSingleExpansion)
+ arrangeSinge ();
+ else
+ arrangeMultiple ();
}
//---------------------------------------------------------------------------------
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
index d57a42df32..7c29e545b7 100644
--- a/indra/llui/llaccordionctrl.h
+++ b/indra/llui/llaccordionctrl.h
@@ -105,6 +105,9 @@ public:
void reset ();
private:
+ void arrangeSinge();
+ void arrangeMultiple();
+
// Calc Splitter's height that is necessary to display all child content
S32 calcRecuiredHeight();
S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
index 4512c22b5d..9f66a77c64 100644
--- a/indra/media_plugins/webkit/CMakeLists.txt
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -14,10 +14,12 @@ include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
+include(PulseAudio)
include(WebKitLibPlugin)
include_directories(
+ ${PULSEAUDIO_INCLUDE_DIRS}
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
@@ -34,25 +36,27 @@ set(media_plugin_webkit_SOURCE_FILES
media_plugin_webkit.cpp
)
-add_library(media_plugin_webkit
- SHARED
- ${media_plugin_webkit_SOURCE_FILES}
-)
-
set(media_plugin_webkit_LINK_LIBRARIES
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${WEBKIT_PLUGIN_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
+ ${PULSEAUDIO_LIBRARIES}
)
if (LINUX)
+ list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp)
list(APPEND media_plugin_webkit_LINK_LIBRARIES
${UI_LIBRARIES} # for glib/GTK
)
endif (LINUX)
+add_library(media_plugin_webkit
+ SHARED
+ ${media_plugin_webkit_SOURCE_FILES}
+)
+
target_link_libraries(media_plugin_webkit ${media_plugin_webkit_LINK_LIBRARIES})
add_dependencies(media_plugin_webkit
diff --git a/indra/media_plugins/webkit/linux_volume_catcher.cpp b/indra/media_plugins/webkit/linux_volume_catcher.cpp
new file mode 100644
index 0000000000..2ba28bd4bf
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher.cpp
@@ -0,0 +1,483 @@
+/**
+ * @file linux_volume_catcher.cpp
+ * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
+ *
+ * @cond
+ * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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$
+ * @endcond
+ */
+
+/*
+ The high-level design is as follows:
+ 1) Connect to the PulseAudio daemon
+ 2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins)
+ 3) Examine any new audio player's PID to see if it belongs to our own process
+ 4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance)
+ 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call
+ */
+
+#include "linden_common.h"
+
+#include "linux_volume_catcher.h"
+
+
+#if LL_PULSEAUDIO_ENABLED
+
+extern "C" {
+#include <glib.h>
+
+#include <pulse/introspect.h>
+#include <pulse/context.h>
+#include <pulse/subscribe.h>
+#include <pulse/glib-mainloop.h> // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+}
+
+////////////////////////////////////////////////////
+
+#define DEBUGMSG(...) do {} while(0)
+#define INFOMSG(...) do {} while(0)
+#define WARNMSG(...) do {} while(0)
+
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) RTN (*ll##PASYM)(__VA_ARGS__) = NULL
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+#undef LL_PA_SYM
+
+static bool sSymsGrabbed = false;
+static apr_pool_t *sSymPADSOMemoryPool = NULL;
+static apr_dso_handle_t *sSymPADSOHandleG = NULL;
+
+bool grab_pa_syms(std::string pulse_dso_name)
+{
+ if (sSymsGrabbed)
+ {
+ // already have grabbed good syms
+ return true;
+ }
+
+ bool sym_error = false;
+ bool rtn = false;
+ apr_status_t rv;
+ apr_dso_handle_t *sSymPADSOHandle = NULL;
+
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0)
+
+ //attempt to load the shared library
+ apr_pool_create(&sSymPADSOMemoryPool, NULL);
+
+ if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle,
+ pulse_dso_name.c_str(),
+ sSymPADSOMemoryPool) ))
+ {
+ INFOMSG("Found DSO: %s", pulse_dso_name.c_str());
+
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+
+ if ( sSymPADSOHandle )
+ {
+ sSymPADSOHandleG = sSymPADSOHandle;
+ sSymPADSOHandle = NULL;
+ }
+
+ rtn = !sym_error;
+ }
+ else
+ {
+ INFOMSG("Couldn't load DSO: %s", pulse_dso_name.c_str());
+ rtn = false; // failure
+ }
+
+ if (sym_error)
+ {
+ WARNMSG("Failed to find necessary symbols in PulseAudio libraries.");
+ }
+#undef LL_PA_SYM
+
+ sSymsGrabbed = rtn;
+ return rtn;
+}
+
+
+void ungrab_pa_syms()
+{
+ // should be safe to call regardless of whether we've
+ // actually grabbed syms.
+
+ if ( sSymPADSOHandleG )
+ {
+ apr_dso_unload(sSymPADSOHandleG);
+ sSymPADSOHandleG = NULL;
+ }
+
+ if ( sSymPADSOMemoryPool )
+ {
+ apr_pool_destroy(sSymPADSOMemoryPool);
+ sSymPADSOMemoryPool = NULL;
+ }
+
+ // NULL-out all of the symbols we'd grabbed
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0)
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+#undef LL_PA_SYM
+
+ sSymsGrabbed = false;
+}
+////////////////////////////////////////////////////
+
+// PulseAudio requires a chain of callbacks with C linkage
+extern "C" {
+ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata);
+ void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata);
+ void callback_context_state(pa_context *context, void *userdata);
+}
+
+
+class LinuxVolumeCatcherImpl
+{
+public:
+ LinuxVolumeCatcherImpl();
+ ~LinuxVolumeCatcherImpl();
+
+ void setVolume(F32 volume);
+ void pump(void);
+
+ // for internal use - can't be private because used from our C callbacks
+
+ bool loadsyms(std::string pulse_dso_name);
+ void init();
+ void cleanup();
+
+ void update_all_volumes(F32 volume);
+ void update_index_volume(U32 index, F32 volume);
+ void connected_okay();
+
+ std::set<U32> mSinkInputIndices;
+ std::map<U32,U32> mSinkInputNumChannels;
+ F32 mDesiredVolume;
+ pa_glib_mainloop *mMainloop;
+ pa_context *mPAContext;
+ bool mConnected;
+ bool mGotSyms;
+};
+
+LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl()
+ : mDesiredVolume(0.0f),
+ mMainloop(NULL),
+ mPAContext(NULL),
+ mConnected(false),
+ mGotSyms(false)
+{
+ init();
+}
+
+LinuxVolumeCatcherImpl::~LinuxVolumeCatcherImpl()
+{
+ cleanup();
+}
+
+bool LinuxVolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
+{
+ return grab_pa_syms(pulse_dso_name);
+}
+
+void LinuxVolumeCatcherImpl::init()
+{
+ // try to be as defensive as possible because PA's interface is a
+ // bit fragile and (for our purposes) we'd rather simply not function
+ // than crash
+
+ // we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in
+ // libpulse.so.0 - this isn't a great assumption, and the two DSOs should
+ // probably be loaded separately. Our Linux DSO framework needs refactoring,
+ // we do this sort of thing a lot with practically identical logic...
+ mGotSyms = loadsyms("libpulse-mainloop-glib.so.0");
+ if (!mGotSyms) return;
+
+ mMainloop = llpa_glib_mainloop_new(g_main_context_default());
+ if (mMainloop)
+ {
+ pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop);
+ if (api)
+ {
+ pa_proplist *proplist = llpa_proplist_new();
+ if (proplist)
+ {
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1");
+
+ // plain old pa_context_new() is broken!
+ mPAContext = llpa_context_new_with_proplist(api, NULL, proplist);
+ llpa_proplist_free(proplist);
+ }
+ }
+ }
+
+ // Now we've set up a PA context and mainloop, try connecting the
+ // PA context to a PA daemon.
+ if (mPAContext)
+ {
+ llpa_context_set_state_callback(mPAContext, callback_context_state, this);
+ pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN?
+ if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0)
+ {
+ // Okay! We haven't definitely connected, but we
+ // haven't definitely failed yet.
+ }
+ else
+ {
+ // Failed to connect to PA manager... we'll leave
+ // things like that. Perhaps we should try again later.
+ }
+ }
+}
+
+void LinuxVolumeCatcherImpl::cleanup()
+{
+ mConnected = false;
+
+ if (mGotSyms && mPAContext)
+ {
+ llpa_context_disconnect(mPAContext);
+ llpa_context_unref(mPAContext);
+ }
+ mPAContext = NULL;
+
+ if (mGotSyms && mMainloop)
+ {
+ llpa_glib_mainloop_free(mMainloop);
+ }
+ mMainloop = NULL;
+}
+
+void LinuxVolumeCatcherImpl::setVolume(F32 volume)
+{
+ mDesiredVolume = volume;
+
+ if (!mGotSyms) return;
+
+ if (mConnected && mPAContext)
+ {
+ update_all_volumes(mDesiredVolume);
+ }
+
+ pump();
+}
+
+void LinuxVolumeCatcherImpl::pump()
+{
+ gboolean may_block = FALSE;
+ g_main_context_iteration(g_main_context_default(), may_block);
+}
+
+void LinuxVolumeCatcherImpl::connected_okay()
+{
+ pa_operation *op;
+
+ // fetch global list of existing sinkinputs
+ if ((op = llpa_context_get_sink_input_info_list(mPAContext,
+ callback_discovered_sinkinput,
+ this)))
+ {
+ llpa_operation_unref(op);
+ }
+
+ // subscribe to future global sinkinput changes
+ llpa_context_set_subscribe_callback(mPAContext,
+ callback_subscription_alert,
+ this);
+ if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t)
+ (PA_SUBSCRIPTION_MASK_SINK_INPUT),
+ NULL, NULL)))
+ {
+ llpa_operation_unref(op);
+ }
+}
+
+void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume)
+{
+ for (std::set<U32>::iterator it = mSinkInputIndices.begin();
+ it != mSinkInputIndices.end(); ++it)
+ {
+ update_index_volume(*it, volume);
+ }
+}
+
+void LinuxVolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
+{
+ static pa_cvolume cvol;
+ llpa_cvolume_set(&cvol, mSinkInputNumChannels[index],
+ llpa_sw_volume_from_linear(volume));
+
+ pa_context *c = mPAContext;
+ uint32_t idx = index;
+ const pa_cvolume *cvolumep = &cvol;
+ pa_context_success_cb_t cb = NULL; // okay as null
+ void *userdata = NULL; // okay as null
+
+ pa_operation *op;
+ if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata)))
+ {
+ llpa_operation_unref(op);
+ }
+}
+
+
+void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+ llassert(impl);
+
+ if (0 == eol)
+ {
+ pa_proplist *proplist = sii->proplist;
+ pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID));
+
+ if (sinkpid == getpid()) // does the discovered sinkinput belong to this process?
+ {
+ bool is_new = (impl->mSinkInputIndices.find(sii->index) ==
+ impl->mSinkInputIndices.end());
+
+ impl->mSinkInputIndices.insert(sii->index);
+ impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels;
+
+ if (is_new)
+ {
+ // new!
+ impl->update_index_volume(sii->index, impl->mDesiredVolume);
+ }
+ else
+ {
+ // seen it already, do nothing.
+ }
+ }
+ }
+}
+
+void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+ llassert(impl);
+
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
+ PA_SUBSCRIPTION_EVENT_REMOVE)
+ {
+ // forget this sinkinput, if we were caring about it
+ impl->mSinkInputIndices.erase(index);
+ impl->mSinkInputNumChannels.erase(index);
+ }
+ else
+ {
+ // ask for more info about this new sinkinput
+ pa_operation *op;
+ if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl)))
+ {
+ llpa_operation_unref(op);
+ }
+ }
+ break;
+
+ default:;
+ }
+}
+
+void callback_context_state(pa_context *context, void *userdata)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+ llassert(impl);
+
+ switch (llpa_context_get_state(context))
+ {
+ case PA_CONTEXT_READY:
+ impl->mConnected = true;
+ impl->connected_okay();
+ break;
+ case PA_CONTEXT_TERMINATED:
+ impl->mConnected = false;
+ break;
+ case PA_CONTEXT_FAILED:
+ impl->mConnected = false;
+ break;
+ default:;
+ }
+}
+
+/////////////////////////////////////////////////////
+
+LinuxVolumeCatcher::LinuxVolumeCatcher()
+{
+ pimpl = new LinuxVolumeCatcherImpl();
+}
+
+LinuxVolumeCatcher::~LinuxVolumeCatcher()
+{
+ delete pimpl;
+ pimpl = NULL;
+}
+
+void LinuxVolumeCatcher::setVolume(F32 volume)
+{
+ llassert(pimpl);
+ pimpl->setVolume(volume);
+}
+
+void LinuxVolumeCatcher::pump()
+{
+ llassert(pimpl);
+ pimpl->pump();
+}
+
+#else // !LL_PULSEAUDIO_ENABLED
+
+// stub.
+
+LinuxVolumeCatcher::LinuxVolumeCatcher()
+{
+ pimpl = NULL;
+}
+
+LinuxVolumeCatcher::~LinuxVolumeCatcher()
+{
+}
+
+void LinuxVolumeCatcher::setVolume(F32 volume)
+{
+}
+
+void LinuxVolumeCatcher::pump()
+{
+}
+
+#endif // LL_PULSEAUDIO_ENABLED
diff --git a/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc b/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
new file mode 100644
index 0000000000..d806b48428
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
@@ -0,0 +1,21 @@
+// required symbols to grab
+LL_PA_SYM(true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
+LL_PA_SYM(true, pa_context_disconnect, void, pa_context *c);
+LL_PA_SYM(true, pa_context_get_sink_input_info, pa_operation*, pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_get_state, pa_context_state_t, pa_context *c);
+LL_PA_SYM(true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+LL_PA_SYM(true, pa_context_set_sink_input_volume, pa_operation*, pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_unref, void, pa_context *c);
+LL_PA_SYM(true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v);
+LL_PA_SYM(true, pa_operation_unref, void, pa_operation *o);
+LL_PA_SYM(true, pa_proplist_free, void, pa_proplist* p);
+LL_PA_SYM(true, pa_proplist_gets, const char*, pa_proplist *p, const char *key);
+LL_PA_SYM(true, pa_proplist_new, pa_proplist*, void);
+LL_PA_SYM(true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value);
+LL_PA_SYM(true, pa_sw_volume_from_linear, pa_volume_t, double v);
+
+// optional symbols to grab
diff --git a/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc b/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
new file mode 100644
index 0000000000..abf628c96c
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
@@ -0,0 +1,6 @@
+// required symbols to grab
+LL_PA_SYM(true, pa_glib_mainloop_free, void, pa_glib_mainloop* g);
+LL_PA_SYM(true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g);
+LL_PA_SYM(true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c);
+
+// optional symbols to grab
diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp
index 688d3bcd3d..c7aba04492 100644
--- a/indra/media_plugins/webkit/media_plugin_webkit.cpp
+++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp
@@ -43,11 +43,15 @@
#include "llpluginmessageclasses.h"
#include "media_plugin_base.h"
+#if LL_LINUX
+# include "linux_volume_catcher.h"
+#endif // LL_LINUX
+
#if LL_WINDOWS
-#include <direct.h>
+# include <direct.h>
#else
-#include <unistd.h>
-#include <stdlib.h>
+# include <unistd.h>
+# include <stdlib.h>
#endif
#if LL_WINDOWS
@@ -102,6 +106,10 @@ private:
F32 mBackgroundG;
F32 mBackgroundB;
+#if LL_LINUX
+ LinuxVolumeCatcher mLinuxVolumeCatcher;
+#endif // LL_LINUX
+
void setInitState(int state)
{
// std::cerr << "changing init state to " << state << std::endl;
@@ -114,6 +122,10 @@ private:
{
LLQtWebKit::getInstance()->pump( milliseconds );
+#if LL_LINUX
+ mLinuxVolumeCatcher.pump();
+#endif // LL_LINUX
+
checkEditState();
if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
@@ -281,6 +293,7 @@ private:
return false;
};
+ void setVolume(F32 vol);
////////////////////////////////////////////////////////////////////////////////
// virtual
@@ -732,6 +745,14 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl;
}
}
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if(message_name == "set_volume")
+ {
+ F32 volume = message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
{
if(message_name == "size_change")
@@ -998,6 +1019,13 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
}
}
+void MediaPluginWebKit::setVolume(F32 volume)
+{
+#if LL_LINUX
+ mLinuxVolumeCatcher.setVolume(volume);
+#endif // LL_LINUX
+}
+
int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
{
MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data);
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 92d321d8c0..e01b9e4bc6 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -15,7 +15,7 @@ Life itself - please see <http://www.secondlife.com/whatis/>.
5.3. Blank window after minimizing it
5.4. Audio
5.5. 'Alt' key for camera controls doesn't work
- 5.6. In-world streaming movie/music playback
+ 5.6. In-world streaming movie, music and Flash playback
6. Advanced Troubleshooting
6.1. Audio
6.2. OpenGL
@@ -169,12 +169,15 @@ SOLUTION:- Some window managers eat the Alt key for their own purposes; you
example, the 'Windows' key!) which will allow the Alt key to function
properly with mouse actions in Second Life and other applications.
-PROBLEM 6:- In-world movie and/or music playback doesn't work for me.
+PROBLEM 6:- In-world movie, music, or Flash playback doesn't work for me.
SOLUTION:- You need to have a working installation of GStreamer 0.10; this
is usually an optional package for most versions of Linux. If you have
installed GStreamer 0.10 and you can play some music/movies but not others
then you need to install a wider selection of GStreamer plugins, either
- from your vendor or an appropriate third party.
+ from your vendor (i.e. the 'Ugly' plugins) or an appropriate third party.
+ For Flash playback, you need to have Flash 10 installed for your normal
+ web browser (for example, Firefox). PulseAudio is required for Flash
+ volume control / muting to fully function inside Second Life.
6. ADVANCED TROUBLESHOOTING
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index f4bc35002b..08cd101b01 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -286,6 +286,7 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i
}
if (mTodo & CALL_RECOVERDONE)
{
+ LLAppearanceManager::instance().addCOFItemLink(inv_item,false);
gAgentWearables.recoverMissingWearableDone();
}
/*
@@ -1038,7 +1039,7 @@ void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void*
{
return;
}
-
+
if (wearable)
{
llassert(type == wearable->getType());
@@ -1057,6 +1058,7 @@ void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void*
// Somehow the asset doesn't exist in the database.
gAgentWearables.recoverMissingWearable(type,index);
}
+
gInventory.notifyObservers();
@@ -1576,7 +1578,7 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
const LLDynamicArray< LLWearable* >& wearables,
BOOL remove)
{
- lldebugs << "setWearableOutfit() start" << llendl;
+ llinfos << "setWearableOutfit() start" << llendl;
BOOL wearables_to_remove[WT_COUNT];
wearables_to_remove[WT_SHAPE] = FALSE;
@@ -2485,7 +2487,7 @@ class LLFetchAndLinkObserver: public LLInventoryFetchObserver
public:
LLFetchAndLinkObserver(LLInventoryFetchObserver::item_ref_t& ids):
m_ids(ids),
- LLInventoryFetchObserver(true)
+ LLInventoryFetchObserver(true) // retry for missing items
{
}
~LLFetchAndLinkObserver()
@@ -2494,7 +2496,9 @@ public:
virtual void done()
{
gInventory.removeObserver(this);
+
// Link to all fetched items in COF.
+ LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
for (LLInventoryFetchObserver::item_ref_t::iterator it = m_ids.begin();
it != m_ids.end();
++it)
@@ -2506,8 +2510,13 @@ public:
llwarns << "fetch failed!" << llendl;
continue;
}
- link_inventory_item(gAgent.getID(), item->getLinkedUUID(), LLAppearanceManager::instance().getCOF(), item->getName(),
- LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL));
+
+ link_inventory_item(gAgent.getID(),
+ item->getLinkedUUID(),
+ LLAppearanceManager::instance().getCOF(),
+ item->getName(),
+ LLAssetType::AT_LINK,
+ link_waiter);
}
}
private:
@@ -2530,11 +2539,13 @@ void LLInitialWearablesFetch::processWearablesMessage()
#ifdef USE_CURRENT_OUTFIT_FOLDER
ids.push_back(wearable_data->mItemID);
#endif
- // Fetch the wearables
- LLWearableList::instance().getAsset(wearable_data->mAssetID,
- LLStringUtil::null,
- LLWearableDictionary::getAssetType(wearable_data->mType),
- LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data));
+#if 0
+// // Fetch the wearables
+// LLWearableList::instance().getAsset(wearable_data->mAssetID,
+// LLStringUtil::null,
+// LLWearableDictionary::getAssetType(wearable_data->mType),
+// LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data));
+#endif
}
else
{
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index eb4a47664b..c9da08701d 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -301,50 +301,52 @@ void LLOutfitFetch::done()
delete this;
}
-class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
+LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy():
+ mFireCount(0)
{
-public:
- LLUpdateAppearanceOnDestroy():
- mFireCount(0)
- {
- }
+}
- virtual ~LLUpdateAppearanceOnDestroy()
+LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
+{
+ llinfos << "done update appearance on destroy" << llendl;
+
+ if (!LLApp::isExiting())
{
- llinfos << "done update appearance on destroy" << llendl;
-
- if (!LLApp::isExiting())
- {
- LLAppearanceManager::instance().updateAppearanceFromCOF();
- }
+ LLAppearanceManager::instance().updateAppearanceFromCOF();
}
+}
- /* virtual */ void fire(const LLUUID& inv_item)
- {
- llinfos << "callback fired" << llendl;
- mFireCount++;
- }
-private:
- U32 mFireCount;
-};
+void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
+{
+ llinfos << "callback fired" << llendl;
+ mFireCount++;
+}
struct LLFoundData
{
- LLFoundData() : mAssetType(LLAssetType::AT_NONE), mWearable(NULL) {}
+ LLFoundData() :
+ mAssetType(LLAssetType::AT_NONE),
+ mWearableType(WT_INVALID),
+ mWearable(NULL) {}
+
LLFoundData(const LLUUID& item_id,
- const LLUUID& asset_id,
- const std::string& name,
- LLAssetType::EType asset_type) :
+ const LLUUID& asset_id,
+ const std::string& name,
+ const LLAssetType::EType& asset_type,
+ const EWearableType& wearable_type
+ ) :
mItemID(item_id),
mAssetID(asset_id),
mName(name),
mAssetType(asset_type),
+ mWearableType(wearable_type),
mWearable( NULL ) {}
LLUUID mItemID;
LLUUID mAssetID;
std::string mName;
LLAssetType::EType mAssetType;
+ EWearableType mWearableType;
LLWearable* mWearable;
};
@@ -355,14 +357,26 @@ public:
LLWearableHoldingPattern();
~LLWearableHoldingPattern();
- bool pollCompletion();
+ bool pollFetchCompletion();
+ void onFetchCompletion();
bool isFetchCompleted();
bool isTimedOut();
+
+ void checkMissingWearables();
+ bool pollMissingWearables();
+ bool isMissingCompleted();
+ void recoverMissingWearable(EWearableType type);
+ void clearCOFLinksForMissingWearables();
+
+ void onAllComplete();
typedef std::list<LLFoundData> found_list_t;
found_list_t mFoundList;
LLInventoryModel::item_array_t mObjItems;
LLInventoryModel::item_array_t mGestItems;
+ typedef std::set<S32> type_set_t;
+ type_set_t mTypesToRecover;
+ type_set_t mTypesToLink;
S32 mResolved;
LLTimer mWaitTime;
bool mFired;
@@ -389,13 +403,99 @@ bool LLWearableHoldingPattern::isTimedOut()
return mWaitTime.getElapsedTimeF32() > max_wait_time;
}
-bool LLWearableHoldingPattern::pollCompletion()
+void LLWearableHoldingPattern::checkMissingWearables()
+{
+ std::vector<S32> found_by_type(WT_COUNT,0);
+ std::vector<S32> requested_by_type(WT_COUNT,0);
+ for (found_list_t::iterator it = mFoundList.begin(); it != mFoundList.end(); ++it)
+ {
+ LLFoundData &data = *it;
+ if (data.mWearableType < WT_COUNT)
+ requested_by_type[data.mWearableType]++;
+ if (data.mWearable)
+ found_by_type[data.mWearableType]++;
+ }
+
+ for (S32 type = 0; type < WT_COUNT; ++type)
+ {
+ llinfos << "type " << type << " requested " << requested_by_type[type] << " found " << found_by_type[type] << llendl;
+ if (found_by_type[type] > 0)
+ continue;
+ if (
+ // Need to recover if at least one wearable of that type
+ // was requested but none was found (prevent missing
+ // pants)
+ (requested_by_type[type] > 0) ||
+ // or if type is a body part and no wearables were found.
+ ((type == WT_SHAPE) || (type == WT_SKIN) || (type == WT_HAIR) || (type == WT_EYES)))
+ {
+ mTypesToRecover.insert(type);
+ mTypesToLink.insert(type);
+ recoverMissingWearable((EWearableType)type);
+ llwarns << "need to replace " << type << llendl;
+ }
+ }
+
+ if (!pollMissingWearables())
+ {
+ mWaitTime.reset();
+ doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
+ }
+}
+
+void LLWearableHoldingPattern::onAllComplete()
+{
+ // Activate all gestures in this folder
+ if (mGestItems.count() > 0)
+ {
+ llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
+
+ LLGestureManager::instance().activateGestures(mGestItems);
+
+ // Update the inventory item labels to reflect the fact
+ // they are active.
+ LLViewerInventoryCategory* catp =
+ gInventory.getCategory(LLAppearanceManager::instance().getCOF());
+
+ if (catp)
+ {
+ gInventory.updateCategory(catp);
+ gInventory.notifyObservers();
+ }
+ }
+
+ // Update wearables.
+ llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
+ LLAppearanceManager::instance().updateAgentWearables(this, false);
+
+ // Update attachments to match those requested.
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
+ LLAgentWearables::userUpdateAttachments(mObjItems);
+ }
+
+ if (isFetchCompleted() && isMissingCompleted())
+ {
+ // Only safe to delete if all wearable callbacks and all missing wearables completed.
+ delete this;
+ }
+}
+
+void LLWearableHoldingPattern::onFetchCompletion()
+{
+ checkMissingWearables();
+}
+
+// Runs as an idle callback until all wearables are fetched (or we time out).
+bool LLWearableHoldingPattern::pollFetchCompletion()
{
bool completed = isFetchCompleted();
bool timed_out = isTimedOut();
bool done = completed || timed_out;
- llinfos << "polling, done status: " << completed << " timed out? " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
+ llinfos << "polling, done status: " << completed << " timed out " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
if (done)
{
@@ -406,46 +506,184 @@ bool LLWearableHoldingPattern::pollCompletion()
llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
}
- // Activate all gestures in this folder
- if (mGestItems.count() > 0)
- {
- llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
-
- LLGestureManager::instance().activateGestures(mGestItems);
-
- // Update the inventory item labels to reflect the fact
- // they are active.
- LLViewerInventoryCategory* catp =
- gInventory.getCategory(LLAppearanceManager::instance().getCOF());
+ onFetchCompletion();
+ }
+ return done;
+}
- if (catp)
- {
- gInventory.updateCategory(catp);
- gInventory.notifyObservers();
- }
- }
+class RecoveredItemLinkCB: public LLInventoryCallback
+{
+public:
+ RecoveredItemLinkCB(EWearableType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+ mHolder(holder),
+ mWearable(wearable),
+ mType(type)
+ {
+ }
+ void fire(const LLUUID& item_id)
+ {
+ llinfos << "Recovered item link for type " << mType << llendl;
+ mHolder->mTypesToLink.erase(mType);
+ // Add wearable to FoundData for actual wearing
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
- // Update wearables.
- llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
- LLAppearanceManager::instance().updateAgentWearables(this, false);
-
- // Update attachments to match those requested.
- LLVOAvatar* avatar = gAgent.getAvatarObject();
- if( avatar )
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+
+ if (item && linked_item)
{
- llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
- LLAgentWearables::userUpdateAttachments(mObjItems);
+ LLFoundData found(linked_item->getUUID(),
+ linked_item->getAssetUUID(),
+ linked_item->getName(),
+ linked_item->getType(),
+ linked_item->isWearableType() ? linked_item->getWearableType() : WT_INVALID
+ );
+ found.mWearable = mWearable;
+ mHolder->mFoundList.push_front(found);
}
+ else
+ {
+ llwarns << "inventory item or link not found for recovered wearable" << llendl;
+ }
+ }
+private:
+ LLWearableHoldingPattern* mHolder;
+ LLWearable *mWearable;
+ EWearableType mType;
+};
- if (completed)
+class RecoveredItemCB: public LLInventoryCallback
+{
+public:
+ RecoveredItemCB(EWearableType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+ mHolder(holder),
+ mWearable(wearable),
+ mType(type)
+ {
+ }
+ void fire(const LLUUID& item_id)
+ {
+ llinfos << "Recovered item for type " << mType << llendl;
+ LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
+ LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
+ mHolder->mTypesToRecover.erase(mType);
+ link_inventory_item( gAgent.getID(),
+ item_id,
+ LLAppearanceManager::instance().getCOF(),
+ itemp->getName(),
+ LLAssetType::AT_LINK,
+ cb);
+ }
+private:
+ LLWearableHoldingPattern* mHolder;
+ LLWearable *mWearable;
+ EWearableType mType;
+};
+
+void LLWearableHoldingPattern::recoverMissingWearable(EWearableType type)
+{
+ // Try to recover by replacing missing wearable with a new one.
+ LLNotificationsUtil::add("ReplacedMissingWearable");
+ lldebugs << "Wearable " << LLWearableDictionary::getTypeLabel(type)
+ << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
+ LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
+
+ // Add a new one in the lost and found folder.
+ const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+ LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this);
+
+ create_inventory_item(gAgent.getID(),
+ gAgent.getSessionID(),
+ lost_and_found_id,
+ wearable->getTransactionID(),
+ wearable->getName(),
+ wearable->getDescription(),
+ wearable->getAssetType(),
+ LLInventoryType::IT_WEARABLE,
+ wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ cb);
+}
+
+bool LLWearableHoldingPattern::isMissingCompleted()
+{
+ return mTypesToLink.size()==0 && mTypesToRecover.size()==0;
+}
+
+void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
+{
+ for (found_list_t::iterator it = mFoundList.begin(); it != mFoundList.end(); ++it)
+ {
+ LLFoundData &data = *it;
+ if ((data.mWearableType < WT_COUNT) && (!data.mWearable))
{
- // Only safe to delete if all wearable callbacks completed.
- delete this;
+ // Wearable link that was never resolved; remove links to it from COF
+ llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
+ LLAppearanceManager::instance().removeCOFItemLinks(data.mItemID,false);
}
}
+}
+
+bool LLWearableHoldingPattern::pollMissingWearables()
+{
+ bool timed_out = isTimedOut();
+ bool missing_completed = isMissingCompleted();
+ bool done = timed_out || missing_completed;
+
+ llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
+ << " links " << mTypesToLink.size()
+ << " wearables, timed out " << timed_out
+ << " elapsed " << mWaitTime.getElapsedTimeF32()
+ << " done " << done << llendl;
+
+ if (done)
+ {
+ clearCOFLinksForMissingWearables();
+ onAllComplete();
+ }
return done;
}
+static void onWearableAssetFetch(LLWearable* wearable, void* data)
+{
+ LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
+ holder->mResolved += 1; // just counting callbacks, not successes.
+ llinfos << "onWearableAssetFetch, resolved count " << holder->mResolved << " of requested " << holder->mFoundList.size() << llendl;
+ if (wearable)
+ {
+ llinfos << "wearable found, type " << wearable->getType() << " asset " << wearable->getAssetID() << llendl;
+ }
+ else
+ {
+ llwarns << "no wearable found" << llendl;
+ }
+
+ if (holder->mFired)
+ {
+ llwarns << "called after holder fired" << llendl;
+ return;
+ }
+
+ if (!wearable)
+ {
+ return;
+ }
+
+ for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
+ iter != holder->mFoundList.end(); ++iter)
+ {
+ LLFoundData& data = *iter;
+ if(wearable->getAssetID() == data.mAssetID)
+ {
+ data.mWearable = wearable;
+ // Failing this means inventory or asset server are corrupted in a way we don't handle.
+ llassert((data.mWearableType < WT_COUNT) && (wearable->getType() == data.mWearableType));
+ break;
+ }
+ }
+}
+
+
static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
{
LLInventoryModel::item_array_t new_items;
@@ -473,30 +711,6 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
items = new_items;
}
-static void onWearableAssetFetch(LLWearable* wearable, void* data)
-{
- LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
- if (holder->mFired)
- {
- llwarns << "called after holder fired" << llendl;
- }
-
- if(wearable)
- {
- for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
- iter != holder->mFoundList.end(); ++iter)
- {
- LLFoundData& data = *iter;
- if(wearable->getAssetID() == data.mAssetID)
- {
- data.mWearable = wearable;
- break;
- }
- }
- }
- holder->mResolved += 1;
-}
-
const LLUUID LLAppearanceManager::getCOF() const
{
return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
@@ -718,7 +932,8 @@ void LLAppearanceManager::linkAll(const LLUUID& category,
void LLAppearanceManager::updateCOF(const LLUUID& category, bool append)
{
- llinfos << "starting" << llendl;
+ LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
+ llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
const LLUUID cof = getCOF();
@@ -893,6 +1108,9 @@ void LLAppearanceManager::updateAppearanceFromCOF()
// wearables can be resolved immediately, then the
// callback will be called (and this object deleted)
// before the final getNextData().
+
+ // BAP future cleanup - no point having found_container when
+ // mFoundList already has all the info.
LLDynamicArray<LLFoundData> found_container;
for(S32 i = 0; i < wear_items.count(); ++i)
{
@@ -903,7 +1121,21 @@ void LLAppearanceManager::updateAppearanceFromCOF()
LLFoundData found(linked_item->getUUID(),
linked_item->getAssetUUID(),
linked_item->getName(),
- linked_item->getType());
+ linked_item->getType(),
+ linked_item->isWearableType() ? linked_item->getWearableType() : WT_INVALID
+ );
+
+#if 0
+ // Fault injection: uncomment this block to test asset
+ // fetch failures (should be replaced by new defaults in
+ // lost&found).
+ if (found.mWearableType == WT_SHAPE || found.mWearableType == WT_JACKET)
+ {
+ found.mAssetID.generate(); // Replace with new UUID, guaranteed not to exist in DB
+
+ }
+#endif
+
holder->mFoundList.push_front(found);
found_container.put(found);
}
@@ -923,7 +1155,9 @@ void LLAppearanceManager::updateAppearanceFromCOF()
for(S32 i = 0; i < found_container.count(); ++i)
{
LLFoundData& found = found_container.get(i);
-
+
+ llinfos << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
+
// Fetch the wearables about to be worn.
LLWearableList::instance().getAsset(found.mAssetID,
found.mName,
@@ -933,9 +1167,9 @@ void LLAppearanceManager::updateAppearanceFromCOF()
}
- if (!holder->pollCompletion())
+ if (!holder->pollFetchCompletion())
{
- doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollCompletion,holder));
+ doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder));
}
}
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 28b51ee0f6..e7e2f33520 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -151,6 +151,18 @@ public:
BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const;
};
+class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
+{
+public:
+ LLUpdateAppearanceOnDestroy();
+ virtual ~LLUpdateAppearanceOnDestroy();
+ /* virtual */ void fire(const LLUUID& inv_item);
+
+private:
+ U32 mFireCount;
+};
+
+
#define SUPPORT_ENSEMBLES 0
LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 5b59f52fa5..d0513c35ce 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -1925,7 +1925,7 @@ bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
// - links to attachments
// - links to gestures
// - links to ensemble folders
- LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); // BAP - safe?
+ LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem();
if (linked_item)
{
LLAssetType::EType type = linked_item->getType();
@@ -1936,7 +1936,7 @@ bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
}
else
{
- LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); // BAP - safe?
+ LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory();
// BAP remove AT_NONE support after ensembles are fully working?
return (linked_category &&
((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
@@ -2962,8 +2962,6 @@ void LLFolderBridge::modifyOutfit(BOOL append)
LLViewerInventoryCategory* cat = getCategory();
if(!cat) return;
- // BAP - was:
- // wear_inventory_category_on_avatar( cat, append );
LLAppearanceManager::instance().wearInventoryCategory( cat, FALSE, append );
}
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
index 61463ed843..706787e824 100644
--- a/indra/newview/llpanelgroup.cpp
+++ b/indra/newview/llpanelgroup.cpp
@@ -337,7 +337,7 @@ void LLPanelGroup::update(LLGroupChange gc)
childSetToolTip("group_name",gdatap->mName);
LLGroupData agent_gdatap;
- bool is_member = gAgent.getGroupData(mID,agent_gdatap);
+ bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlike();
bool join_btn_visible = !is_member && gdatap->mOpenEnrollment;
mButtonJoin->setVisible(join_btn_visible);
@@ -466,7 +466,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)
}
LLGroupData agent_gdatap;
- bool is_member = gAgent.getGroupData(mID,agent_gdatap);
+ bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlike();
tab_roles->setVisible(is_member);
tab_notices->setVisible(is_member);
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
index 6210973dae..5f913d5469 100644
--- a/indra/newview/llpanelgroupnotices.cpp
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -652,6 +652,9 @@ void LLPanelGroupNotices::setGroupID(const LLUUID& id)
LLGroupDropTarget* target = getChild<LLGroupDropTarget> ("drop_target");
target->setPanel (this);
target->setGroup (mGroupID);
+
+ if(mViewMessage)
+ mViewMessage->clear();
activate();
}
diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp
index e1676d2299..173ff8d68b 100644
--- a/indra/newview/llpanelnearbymedia.cpp
+++ b/indra/newview/llpanelnearbymedia.cpp
@@ -535,7 +535,9 @@ void LLPanelNearByMedia::refreshParcelItems()
const LLSD &choice_llsd = mShowCtrl->getSelectedValue();
MediaClass choice = (MediaClass)choice_llsd.asInteger();
// Only show "special parcel items" if "All" or "Within" filter
- bool should_include = choice == MEDIA_CLASS_ALL || choice == MEDIA_CLASS_WITHIN_PARCEL;
+ // (and if media is "enabled")
+ bool should_include = gSavedSettings.getBOOL("AudioStreamingMedia") &&
+ (choice == MEDIA_CLASS_ALL || choice == MEDIA_CLASS_WITHIN_PARCEL);
// First Parcel Media: add or remove it as necessary
if (should_include && LLViewerMedia::hasParcelMedia())
@@ -579,8 +581,8 @@ void LLPanelNearByMedia::refreshParcelItems()
"parcel media");
}
- // Next Parcel Audio: add or remove it as necessary
- if (should_include && LLViewerMedia::hasParcelAudio())
+ // Next Parcel Audio: add or remove it as necessary (don't show if disabled in prefs)
+ if (should_include && LLViewerMedia::hasParcelAudio() && gSavedSettings.getBOOL("AudioStreamingMusic"))
{
// Yes, there is parcel audio.
if (NULL == mParcelAudioItem)
@@ -693,14 +695,16 @@ void LLPanelNearByMedia::refreshList()
}
}
}
- mDisableAllCtrl->setEnabled(LLViewerMedia::isAnyMediaShowing() ||
- LLViewerMedia::isParcelMediaPlaying() ||
- LLViewerMedia::isParcelAudioPlaying());
- mEnableAllCtrl->setEnabled(disabled_count > 0 ||
- // parcel media (if we have it, and it isn't playing, enable "start")
- (LLViewerMedia::hasParcelMedia() && ! LLViewerMedia::isParcelMediaPlaying()) ||
- // parcel audio (if we have it, and it isn't playing, enable "start")
- (LLViewerMedia::hasParcelAudio() && ! LLViewerMedia::isParcelAudioPlaying()));
+ mDisableAllCtrl->setEnabled(gSavedSettings.getBOOL("AudioStreamingMedia") &&
+ (LLViewerMedia::isAnyMediaShowing() ||
+ LLViewerMedia::isParcelMediaPlaying() ||
+ LLViewerMedia::isParcelAudioPlaying()));
+ mEnableAllCtrl->setEnabled(gSavedSettings.getBOOL("AudioStreamingMedia") &&
+ (disabled_count > 0 ||
+ // parcel media (if we have it, and it isn't playing, enable "start")
+ (LLViewerMedia::hasParcelMedia() && ! LLViewerMedia::isParcelMediaPlaying()) ||
+ // parcel audio (if we have it, and it isn't playing, enable "start")
+ (LLViewerMedia::hasParcelAudio() && ! LLViewerMedia::isParcelAudioPlaying())));
// Iterate over the rows in the control, updating ones whose impl exists, and deleting ones whose impl has gone away.
std::vector<LLScrollListItem*> items = mMediaList->getAllData();
@@ -825,10 +829,7 @@ void LLPanelNearByMedia::onZoomMedia(void* user_data)
void LLPanelNearByMedia::onClickParcelMediaPlay()
{
- if (gSavedSettings.getBOOL("AudioStreamingMedia"))
- {
- LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel());
- }
+ LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel());
}
void LLPanelNearByMedia::onClickParcelMediaStop()
@@ -854,8 +855,8 @@ void LLPanelNearByMedia::onClickParcelAudioStart()
if (!gAudiop)
return;
+
gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL());
-
}
void LLPanelNearByMedia::onClickParcelAudioPlay()
@@ -957,15 +958,29 @@ void LLPanelNearByMedia::onMoreLess()
void LLPanelNearByMedia::updateControls()
{
+ if (! gSavedSettings.getBOOL("AudioStreamingMedia"))
+ {
+ // Just show disabled controls
+ showDisabledControls();
+ return;
+ }
+
LLUUID selected_media_id = mMediaList->getValue().asUUID();
if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID)
{
- showTimeBasedControls(LLViewerMedia::isParcelAudioPlaying(),
+ if (!LLViewerMedia::hasParcelAudio() || !gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ // Shouldn't happen, but do this anyway
+ showDisabledControls();
+ }
+ else {
+ showTimeBasedControls(LLViewerMedia::isParcelAudioPlaying(),
false, // include_zoom
false, // is_zoomed
gSavedSettings.getBOOL("MuteMusic"),
gSavedSettings.getF32("AudioLevelMusic") );
+ }
}
else if (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID)
{
@@ -1079,15 +1094,18 @@ void LLPanelNearByMedia::onClickSelectedMediaPlay()
{
LLViewerMediaImpl *impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ?
((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id);
- if (NULL != impl && impl->isMediaTimeBased() && impl->isMediaPaused())
- {
- // Aha! It's really time-based media that's paused, so unpause
- impl->play();
- return;
- }
- else if (NULL != impl && impl->isParcelMedia())
+ if (NULL != impl)
{
- LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel());
+ if (impl->isMediaTimeBased() && impl->isMediaPaused())
+ {
+ // Aha! It's really time-based media that's paused, so unpause
+ impl->play();
+ return;
+ }
+ else if (impl->isParcelMedia())
+ {
+ LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel());
+ }
}
}
}
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index e83c882866..732c23982b 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -358,8 +358,10 @@ void LLStatusBar::refresh()
bool mute_audio = LLAppViewer::instance()->getMasterSystemAudioMute();
mBtnVolume->setToggleState(mute_audio);
- // Don't show media toggle if there's no media, parcel media, and no parcel audio
- mMediaToggle->setVisible(LLViewerMedia::hasInWorldMedia() || LLViewerMedia::hasParcelMedia() || LLViewerMedia::hasParcelAudio());
+ // Disable media toggle if there's no media, parcel media, and no parcel audio
+ // (or if media is disabled)
+ mMediaToggle->setEnabled(gSavedSettings.getBOOL("AudioStreamingMedia") &&
+ (LLViewerMedia::hasInWorldMedia() || LLViewerMedia::hasParcelMedia() || LLViewerMedia::hasParcelAudio()));
// Note the "sense" of the toggle is opposite whether media is playing or not
mMediaToggle->setValue(! (LLViewerMedia::isAnyMediaShowing() ||
LLViewerMedia::isParcelMediaPlaying() ||
@@ -547,13 +549,13 @@ void LLStatusBar::onMouseEnterNearbyMedia()
LLButton* nearby_media_btn = getChild<LLButton>( "media_toggle_btn" );
LLRect nearby_media_btn_rect = nearby_media_btn->calcScreenRect();
nearby_media_rect.setLeftTopAndSize(nearby_media_btn_rect.mLeft -
- (nearby_media_rect.getWidth() - nearby_media_btn_rect.getWidth())/2,
- nearby_media_btn_rect.mBottom,
- nearby_media_rect.getWidth(),
- nearby_media_rect.getHeight());
+ (nearby_media_rect.getWidth() - nearby_media_btn_rect.getWidth())/2,
+ nearby_media_btn_rect.mBottom,
+ nearby_media_rect.getWidth(),
+ nearby_media_rect.getHeight());
// force onscreen
nearby_media_rect.translate(popup_holder->getRect().getWidth() - nearby_media_rect.mRight, 0);
-
+
// show the master volume pull-down
mPanelNearByMedia->setShape(nearby_media_rect);
mPanelNearByMedia->setVisible(TRUE);
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index f1ca56a8ae..86336e353c 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -952,7 +952,10 @@ void LLViewerMedia::setAllMediaEnabled(bool val)
LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel());
}
- if (!LLViewerMedia::isParcelAudioPlaying() && gAudiop && LLViewerMedia::hasParcelAudio())
+ if (gSavedSettings.getBOOL("AudioStreamingMusic") &&
+ !LLViewerMedia::isParcelAudioPlaying() &&
+ gAudiop &&
+ LLViewerMedia::hasParcelAudio())
{
gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL());
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 989cfae464..02bde51fb6 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -6897,7 +6897,8 @@ class LLToolsEditLinkedParts : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- BOOL select_individuals = gSavedSettings.getBOOL("EditLinkedParts");
+ BOOL select_individuals = !gSavedSettings.getBOOL("EditLinkedParts");
+ gSavedSettings.setBOOL( "EditLinkedParts", select_individuals );
if (select_individuals)
{
LLSelectMgr::getInstance()->demoteSelectionToIndividuals();
diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml
index 41e53be29e..f913c58cc9 100644
--- a/indra/newview/skins/default/xui/en/panel_group_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_general.xml
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
label="General"
- follows="all"
height="604"
- width="303"
+ width="304"
class="panel_group_general"
name="general_tab">
<panel.string
@@ -26,7 +25,7 @@ Hover your mouse over the options for more help.
top="0"
left="0"
height="129"
- width="313"
+ width="304"
layout="topleft">
<texture_picker
follows="left|top"
diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
index b196d8eeb7..4e57b428bd 100644
--- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
@@ -81,80 +81,129 @@ background_visible="true"
width="292"
border_size="0">
<layout_panel
- bg_alpha_color="DkGray2"
- bg_opaque_color="DkGray2"
- background_visible="true"
- background_opaque="true"
- name="group_accordions"
- follows="all"
- layout="topleft"
- auto_resize="true"
- >
+ bg_alpha_color="DkGray2"
+ bg_opaque_color="DkGray2"
+ background_visible="true"
+ background_opaque="true"
+ name="group_accordions"
+ follows="all"
+ layout="topleft"
+ auto_resize="true">
<accordion
left="0"
top="0"
- single_expansion="true"
- follows="all"
- layout="topleft"
- name="groups_accordion">
- <accordion_tab
+ single_expansion="true"
+ follows="all"
+ layout="topleft"
+ name="groups_accordion">
+ <accordion_tab
expanded="true"
layout="topleft"
name="group_general_tab"
title="General">
- <panel
- border="false"
- class="panel_group_general"
- filename="panel_group_general.xml"
- layout="topleft"
- left="0"
- follows="all"
- help_topic="group_general_tab"
- name="group_general_tab_panel"
- top="0" />
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="profile_scroll"
+ opaque="true"
+ height="604"
+ width="304"
+ top="0">
+ <panel
+ border="false"
+ class="panel_group_general"
+ filename="panel_group_general.xml"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ help_topic="group_general_tab"
+ name="group_general_tab_panel"
+ top="0" />
+ </scroll_container>
</accordion_tab>
<accordion_tab
expanded="false"
layout="topleft"
name="group_roles_tab"
title="Roles">
- <panel
- border="false"
- class="panel_group_roles"
- filename="panel_group_roles.xml"
- layout="topleft"
- left="0"
- name="group_roles_tab_panel"
- top="0" />
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="profile_scroll"
+ opaque="true"
+ height="680"
+ width="304"
+ top="0">
+
+ <panel
+ border="false"
+ class="panel_group_roles"
+ filename="panel_group_roles.xml"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ name="group_roles_tab_panel"
+ top="0" />
+ </scroll_container>
</accordion_tab>
<accordion_tab
- expanded="false"
+ expanded="false"
+ layout="topleft"
+ name="group_notices_tab"
+ title="Notices">
+ <scroll_container
+ color="DkGray2"
+ follows="all"
layout="topleft"
- name="group_notices_tab"
- title="Notices">
- <panel
- border="false"
- class="panel_group_notices"
- filename="panel_group_notices.xml"
- layout="topleft"
- left="0"
- help_topic="group_notices_tab"
- name="group_notices_tab_panel"
- top="0" />
+ left="0"
+ name="profile_scroll"
+ opaque="true"
+ height="530"
+ width="304"
+ top="0">
+
+ <panel
+ border="false"
+ class="panel_group_notices"
+ filename="panel_group_notices.xml"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ help_topic="group_notices_tab"
+ name="group_notices_tab_panel"
+ top="0" />
+ </scroll_container>
</accordion_tab>
- <accordion_tab
- expanded="false"
+ <accordion_tab
+ expanded="false"
+ layout="topleft"
+ name="group_land_tab"
+ title="Land/Assets">
+ <scroll_container
+ color="DkGray2"
+ follows="all"
layout="topleft"
- name="group_land_tab"
- title="Land/Assets">
- <panel
- border="false"
- class="panel_group_land_money"
- filename="panel_group_land_money.xml"
- layout="topleft"
- left="0"
- name="group_land_tab_panel"
- top="0" />
+ left="0"
+ name="profile_scroll"
+ opaque="true"
+ height="500"
+ width="304"
+ top="0">
+
+ <panel
+ border="false"
+ class="panel_group_land_money"
+ filename="panel_group_land_money.xml"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ name="group_land_tab_panel"
+ top="0" />
+ </scroll_container>
</accordion_tab>
</accordion>
</layout_panel>
diff --git a/indra/newview/skins/default/xui/en/panel_group_land_money.xml b/indra/newview/skins/default/xui/en/panel_group_land_money.xml
index 38b0f234d5..7996a89e72 100644
--- a/indra/newview/skins/default/xui/en/panel_group_land_money.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_land_money.xml
@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
border="false"
- follows="all"
height="500"
label="Land &amp; L$"
layout="topleft"
left="0"
name="land_money_tab"
top="0"
- width="313">
+ width="304">
<panel.string
name="help_text">
A warning appears until the Total Land in Use is less than or = to the Total Contribution.
diff --git a/indra/newview/skins/default/xui/en/panel_group_notices.xml b/indra/newview/skins/default/xui/en/panel_group_notices.xml
index 5f46ad7860..731b3c119c 100644
--- a/indra/newview/skins/default/xui/en/panel_group_notices.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_notices.xml
@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- follows="all"
height="530"
label="Notices"
layout="topleft"
left="0"
name="notices_tab"
top="0"
- width="313">
+ width="304">
<panel.string
name="help_text">
Notices let you send a message and an optionally attached item.
@@ -44,7 +43,7 @@ Maximum 200 per group daily
right="-1"
name="notice_list"
top_pad="0"
- width="313">
+ width="304">
<scroll_list.columns
label=""
name="icon"
diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml
index 7557e2cc5e..19c0da4f08 100644
--- a/indra/newview/skins/default/xui/en/panel_group_roles.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml
@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- follows="all"
height="680"
label="Members &amp; Roles"
layout="topleft"
left="0"
top="0"
name="roles_tab"
- width="313">
+ width="304">
<panel.string
name="default_needs_apply_text">
There are unsaved changes
@@ -31,7 +30,7 @@
tab_height="22"
tab_min_width="90"
top="0"
- width="313">
+ width="304">
<panel
border="false"
follows="all"
diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml
index d72e175bc4..412485e03f 100644
--- a/indra/newview/skins/default/xui/en/panel_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile.xml
@@ -284,10 +284,11 @@
width="300" />
<expandable_text
follows="all"
- height="113"
+ height="103"
layout="topleft"
left="7"
name="sl_groups"
+ textbox.max_length="512"
top_pad="0"
translate="false"
width="290"
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 96c61b69f5..4167401338 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -102,7 +102,7 @@
left_pad="15"
top="2"
name="media_toggle_btn"
- tool_tip="Click to toggle media"
+ tool_tip="Start/Stop All Media (Music, Video, Web pages)"
width="16" >
</button>
<button
diff --git a/indra/newview/skins/default/xui/en/widgets/tab_container.xml b/indra/newview/skins/default/xui/en/widgets/tab_container.xml
index 4a163fc1e3..8d6b0c1cfe 100644
--- a/indra/newview/skins/default/xui/en/widgets/tab_container.xml
+++ b/indra/newview/skins/default/xui/en/widgets/tab_container.xml
@@ -16,17 +16,17 @@ label_pad_left - padding to the left of tab button labels
tab_bottom_image_unselected="Toolbar_Left_Off"
tab_bottom_image_selected="Toolbar_Left_Selected"
tab_left_image_unselected="SegmentedBtn_Left_Disabled"
- tab_left_image_selected="SegmentedBtn_Left_Off"/>
+ tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>
<middle_tab tab_top_image_unselected="TabTop_Middle_Off"
tab_top_image_selected="TabTop_Middle_Selected"
tab_bottom_image_unselected="Toolbar_Middle_Off"
tab_bottom_image_selected="Toolbar_Middle_Selected"
tab_left_image_unselected="SegmentedBtn_Left_Disabled"
- tab_left_image_selected="SegmentedBtn_Left_Off"/>
+ tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>
<last_tab tab_top_image_unselected="TabTop_Right_Off"
tab_top_image_selected="TabTop_Right_Selected"
tab_bottom_image_unselected="Toolbar_Right_Off"
tab_bottom_image_selected="Toolbar_Right_Selected"
tab_left_image_unselected="SegmentedBtn_Left_Disabled"
- tab_left_image_selected="SegmentedBtn_Left_Off"/>
+ tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>
</tab_container>