From ef3e91855880c4865367ef74908b88914a1f2cdc Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 31 May 2013 05:33:41 -0500 Subject: [PATCH 01/36] Initial moap commit. /VERY/ WIP. Primarily committed to clean up unstaged changes. Do not run this, even if it may compile, as it will NOT work (for a myriad of reasons)! --- indra/llcommon/CMakeLists.txt | 4 +- indra/llcommon/llregistry.h | 356 +++ indra/llcommon/llsdparam.cpp | 342 +++ indra/llcommon/llsdparam.h | 126 + indra/llcommon/llsecondlifeurls.cpp | 37 +- indra/llcommon/llsecondlifeurls.h | 14 +- indra/llmessage/aihttptimeoutpolicy.cpp | 1 + indra/llplugin/llpluginclassmedia.h | 4 +- indra/llprimitive/lltextureentry.cpp | 191 +- indra/llprimitive/lltextureentry.h | 41 + indra/llui/CMakeLists.txt | 10 + indra/llui/llalertdialog.cpp | 2 +- indra/llui/llalertdialog.h | 2 +- indra/llui/llfloater.cpp | 5 + indra/llui/lllayoutstack.cpp | 682 +++++ indra/llui/lllayoutstack.h | 113 + indra/llui/llnotifications.cpp | 1 - indra/llui/llnotifications.h | 3 +- indra/llui/llpanel.cpp | 666 +---- indra/llui/llpanel.h | 56 +- indra/llui/lltexteditor.cpp | 23 +- indra/llui/lltexteditor.h | 7 - indra/llui/llui.cpp | 10 +- indra/llui/llui.h | 2 + indra/llui/lluictrl.cpp | 31 + indra/llui/lluictrl.h | 9 + indra/llui/lluictrlfactory.cpp | 13 +- indra/llui/llurlaction.cpp | 159 + indra/llui/llurlaction.h | 98 + indra/llui/llurlentry.cpp | 1188 ++++++++ indra/llui/llurlentry.h | 429 +++ indra/llui/llurlmatch.cpp | 62 + indra/llui/llurlmatch.h | 105 + indra/llui/llurlregistry.cpp | 263 ++ indra/llui/llurlregistry.h | 97 + indra/llvfs/lldir.cpp | 129 +- indra/llvfs/lldir.h | 10 + indra/llwindow/lldragdropwin32.cpp | 10 +- indra/llwindow/llwindow.cpp | 105 +- indra/llwindow/llwindow.h | 13 +- indra/llwindow/llwindowheadless.cpp | 2 +- indra/llwindow/llwindowheadless.h | 5 +- indra/llwindow/llwindowmacosx-objc.h | 36 +- indra/llwindow/llwindowmacosx-objc.mm | 36 +- indra/llwindow/llwindowmacosx.cpp | 40 +- indra/llwindow/llwindowmacosx.h | 5 +- indra/llwindow/llwindowmesaheadless.cpp | 33 +- indra/llwindow/llwindowmesaheadless.h | 5 +- indra/llwindow/llwindowsdl.cpp | 68 +- indra/llwindow/llwindowsdl.h | 6 +- indra/llwindow/llwindowwin32.cpp | 169 +- indra/llwindow/llwindowwin32.h | 5 +- indra/newview/CMakeLists.txt | 15 +- indra/newview/app_settings/settings.xml | 46 + indra/newview/chatbar_as_cmdline.cpp | 7 +- indra/newview/llagent.cpp | 47 +- indra/newview/llagent.h | 22 +- indra/newview/llagentui.cpp | 6 +- indra/newview/llappviewer.cpp | 63 +- indra/newview/llappviewermacosx.cpp | 1 - indra/newview/llchatbar.cpp | 2 +- indra/newview/llcommandhandler.cpp | 171 +- indra/newview/llcommandhandler.h | 53 +- indra/newview/llface.cpp | 45 +- indra/newview/llface.h | 4 + indra/newview/llfloateravatarinfo.cpp | 2 +- indra/newview/llfloaterclassified.cpp | 2 +- indra/newview/llfloaterevent.cpp | 2 +- indra/newview/llfloatergroupinfo.cpp | 3 +- indra/newview/llfloaterhandler.h | 2 +- indra/newview/llfloaterhtmlsimple.cpp | 2 +- indra/newview/llfloaterhud.cpp | 2 +- indra/newview/llfloaternotificationsconsole.h | 1 + indra/newview/llfloaterobjectiminfo.cpp | 116 +- indra/newview/llfloaterparcel.cpp | 3 +- indra/newview/llfloaterpreference.cpp | 8 +- indra/newview/llfloaterreporter.cpp | 6 +- indra/newview/llfloaterteleporthistory.cpp | 23 +- indra/newview/llfloaterurlentry.cpp | 31 +- indra/newview/llfloaterurlentry.h | 4 +- indra/newview/llfloaterwebcontent.cpp | 517 ++++ indra/newview/llfloaterwebcontent.h | 125 + indra/newview/llfloaterworldmap.cpp | 5 +- indra/newview/llloginhandler.cpp | 10 +- indra/newview/llloginhandler.h | 2 +- indra/newview/llmediactrl.cpp | 815 ++---- indra/newview/llmediactrl.h | 145 +- indra/newview/llmediadataclient.cpp | 1070 +++++++ indra/newview/llmediadataclient.h | 416 +++ indra/newview/llpanelavatar.cpp | 2 +- indra/newview/llpaneldirfind.cpp | 6 +- indra/newview/llpanelface.cpp | 77 +- indra/newview/llpanelface.h | 3 + indra/newview/llpanelgeneral.cpp | 3 - indra/newview/llpanellandmedia.cpp | 11 +- indra/newview/llpanellogin.cpp | 512 ++-- indra/newview/llpanellogin.h | 24 +- indra/newview/llpanelmediahud.cpp | 664 ----- indra/newview/llpanelmediahud.h | 111 - indra/newview/llpanelprimmediacontrols.cpp | 1335 +++++++++ indra/newview/llpanelprimmediacontrols.h | 227 ++ indra/newview/llpanelweb.cpp | 48 +- indra/newview/llpanelweb.h | 1 - indra/newview/llselectmgr.cpp | 78 +- indra/newview/llselectmgr.h | 7 +- indra/newview/llslurl.cpp | 526 ++++ indra/newview/llslurl.h | 114 + indra/newview/llstartup.cpp | 483 +-- indra/newview/llstartup.h | 7 +- indra/newview/llstatusbar.cpp | 2 +- indra/newview/lltexturectrl.cpp | 10 +- indra/newview/lltexturectrl.h | 3 + indra/newview/lltoolpie.cpp | 87 +- indra/newview/llurldispatcher.cpp | 356 +-- indra/newview/llurldispatcher.h | 19 +- indra/newview/llviewermedia.cpp | 2604 +++++++++++++++-- indra/newview/llviewermedia.h | 332 ++- indra/newview/llviewermediafocus.cpp | 574 ++-- indra/newview/llviewermediafocus.h | 106 +- indra/newview/llviewermenu.cpp | 9 +- indra/newview/llviewermessage.cpp | 256 +- indra/newview/llviewerobject.cpp | 1 + indra/newview/llviewerobject.h | 5 +- indra/newview/llviewerparcelmedia.cpp | 97 +- indra/newview/llviewerparcelmedia.h | 2 + indra/newview/llviewerregion.cpp | 8 +- indra/newview/llviewertexture.cpp | 33 +- indra/newview/llviewertexture.h | 13 +- indra/newview/llviewertexturelist.cpp | 7 +- indra/newview/llviewerwindow.cpp | 696 +++-- indra/newview/llviewerwindow.h | 12 +- indra/newview/llvopartgroup.cpp | 6 + indra/newview/llvovolume.cpp | 766 ++++- indra/newview/llvovolume.h | 60 + indra/newview/llweb.cpp | 100 +- indra/newview/llweb.h | 30 +- indra/newview/llworldmapmessage.cpp | 6 +- indra/newview/pipeline.cpp | 72 +- indra/newview/pipeline.h | 5 + .../default/xui/en-us/floater_directory.xml | 5 +- .../default/xui/en-us/floater_directory2.xml | 2 +- .../default/xui/en-us/floater_directory3.xml | 5 +- .../skins/default/xui/en-us/floater_tos.xml | 1 + .../xui/en-us/floater_voice_license.xml | 1 + .../skins/default/xui/en-us/notifications.xml | 73 +- .../skins/default/xui/en-us/panel_avatar.xml | 3 +- .../skins/default/xui/en-us/panel_login.xml | 7 +- .../skins/default/xui/en-us/strings.xml | 33 + 148 files changed, 15636 insertions(+), 4633 deletions(-) create mode 100644 indra/llcommon/llregistry.h create mode 100644 indra/llcommon/llsdparam.cpp create mode 100644 indra/llcommon/llsdparam.h create mode 100644 indra/llui/lllayoutstack.cpp create mode 100644 indra/llui/lllayoutstack.h create mode 100644 indra/llui/llurlaction.cpp create mode 100644 indra/llui/llurlaction.h create mode 100644 indra/llui/llurlentry.cpp create mode 100644 indra/llui/llurlentry.h create mode 100644 indra/llui/llurlmatch.cpp create mode 100644 indra/llui/llurlmatch.h create mode 100644 indra/llui/llurlregistry.cpp create mode 100644 indra/llui/llurlregistry.h create mode 100644 indra/newview/llfloaterwebcontent.cpp create mode 100644 indra/newview/llfloaterwebcontent.h create mode 100644 indra/newview/llmediadataclient.cpp create mode 100644 indra/newview/llmediadataclient.h delete mode 100644 indra/newview/llpanelmediahud.cpp delete mode 100644 indra/newview/llpanelmediahud.h create mode 100644 indra/newview/llpanelprimmediacontrols.cpp create mode 100644 indra/newview/llpanelprimmediacontrols.h create mode 100644 indra/newview/llslurl.cpp create mode 100644 indra/newview/llslurl.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ac958ef0e..6f93be79e 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -76,6 +76,7 @@ set(llcommon_SOURCE_FILES llrun.cpp llscopedvolatileaprpool.h llsd.cpp + llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp llsdutil.cpp @@ -204,10 +205,11 @@ set(llcommon_HEADER_FILES llqueuedthread.h llrand.h llrefcount.h - llrefcount.h + llregistry.h llrun.h llsafehandle.h llsd.h + llsdparam.h llsdserialize.h llsdserialize_xml.h llsdutil.h diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h new file mode 100644 index 000000000..853c427a1 --- /dev/null +++ b/indra/llcommon/llregistry.h @@ -0,0 +1,356 @@ +/** + * @file llregistry.h + * @brief template classes for registering name, value pairs in nested scopes, statically, etc. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLREGISTRY_H +#define LL_LLREGISTRY_H + +#include + +#include +#include "llsingleton.h" +#include "llstl.h" + +template +struct LLRegistryDefaultComparator +{ + bool operator()(const T& lhs, const T& rhs) const + { + using std::less; + return less()(lhs, rhs); + } +}; + +template > +class LLRegistry +{ +public: + typedef LLRegistry registry_t; + typedef typename boost::add_reference::type>::type ref_const_key_t; + typedef typename boost::add_reference::type>::type ref_const_value_t; + typedef typename boost::add_reference::type ref_value_t; + typedef typename boost::add_pointer::type>::type ptr_const_value_t; + typedef typename boost::add_pointer::type ptr_value_t; + + class Registrar + { + friend class LLRegistry; + public: + typedef std::map registry_map_t; + + bool add(ref_const_key_t key, ref_const_value_t value) + { + if (mMap.insert(std::make_pair(key, value)).second == false) + { + llwarns << "Tried to register " << key << " but it was already registered!" << llendl; + return false; + } + return true; + } + + void remove(ref_const_key_t key) + { + mMap.erase(key); + } + + void replace(ref_const_key_t key, ref_const_value_t value) + { + mMap[key] = value; + } + + typename registry_map_t::const_iterator beginItems() const + { + return mMap.begin(); + } + + typename registry_map_t::const_iterator endItems() const + { + return mMap.end(); + } + + protected: + ptr_value_t getValue(ref_const_key_t key) + { + typename registry_map_t::iterator found_it = mMap.find(key); + if (found_it != mMap.end()) + { + return &(found_it->second); + } + return NULL; + } + + ptr_const_value_t getValue(ref_const_key_t key) const + { + typename registry_map_t::const_iterator found_it = mMap.find(key); + if (found_it != mMap.end()) + { + return &(found_it->second); + } + return NULL; + } + + // if the registry is used to store pointers, and null values are valid entries + // then use this function to check the existence of an entry + bool exists(ref_const_key_t key) const + { + return mMap.find(key) != mMap.end(); + } + + bool empty() const + { + return mMap.empty(); + } + + protected: + // use currentRegistrar() or defaultRegistrar() + Registrar() {} + ~Registrar() {} + + private: + registry_map_t mMap; + }; + + typedef typename std::list scope_list_t; + typedef typename std::list::iterator scope_list_iterator_t; + typedef typename std::list::const_iterator scope_list_const_iterator_t; + + LLRegistry() + {} + + ~LLRegistry() {} + + ptr_value_t getValue(ref_const_key_t key) + { + for(scope_list_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + ptr_value_t valuep = (*it)->getValue(key); + if (valuep != NULL) return valuep; + } + return mDefaultRegistrar.getValue(key); + } + + ptr_const_value_t getValue(ref_const_key_t key) const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + ptr_value_t valuep = (*it)->getValue(key); + if (valuep != NULL) return valuep; + } + return mDefaultRegistrar.getValue(key); + } + + bool exists(ref_const_key_t key) const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + if ((*it)->exists(key)) return true; + } + + return mDefaultRegistrar.exists(key); + } + + bool empty() const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + if (!(*it)->empty()) return false; + } + + return mDefaultRegistrar.empty(); + } + + + Registrar& defaultRegistrar() + { + return mDefaultRegistrar; + } + + const Registrar& defaultRegistrar() const + { + return mDefaultRegistrar; + } + + + Registrar& currentRegistrar() + { + if (!mActiveScopes.empty()) + { + return *mActiveScopes.front(); + } + + return mDefaultRegistrar; + } + + const Registrar& currentRegistrar() const + { + if (!mActiveScopes.empty()) + { + return *mActiveScopes.front(); + } + + return mDefaultRegistrar; + } + + +protected: + void addScope(Registrar* scope) + { + // newer scopes go up front + mActiveScopes.insert(mActiveScopes.begin(), scope); + } + + void removeScope(Registrar* scope) + { + // O(N) but should be near the beggining and N should be small and this is safer than storing iterators + scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope); + if (iter != mActiveScopes.end()) + { + mActiveScopes.erase(iter); + } + } + +private: + scope_list_t mActiveScopes; + Registrar mDefaultRegistrar; +}; + +template > +class LLRegistrySingleton + : public LLRegistry, + public LLSingleton +{ + friend class LLSingleton; +public: + typedef LLRegistry registry_t; + typedef const KEY& ref_const_key_t; + typedef const VALUE& ref_const_value_t; + typedef VALUE* ptr_value_t; + typedef const VALUE* ptr_const_value_t; + typedef LLSingleton singleton_t; + + class ScopedRegistrar : public registry_t::Registrar + { + public: + ScopedRegistrar(bool push_scope = true) + { + if (push_scope) + { + pushScope(); + } + } + + ~ScopedRegistrar() + { + if (!singleton_t::destroyed()) + { + popScope(); + } + } + + void pushScope() + { + singleton_t::instance().addScope(this); + } + + void popScope() + { + singleton_t::instance().removeScope(this); + } + + ptr_value_t getValueFromScope(ref_const_key_t key) + { + return getValue(key); + } + + ptr_const_value_t getValueFromScope(ref_const_key_t key) const + { + return getValue(key); + } + + private: + typename std::list::iterator mListIt; + }; + + class StaticRegistrar : public registry_t::Registrar + { + public: + virtual ~StaticRegistrar() {} + StaticRegistrar(ref_const_key_t key, ref_const_value_t value) + { + singleton_t::instance().mStaticScope->add(key, value); + } + }; + + // convenience functions + typedef typename LLRegistry::Registrar& ref_registrar_t; + static ref_registrar_t currentRegistrar() + { + return singleton_t::instance().registry_t::currentRegistrar(); + } + + static ref_registrar_t defaultRegistrar() + { + return singleton_t::instance().registry_t::defaultRegistrar(); + } + + static ptr_value_t getValue(ref_const_key_t key) + { + return singleton_t::instance().registry_t::getValue(key); + } + +protected: + // DERIVED_TYPE needs to derive from LLRegistrySingleton + LLRegistrySingleton() + : mStaticScope(NULL) + {} + + virtual void initSingleton() + { + mStaticScope = new ScopedRegistrar(); + } + + virtual ~LLRegistrySingleton() + { + delete mStaticScope; + } + +private: + ScopedRegistrar* mStaticScope; +}; + +// helper macro for doing static registration +#define GLUED_TOKEN(x, y) x ## y +#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y) +#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE); + +#endif diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp new file mode 100644 index 000000000..0e29873bb --- /dev/null +++ b/indra/llcommon/llsdparam.cpp @@ -0,0 +1,342 @@ +/** + * @file llsdparam.cpp + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +// Project includes +#include "llsdparam.h" +#include "llsdutil.h" + +static LLInitParam::Parser::parser_read_func_map_t sReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs; +static const LLSD NO_VALUE_MARKER; + +LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR("LLSD to LLInitParam conversion"); + +// +// LLParamSDParser +// +LLParamSDParser::LLParamSDParser() +: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs) +{ + using boost::bind; + + if (sReadFuncs.empty()) + { + registerParserFuncs(readFlag, &LLParamSDParser::writeFlag); + registerParserFuncs(readS32, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readU32, &LLParamSDParser::writeU32Param); + registerParserFuncs(readF32, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readF64, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readBool, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readString, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readUUID, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readDate, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readURI, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readSD, &LLParamSDParser::writeTypedValue); + } +} + +// special case handling of U32 due to ambiguous LLSD::assign overload +bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + sd_to_write.assign((S32)*((const U32*)val_ptr)); + + return true; +} + +bool LLParamSDParser::writeFlag(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + + return true; +} + +void LLParamSDParser::submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack) +{ + mCurReadSD = &sd; + block.submitValue(name_stack, *this); +} + +void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent) +{ + mCurReadSD = NULL; + mNameStack.clear(); + setParseSilently(silent); + + LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack); + //readSDValues(sd, block); +} + +void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block) +{ + mNameStack.clear(); + mWriteRootSD = &sd; + + name_stack_t name_stack; + block.serializeBlock(*this, name_stack); +} + +/*virtual*/ std::string LLParamSDParser::getCurrentElementName() +{ + std::string full_name = "sd"; + for (name_stack_t::iterator it = mNameStack.begin(); + it != mNameStack.end(); + ++it) + { + full_name += llformat("[%s]", it->first.c_str()); + } + + return full_name; +} + + +bool LLParamSDParser::readFlag(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + return self.mCurReadSD == &NO_VALUE_MARKER; +} + + +bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((S32*)val_ptr) = self.mCurReadSD->asInteger(); + return true; +} + +bool LLParamSDParser::readU32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((U32*)val_ptr) = self.mCurReadSD->asInteger(); + return true; +} + +bool LLParamSDParser::readF32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((F32*)val_ptr) = self.mCurReadSD->asReal(); + return true; +} + +bool LLParamSDParser::readF64(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((F64*)val_ptr) = self.mCurReadSD->asReal(); + return true; +} + +bool LLParamSDParser::readBool(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((bool*)val_ptr) = self.mCurReadSD->asBoolean(); + return true; +} + +bool LLParamSDParser::readString(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((std::string*)val_ptr) = self.mCurReadSD->asString(); + return true; +} + +bool LLParamSDParser::readUUID(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID(); + return true; +} + +bool LLParamSDParser::readDate(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLDate*)val_ptr) = self.mCurReadSD->asDate(); + return true; +} + +bool LLParamSDParser::readURI(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLURI*)val_ptr) = self.mCurReadSD->asURI(); + return true; +} + +bool LLParamSDParser::readSD(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLSD*)val_ptr) = *self.mCurReadSD; + return true; +} + +// static +LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range) +{ + LLSD* sd_to_write = &input; + + for (LLInitParam::Parser::name_stack_t::iterator it = name_stack_range.first; + it != name_stack_range.second; + ++it) + { + bool new_traversal = it->second; + + LLSD* child_sd = it->first.empty() ? sd_to_write : &(*sd_to_write)[it->first]; + + if (child_sd->isArray()) + { + if (new_traversal) + { + // write to new element at end + sd_to_write = &(*child_sd)[child_sd->size()]; + } + else + { + // write to last of existing elements, or first element if empty + sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)]; + } + } + else + { + if (new_traversal + && child_sd->isDefined() + && !child_sd->isArray()) + { + // copy child contents into first element of an array + LLSD new_array = LLSD::emptyArray(); + new_array.append(*child_sd); + // assign array to slot that previously held the single value + *child_sd = new_array; + // return next element in that array + sd_to_write = &((*child_sd)[1]); + } + else + { + sd_to_write = child_sd; + } + } + it->second = false; + } + + return *sd_to_write; +} + +//static +void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack) +{ + if (sd.isMap()) + { + for (LLSD::map_const_iterator it = sd.beginMap(); + it != sd.endMap(); + ++it) + { + stack.push_back(make_pair(it->first, true)); + readSDValues(cb, it->second, stack); + stack.pop_back(); + } + } + else if (sd.isArray()) + { + for (LLSD::array_const_iterator it = sd.beginArray(); + it != sd.endArray(); + ++it) + { + stack.back().second = true; + readSDValues(cb, *it, stack); + } + } + else if (sd.isUndefined()) + { + if (!cb.empty()) + { + cb(NO_VALUE_MARKER, stack); + } + } + else + { + if (!cb.empty()) + { + cb(sd, stack); + } + } +} + +//static +void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd) +{ + LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t(); + readSDValues(cb, sd, stack); +} +namespace LLInitParam +{ + // LLSD specialization + // block param interface + bool ParamValue, false>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name) + { + LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); + + LLSD::String string; + + if (p.readValue(string)) + { + sd = string; + return true; + } + return false; + } + + //static + void ParamValue, false>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) + { + p.writeValue(sd.asString(), name_stack); + } + + void ParamValue, false>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const + { + // read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) + Parser::name_stack_t stack; + LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, stack); + } +} diff --git a/indra/llcommon/llsdparam.h b/indra/llcommon/llsdparam.h new file mode 100644 index 000000000..6ef5debd7 --- /dev/null +++ b/indra/llcommon/llsdparam.h @@ -0,0 +1,126 @@ +/** + * @file llsdparam.h + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDPARAM_H +#define LL_LLSDPARAM_H + +#include "llinitparam.h" +#include "boost/function.hpp" + +struct LL_COMMON_API LLParamSDParserUtilities +{ + static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range); + + typedef boost::function read_sd_cb_t; + static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack); + static void readSDValues(read_sd_cb_t cb, const LLSD& sd); +}; + +class LL_COMMON_API LLParamSDParser +: public LLInitParam::Parser +{ +LOG_CLASS(LLParamSDParser); + +typedef LLInitParam::Parser parser_t; + +public: + LLParamSDParser(); + void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false); + void writeSD(LLSD& sd, const LLInitParam::BaseBlock& block); + + /*virtual*/ std::string getCurrentElementName(); + +private: + void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack); + + template + static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) + { + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + + sd_to_write.assign(*((const T*)val_ptr)); + return true; + } + + static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + + static bool readFlag(Parser& parser, void* val_ptr); + static bool readS32(Parser& parser, void* val_ptr); + static bool readU32(Parser& parser, void* val_ptr); + static bool readF32(Parser& parser, void* val_ptr); + static bool readF64(Parser& parser, void* val_ptr); + static bool readBool(Parser& parser, void* val_ptr); + static bool readString(Parser& parser, void* val_ptr); + static bool readUUID(Parser& parser, void* val_ptr); + static bool readDate(Parser& parser, void* val_ptr); + static bool readURI(Parser& parser, void* val_ptr); + static bool readSD(Parser& parser, void* val_ptr); + + Parser::name_stack_t mNameStack; + const LLSD* mCurReadSD; + LLSD* mWriteRootSD; + LLSD* mCurWriteSD; +}; + + +extern LL_COMMON_API LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR; +template +class LLSDParamAdapter : public T +{ +public: + LLSDParamAdapter() {} + LLSDParamAdapter(const LLSD& sd) + { + LLFastTimer _(FTM_SD_PARAM_ADAPTOR); + LLParamSDParser parser; + // don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it + bool parse_silently = true; + parser.readSD(sd, *this, parse_silently); + } + + operator LLSD() const + { + LLParamSDParser parser; + LLSD sd; + parser.writeSD(sd, *this); + return sd; + } + + LLSDParamAdapter(const T& val) + : T(val) + { + T::operator=(val); + } +}; + +#endif // LL_LLSDPARAM_H + diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp index 3aca80f6e..4f21bca5a 100644 --- a/indra/llcommon/llsecondlifeurls.cpp +++ b/indra/llcommon/llsecondlifeurls.cpp @@ -45,44 +45,49 @@ const std::string AUCTION_URL ( const std::string EVENTS_URL ( "http://secondlife.com/events/"); +/* const std::string TIER_UP_URL ( - "http://secondlife.com/app/landtier"); - -const std::string LAND_URL ( - "http://secondlife.com/app/landtier"); - -const std::string UPGRADE_TO_PREMIUM_URL ( - "http://secondlife.com/app/upgrade/"); + "http://secondlife.com/app/landtier"); // *TODO: NOT USED +*/ const std::string DIRECTX_9_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED +/* +const std::string LAND_URL ( + "http://secondlife.com/app/landtier"); // *TODO: NOT USED + +const std::string UPGRADE_TO_PREMIUM_URL ( + "http://secondlife.com/app/upgrade/"); // *TODO: NOT USED const std::string AMD_AGP_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string VIA_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SUPPORT_URL ( "http://secondlife.com/support/"); const std::string INTEL_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SIS_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string BLOGS_URL ( - "http://blog.secondlife.com/"); + "http://blog.secondlife.com/"); // *TODO: NOT USED +*/ const std::string BUY_CURRENCY_URL ( "http://secondlife.com/app/currency/"); +/* const std::string LSL_DOC_URL ( - "http://secondlife.com/app/lsldoc/"); + "http://secondlife.com/app/lsldoc/"); // *TODO: NOT USED const std::string SL_KB_URL ( - "http://secondlife.com/knowledgebase/"); + "http://secondlife.com/knowledgebase/"); // *TODO: NOT USED const std::string RELEASE_NOTES_BASE_URL ( - "http://ascent.balseraph.org/?"); + "http://secondlife.com/app/releasenotes/"); +*/ diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h index 81d840833..4c741dce4 100644 --- a/indra/llcommon/llsecondlifeurls.h +++ b/indra/llcommon/llsecondlifeurls.h @@ -43,18 +43,22 @@ LL_COMMON_API extern const std::string AUCTION_URL; LL_COMMON_API extern const std::string EVENTS_URL; +/* // Tier up to a new land level. LL_COMMON_API extern const std::string TIER_UP_URL; // Tier up to a new land level. LL_COMMON_API extern const std::string LAND_URL; - -// Upgrade from basic membership to premium membership -LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; +*/ // How to get DirectX 9 LL_COMMON_API extern const std::string DIRECTX_9_URL; +/* +// Upgrade from basic membership to premium membership +LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; + + // Out of date VIA chipset LL_COMMON_API extern const std::string VIA_URL; @@ -63,10 +67,12 @@ LL_COMMON_API extern const std::string SUPPORT_URL; // Linden Blogs page LL_COMMON_API extern const std::string BLOGS_URL; +*/ // Currency page LL_COMMON_API extern const std::string BUY_CURRENCY_URL; +/* // LSL script wiki LL_COMMON_API extern const std::string LSL_DOC_URL; @@ -75,5 +81,5 @@ LL_COMMON_API extern const std::string SL_KB_URL; // Release Notes Redirect URL for Server and Viewer LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL; - +*/ #endif diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 840532e43..62eb07fed 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -917,6 +917,7 @@ P(MPImportGetResponder); P(MPImportPostResponder); P(mapLayerResponder); P2(maturityPreferences, transfer_30s); +P(mediaDataClientResponder); P(mediaTypeResponder); P(meshDecompositionResponder); P(meshHeaderResponder); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index fcaa52874..8e7a40a67 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -34,7 +34,9 @@ #include "llrect.h" #include "v4color.h" -class LLPluginClassMedia : public LLPluginClassBasic +#include + +class LLPluginClassMedia : public LLPluginClassBasic, public boost::signals2::trackable { LOG_CLASS(LLPluginClassMedia); diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 8b80180d4..34eff1751 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -26,13 +26,30 @@ #include "linden_common.h" +#include "lluuid.h" +#include "llmediaentry.h" #include "lltextureentry.h" #include "llsdutil_math.h" +#include "v4color.h" const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess const LLTextureEntry LLTextureEntry::null; +// Some LLSD keys. Do not change these! +#define OBJECT_ID_KEY_STR "object_id" +#define TEXTURE_INDEX_KEY_STR "texture_index" +#define OBJECT_MEDIA_VERSION_KEY_STR "object_media_version" +#define OBJECT_MEDIA_DATA_KEY_STR "object_media_data" +#define TEXTURE_MEDIA_DATA_KEY_STR "media_data" + +/*static*/ const char* LLTextureEntry::OBJECT_ID_KEY = OBJECT_ID_KEY_STR; +/*static*/ const char* LLTextureEntry::OBJECT_MEDIA_DATA_KEY = OBJECT_MEDIA_DATA_KEY_STR; +/*static*/ const char* LLTextureEntry::MEDIA_VERSION_KEY = OBJECT_MEDIA_VERSION_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_INDEX_KEY = TEXTURE_INDEX_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_MEDIA_DATA_KEY = TEXTURE_MEDIA_DATA_KEY_STR; + +static const std::string MEDIA_VERSION_STRING_PREFIX = "x-mv:"; // static LLTextureEntry* LLTextureEntry::newTextureEntry() @@ -42,16 +59,19 @@ LLTextureEntry* LLTextureEntry::newTextureEntry() //=============================================================== LLTextureEntry::LLTextureEntry() + : mMediaEntry(NULL) { init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLUUID& tex_id) + : mMediaEntry(NULL) { init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) + : mMediaEntry(NULL) { mID = rhs.mID; mScaleS = rhs.mScaleS; @@ -63,6 +83,10 @@ LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } } LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) @@ -79,6 +103,16 @@ LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } + else { + mMediaEntry = NULL; + } } return *this; @@ -98,10 +132,19 @@ void LLTextureEntry::init(const LLUUID& tex_id, F32 scale_s, F32 scale_t, F32 of mGlow = 0; setColor(LLColor4(1.f, 1.f, 1.f, 1.f)); + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; } LLTextureEntry::~LLTextureEntry() { + if(mMediaEntry) + { + delete mMediaEntry; + mMediaEntry = NULL; + } } bool LLTextureEntry::operator!=(const LLTextureEntry &rhs) const @@ -153,6 +196,13 @@ void LLTextureEntry::asLLSD(LLSD& sd) const sd["bump"] = getBumpShiny(); sd["fullbright"] = getFullbright(); sd["media_flags"] = mMediaFlags; + if (hasMedia()) { + LLSD mediaData; + if (NULL != getMediaData()) { + getMediaData()->asLLSD(mediaData); + } + sd[TEXTURE_MEDIA_DATA_KEY] = mediaData; + } sd["glow"] = mGlow; } @@ -201,6 +251,17 @@ bool LLTextureEntry::fromLLSD(const LLSD& sd) { setMediaTexGen( sd[w].asInteger() ); } else goto fail; + // If the "has media" flag doesn't match the fact that + // media data exists, updateMediaData will "fix" it + // by either clearing or setting the flag + w = TEXTURE_MEDIA_DATA_KEY; + if (hasMedia() != sd.has(w)) + { + llwarns << "LLTextureEntry::fromLLSD: media_flags (" << hasMedia() << + ") does not match presence of media_data (" << sd.has(w) << "). Fixing." << llendl; + } + updateMediaData(sd[w]); + w = "glow"; if (sd.has(w)) { @@ -362,12 +423,10 @@ S32 LLTextureEntry::setBumpShinyFullbright(U8 bump) S32 LLTextureEntry::setMediaTexGen(U8 media) { - if (mMediaFlags != media) - { - mMediaFlags = media; - return TEM_CHANGE_MEDIA; - } - return TEM_CHANGE_NONE; + S32 result = TEM_CHANGE_NONE; + result |= setTexGen(media & TEM_TEX_GEN_MASK); + result |= setMediaFlags(media & TEM_MEDIA_MASK); + return result; } S32 LLTextureEntry::setBumpmap(U8 bump) @@ -425,6 +484,18 @@ S32 LLTextureEntry::setMediaFlags(U8 media_flags) { mMediaFlags &= ~TEM_MEDIA_MASK; mMediaFlags |= media_flags; + + // Special code for media handling + if( hasMedia() && mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + else if ( ! hasMedia() && mMediaEntry != NULL) + { + delete mMediaEntry; + mMediaEntry = NULL; + } + return TEM_CHANGE_MEDIA; } return TEM_CHANGE_NONE; @@ -452,4 +523,112 @@ S32 LLTextureEntry::setGlow(F32 glow) return TEM_CHANGE_NONE; } +void LLTextureEntry::setMediaData(const LLMediaEntry &media_entry) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (NULL != mMediaEntry) + { + delete mMediaEntry; + } + mMediaEntry = new LLMediaEntry(media_entry); +} +bool LLTextureEntry::updateMediaData(const LLSD& media_data) +{ + if (media_data.isUndefined()) + { + // clear the media data + clearMediaData(); + return false; + } + else { + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *clobber* all of the fields in mMediaEntry + // with whatever fields are present (or not present) in media_data! + mMediaEntry->fromLLSD(media_data); + return true; + } +} + +void LLTextureEntry::clearMediaData() +{ + mMediaFlags &= ~MF_HAS_MEDIA; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; +} + +void LLTextureEntry::mergeIntoMediaData(const LLSD& media_fields) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *merge* the data in media_fields + // with the data in our media entry + mMediaEntry->mergeFromLLSD(media_fields); +} + +//static +std::string LLTextureEntry::touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id) +{ + // XXX TODO: make media version string binary (base64-encoded?) + // Media "URL" is a representation of a version and the last-touched agent + // x-mv:nnnnn/agent-id + // where "nnnnn" is version number + // *NOTE: not the most efficient code in the world... + U32 current_version = getVersionFromMediaVersionString(in_version) + 1; + const size_t MAX_VERSION_LEN = 10; // 2^32 fits in 10 decimal digits + char buf[MAX_VERSION_LEN+1]; + snprintf(buf, (int)MAX_VERSION_LEN+1, "%0*u", (int)MAX_VERSION_LEN, current_version); // added int cast to fix warning/breakage on mac. + return MEDIA_VERSION_STRING_PREFIX + buf + "/" + agent_id.asString(); +} + +//static +U32 LLTextureEntry::getVersionFromMediaVersionString(const std::string &version_string) +{ + U32 version = 0; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + std::string v = version_string.substr(MEDIA_VERSION_STRING_PREFIX.length(), found); + version = strtoul(v.c_str(),NULL,10); + } + } + return version; +} + +//static +LLUUID LLTextureEntry::getAgentIDFromMediaVersionString(const std::string &version_string) +{ + LLUUID id; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + if (found != std::string::npos) + { + std::string v = version_string.substr(found + 1); + id.set(v); + } + } + } + return id; +} + +//static +bool LLTextureEntry::isMediaVersionString(const std::string &version_string) +{ + return std::string::npos != version_string.find(MEDIA_VERSION_STRING_PREFIX); +} diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index ba0c92dc7..d0181421f 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -62,6 +62,8 @@ const S32 TEM_MEDIA_MASK = 0x01; const S32 TEM_TEX_GEN_MASK = 0x06; const S32 TEM_TEX_GEN_SHIFT = 1; +// forward declarations +class LLMediaEntry; class LLTextureEntry { @@ -137,6 +139,34 @@ public: U8 getTexGen() const { return mMediaFlags & TEM_TEX_GEN_MASK; } U8 getMediaTexGen() const { return mMediaFlags; } F32 getGlow() const { return mGlow; } + + // *NOTE: it is possible for hasMedia() to return true, but getMediaData() to return NULL. + // CONVERSELY, it is also possible for hasMedia() to return false, but getMediaData() + // to NOT return NULL. + bool hasMedia() const { return (bool)(mMediaFlags & MF_HAS_MEDIA); } + LLMediaEntry* getMediaData() const { return mMediaEntry; } + + // Completely change the media data on this texture entry. + void setMediaData(const LLMediaEntry &media_entry); + // Returns true if media data was updated, false if it was cleared + bool updateMediaData(const LLSD& media_data); + // Clears media data, and sets the media flags bit to 0 + void clearMediaData(); + // Merges the given LLSD of media fields with this media entry. + // Only those fields that are set that match the keys in + // LLMediaEntry will be affected. If no fields are set or if + // the LLSD is undefined, this is a no-op. + void mergeIntoMediaData(const LLSD& media_fields); + + // Takes a media version string (an empty string or a previously-returned string) + // and returns a "touched" string, touched by agent_id + static std::string touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id); + // Given a media version string, return the version + static U32 getVersionFromMediaVersionString(const std::string &version_string); + // Given a media version string, return the UUID of the agent + static LLUUID getAgentIDFromMediaVersionString(const std::string &version_string); + // Return whether or not the given string is actually a media version + static bool isMediaVersionString(const std::string &version_string); // Media flags enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 }; @@ -149,6 +179,14 @@ public: F32 mRotation; // anti-clockwise rotation in rad about the bottom left corner static const LLTextureEntry null; + + // LLSD key defines + static const char* OBJECT_ID_KEY; + static const char* OBJECT_MEDIA_DATA_KEY; + static const char* MEDIA_VERSION_KEY; + static const char* TEXTURE_INDEX_KEY; + static const char* TEXTURE_MEDIA_DATA_KEY; + protected: LLUUID mID; // Texture GUID LLColor4 mColor; @@ -156,6 +194,9 @@ protected: U8 mMediaFlags; // replace with web page, movie, etc. F32 mGlow; + // Note the media data is not sent via the same message structure as the rest of the TE + LLMediaEntry* mMediaEntry; // The media data for the face + // NOTE: when adding new data to this class, in addition to adding it to the serializers asLLSD/fromLLSD and the // message packers (e.g. LLPrimitive::packTEMessage) you must also implement its copy in LLPrimitive::copyTEs() diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 54ebcb504..4b6c6156d 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -39,6 +39,7 @@ set(llui_SOURCE_FILES llfunctorregistry.cpp lliconctrl.cpp llkeywords.cpp + lllayoutstack.cpp lllineeditor.cpp lllocalcliprect.cpp llmenugl.cpp @@ -78,6 +79,10 @@ set(llui_SOURCE_FILES lluictrlfactory.cpp lluistring.cpp llundo.cpp + llurlaction.cpp + llurlentry.cpp + llurlmatch.cpp + llurlregistry.cpp llview.cpp llviewborder.cpp llviewmodel.cpp @@ -105,6 +110,7 @@ set(llui_HEADER_FILES llhtmlhelp.h lliconctrl.h llkeywords.h + lllayoutstack.h lllineeditor.h lllocalcliprect.h llmemberlistener.h @@ -150,6 +156,10 @@ set(llui_HEADER_FILES lluistring.h lluixmltags.h llundo.h + llurlaction.h + llurlentry.h + llurlmatch.h + llurlregistry.h llview.h llviewborder.h llviewmodel.h diff --git a/indra/llui/llalertdialog.cpp b/indra/llui/llalertdialog.cpp index 7015e9c6a..739eb61d4 100644 --- a/indra/llui/llalertdialog.cpp +++ b/indra/llui/llalertdialog.cpp @@ -520,7 +520,7 @@ void LLAlertDialog::onButtonPressed( LLUICtrl* ctrl, const std::string url ) // If we declared a URL and chose the URL option, go to the url if (!url.empty() && sURLLoader != NULL) { - sURLLoader->load(url); + sURLLoader->load(url, false); } mNote->respond(response); // new notification reponse diff --git a/indra/llui/llalertdialog.h b/indra/llui/llalertdialog.h index 799ab8a80..5463eb54b 100644 --- a/indra/llui/llalertdialog.h +++ b/indra/llui/llalertdialog.h @@ -57,7 +57,7 @@ public: class URLLoader { public: - virtual void load(const std::string& url) = 0; + virtual void load(const std::string& url, bool force_open_externally) = 0; virtual ~URLLoader() {} }; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 348e5e383..516f028b1 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -2487,8 +2487,13 @@ LLView* LLFloater::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f if (filename.empty()) { + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + floaterp->getCommitCallbackRegistrar().pushScope(); + floaterp->getEnableCallbackRegistrar().pushScope(); // Load from node floaterp->initFloaterXML(node, parent, factory); + floaterp->getCommitCallbackRegistrar().popScope(); + floaterp->getEnableCallbackRegistrar().popScope(); } else { diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp new file mode 100644 index 000000000..58c70aec5 --- /dev/null +++ b/indra/llui/lllayoutstack.cpp @@ -0,0 +1,682 @@ +/** + * @file lllayoutstack.cpp + * @brief LLLayout class - dynamic stacking of UI elements + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Opaque view with a background and a border. Can contain LLUICtrls. + +#include "linden_common.h" + +#include "lllayoutstack.h" + +#include "lllocalcliprect.h" +#include "llpanel.h" +#include "llresizebar.h" +#include "lluictrlfactory.h" +#include "llcriticaldamp.h" +#include "boost/foreach.hpp" + +static const F32 MIN_FRACTIONAL_SIZE = 0.0f; +static const F32 MAX_FRACTIONAL_SIZE = 1.f; +static const S32 RESIZE_BAR_OVERLAP = 1; +static const S32 RESIZE_BAR_HEIGHT = 3; + +// +// LLLayoutPanel +// +LLLayoutPanel::LLLayoutPanel(LLPanel* panelp, LLLayoutStack::eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : + mPanel(panelp), + mMinWidth(min_width), + mMinHeight(min_height), + mAutoResize(auto_resize), + mUserResize(user_resize), + mCollapsed(FALSE), + mCollapseAmt(0.f), + mVisibleAmt(1.f), // default to fully visible + mResizeBar(NULL), + mOrientation(orientation) +{ + LLResizeBar::Side side = (orientation == LLLayoutStack::HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; + + S32 min_dim; + if (orientation == LLLayoutStack::HORIZONTAL) + { + min_dim = mMinHeight; + } + else + { + min_dim = mMinWidth; + } + LLResizeBar::Params p; + p.name = "resizer"; + p.resizing_view = mPanel; + p.min_size = min_dim; + p.max_size = S32_MAX; + p.side = side; + mResizeBar = LLUICtrlFactory::create(p); + mResizeBar->setEnableSnapping(FALSE); + // panels initialized as hidden should not start out partially visible + if (!mPanel->getVisible()) + { + mVisibleAmt = 0.f; + } +} + +LLLayoutPanel::~LLLayoutPanel() +{ + // probably not necessary, but... + delete mResizeBar; + mResizeBar = NULL; +} + +F32 LLLayoutPanel::getCollapseFactor() +{ + if (mOrientation == LLLayoutStack::HORIZONTAL) + { + F32 collapse_amt = + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth())); + return mVisibleAmt * collapse_amt; + } + else + { + F32 collapse_amt = + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinHeight / (F32)llmax(1, mPanel->getRect().getHeight()))); + return mVisibleAmt * collapse_amt; + } +} + + +static LLRegisterWidget r2("layout_stack"); + +LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : + mOrientation(orientation), + mMinWidth(0), + mMinHeight(0), + mPanelSpacing(RESIZE_BAR_HEIGHT) +{ +} + +LLLayoutStack::~LLLayoutStack() +{ + e_panel_list_t panels = mPanels; // copy list of panel pointers + mPanels.clear(); // clear so that removeChild() calls don't cause trouble + std::for_each(panels.begin(), panels.end(), DeletePointer()); +} + +void LLLayoutStack::draw() +{ + updateLayout(); + + e_panel_list_t::iterator panel_it; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + // clip to layout rectangle, not bounding rectangle + LLRect clip_rect = (*panel_it)->mPanel->getRect(); + // scale clipping rectangle by visible amount + if (mOrientation == HORIZONTAL) + { + clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor()); + } + else + { + clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor()); + } + + LLPanel* panelp = (*panel_it)->mPanel; + + LLLocalClipRect clip(clip_rect); + // only force drawing invisible children if visible amount is non-zero + drawChild(panelp, 0, 0, clip_rect.notEmpty()); + } +} + +void LLLayoutStack::removeChild(LLView* ctrl) +{ + LLView::removeChild(ctrl); + LLPanel* panel = dynamic_cast(ctrl); + if(!panel) + return; + LLLayoutPanel* embedded_panelp = findEmbeddedPanel(panel); + + if (embedded_panelp) + { + mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); + delete embedded_panelp; + } + + // need to update resizebars + + calcMinExtents(); +} + +LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const +{ + LLXMLNodePtr node = LLView::getXML(); + node->setName(LL_LAYOUT_STACK_TAG); + + if (mOrientation == HORIZONTAL) + { + node->createChild("orientation", TRUE)->setStringValue("horizontal"); + } + else + { + node->createChild("orientation", TRUE)->setStringValue("vertical"); + } + + if (save_children) + { + LLView::child_list_const_reverse_iter_t rit; + for (rit = getChildList()->rbegin(); rit != getChildList()->rend(); ++rit) + { + LLView* childp = *rit; + + if (childp->getSaveToXML()) + { + LLXMLNodePtr xml_node = childp->getXML(); + + if (xml_node->hasName(LL_PANEL_TAG)) + { + xml_node->setName(LL_LAYOUT_PANEL_TAG); + } + + node->addChild(xml_node); + } + } + } + + return node; +} + +//static +LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + std::string orientation_string("vertical"); + node->getAttributeString("orientation", orientation_string); + + eLayoutOrientation orientation = VERTICAL; + + if (orientation_string == "horizontal") + { + orientation = HORIZONTAL; + } + else if (orientation_string == "vertical") + { + orientation = VERTICAL; + } + else + { + llwarns << "Unknown orientation " << orientation_string << ", using vertical" << llendl; + } + + LLLayoutStack* layout_stackp = new LLLayoutStack(orientation); + + node->getAttributeS32("border_size", layout_stackp->mPanelSpacing); + // don't allow negative spacing values + layout_stackp->mPanelSpacing = llmax(layout_stackp->mPanelSpacing, 0); + + std::string name("stack"); + node->getAttributeString("name", name); + + layout_stackp->setName(name); + layout_stackp->initFromXML(node, parent); + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + S32 min_width = 0; + S32 min_height = 0; + BOOL auto_resize = TRUE; + + child->getAttributeS32("min_width", min_width); + child->getAttributeS32("min_height", min_height); + child->getAttributeBOOL("auto_resize", auto_resize); + + if (child->hasName("layout_panel")) + { + BOOL user_resize = TRUE; + child->getAttributeBOOL("user_resize", user_resize); + LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory); + if (panelp) + { + panelp->setFollowsNone(); + layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); + } + } + else + { + BOOL user_resize = FALSE; + child->getAttributeBOOL("user_resize", user_resize); + + LLPanel* panelp = new LLPanel(std::string("auto_panel")); + LLView* new_child = factory->createWidget(panelp, child); + if (new_child) + { + // put child in new embedded panel + layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); + // resize panel to contain widget and move widget to be contained in panel + panelp->setRect(new_child->getRect()); + new_child->setOrigin(0, 0); + } + else + { + panelp->die(); + } + } + } + layout_stackp->updateLayout(); + + return layout_stackp; +} + +S32 LLLayoutStack::getDefaultHeight(S32 cur_height) +{ + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == HORIZONTAL) + { + cur_height = llmax(mMinHeight, getRect().getHeight()); + } + + return cur_height; +} + +S32 LLLayoutStack::getDefaultWidth(S32 cur_width) +{ + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == VERTICAL) + { + cur_width = llmax(mMinWidth, getRect().getWidth()); + } + + return cur_width; +} + +void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index) +{ + // panel starts off invisible (collapsed) + if (animate == ANIMATE) + { + panel->setVisible(FALSE); + } + LLLayoutPanel* embedded_panel = new LLLayoutPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); + + mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); + + addChild(panel); + addChild(embedded_panel->mResizeBar); + + // bring all resize bars to the front so that they are clickable even over the panels + // with a bit of overlap + for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLResizeBar* resize_barp = (*panel_it)->mResizeBar; + sendChildToFront(resize_barp); + } + + // start expanding panel animation + if (animate == ANIMATE) + { + panel->setVisible(TRUE); + } +} + +void LLLayoutStack::removePanel(LLPanel* panel) +{ + removeChild(panel); +} + +void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) +{ + LLLayoutPanel* panel_container = findEmbeddedPanel(panel); + if (!panel_container) return; + + panel_container->mCollapsed = collapsed; +} + +void LLLayoutStack::updateLayout(BOOL force_resize) +{ + calcMinExtents(); + + // calculate current extents + S32 total_width = 0; + S32 total_height = 0; + + const F32 ANIM_OPEN_TIME = 0.02f; + const F32 ANIM_CLOSE_TIME = 0.03f; + + e_panel_list_t::iterator panel_it; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLPanel* panelp = (*panel_it)->mPanel; + if (panelp->getVisible()) + { + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); + if ((*panel_it)->mVisibleAmt > 0.99f) + { + (*panel_it)->mVisibleAmt = 1.f; + } + } + else // not visible + { + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + if ((*panel_it)->mVisibleAmt < 0.001f) + { + (*panel_it)->mVisibleAmt = 0.f; + } + } + + if ((*panel_it)->mCollapsed) + { + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + } + else + { + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + } + + if (mOrientation == HORIZONTAL) + { + // enforce minimize size constraint by default + if (panelp->getRect().getWidth() < (*panel_it)->mMinWidth) + { + panelp->reshape((*panel_it)->mMinWidth, panelp->getRect().getHeight()); + } + total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor()); + // want n-1 panel gaps for n panels + if (panel_it != mPanels.begin()) + { + total_width += mPanelSpacing; + } + } + else //VERTICAL + { + // enforce minimize size constraint by default + if (panelp->getRect().getHeight() < (*panel_it)->mMinHeight) + { + panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinHeight); + } + total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor()); + if (panel_it != mPanels.begin()) + { + total_height += mPanelSpacing; + } + } + } + + S32 num_resizable_panels = 0; + S32 shrink_headroom_available = 0; + S32 shrink_headroom_total = 0; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + // panels that are not fully visible do not count towards shrink headroom + if ((*panel_it)->getCollapseFactor() < 1.f) + { + continue; + } + + // if currently resizing a panel or the panel is flagged as not automatically resizing + // only track total available headroom, but don't use it for automatic resize logic + if ((*panel_it)->mResizeBar->hasMouseCapture() + || (!(*panel_it)->mAutoResize + && !force_resize)) + { + if (mOrientation == HORIZONTAL) + { + shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; + } + else //VERTICAL + { + shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; + } + } + else + { + num_resizable_panels++; + if (mOrientation == HORIZONTAL) + { + shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; + shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; + } + else //VERTICAL + { + shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; + shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; + } + } + } + + // calculate how many pixels need to be distributed among layout panels + // positive means panels need to grow, negative means shrink + S32 pixels_to_distribute; + if (mOrientation == HORIZONTAL) + { + pixels_to_distribute = getRect().getWidth() - total_width; + } + else //VERTICAL + { + pixels_to_distribute = getRect().getHeight() - total_height; + } + + // now we distribute the pixels... + S32 cur_x = 0; + S32 cur_y = getRect().getHeight(); + + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLPanel* panelp = (*panel_it)->mPanel; + + S32 cur_width = panelp->getRect().getWidth(); + S32 cur_height = panelp->getRect().getHeight(); + S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); + S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); + + S32 delta_size = 0; + + // if panel can automatically resize (not animating, and resize flag set)... + if ((*panel_it)->getCollapseFactor() == 1.f + && (force_resize || (*panel_it)->mAutoResize) + && !(*panel_it)->mResizeBar->hasMouseCapture()) + { + if (mOrientation == HORIZONTAL) + { + // if we're shrinking + if (pixels_to_distribute < 0) + { + // shrink proportionally to amount over minimum + // so we can do this in one pass + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_width - (*panel_it)->mMinWidth); + } + else + { + // grow all elements equally + delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); + num_resizable_panels--; + } + pixels_to_distribute -= delta_size; + new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); + } + else + { + new_width = getDefaultWidth(new_width); + } + + if (mOrientation == VERTICAL) + { + if (pixels_to_distribute < 0) + { + // shrink proportionally to amount over minimum + // so we can do this in one pass + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_height - (*panel_it)->mMinHeight); + } + else + { + delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); + num_resizable_panels--; + } + pixels_to_distribute -= delta_size; + new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); + } + else + { + new_height = getDefaultHeight(new_height); + } + } + else + { + if (mOrientation == HORIZONTAL) + { + new_height = getDefaultHeight(new_height); + } + else // VERTICAL + { + new_width = getDefaultWidth(new_width); + } + } + + // adjust running headroom count based on new sizes + shrink_headroom_total += delta_size; + + panelp->reshape(new_width, new_height); + panelp->setOrigin(cur_x, cur_y - new_height); + + LLRect panel_rect = panelp->getRect(); + LLRect resize_bar_rect = panel_rect; + if (mOrientation == HORIZONTAL) + { + resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP; + resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + RESIZE_BAR_OVERLAP; + } + else + { + resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP; + resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - RESIZE_BAR_OVERLAP; + } + (*panel_it)->mResizeBar->setRect(resize_bar_rect); + + if (mOrientation == HORIZONTAL) + { + cur_x += llround(new_width * (*panel_it)->getCollapseFactor()) + mPanelSpacing; + } + else //VERTICAL + { + cur_y -= llround(new_height * (*panel_it)->getCollapseFactor()) + mPanelSpacing; + } + } + + // update resize bars with new limits + LLResizeBar* last_resize_bar = NULL; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLPanel* panelp = (*panel_it)->mPanel; + + if (mOrientation == HORIZONTAL) + { + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinWidth, + (*panel_it)->mMinWidth + shrink_headroom_total); + } + else //VERTICAL + { + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinHeight, + (*panel_it)->mMinHeight + shrink_headroom_total); + } + + // toggle resize bars based on panel visibility, resizability, etc + BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; + (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); + + if (resize_bar_enabled) + { + last_resize_bar = (*panel_it)->mResizeBar; + } + } + + // hide last resize bar as there is nothing past it + // resize bars need to be in between two resizable panels + if (last_resize_bar) + { + last_resize_bar->setVisible(FALSE); + } + + // not enough room to fit existing contents + if (force_resize == FALSE + // layout did not complete by reaching target position + && ((mOrientation == VERTICAL && cur_y != -mPanelSpacing) + || (mOrientation == HORIZONTAL && cur_x != getRect().getWidth() + mPanelSpacing))) + { + // do another layout pass with all stacked elements contributing + // even those that don't usually resize + llassert_always(force_resize == FALSE); + updateLayout(TRUE); + } +} // end LLLayoutStack::updateLayout + + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const +{ + e_panel_list_t::const_iterator panel_it; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + if ((*panel_it)->mPanel == panelp) + { + return *panel_it; + } + } + return NULL; +} + +void LLLayoutStack::calcMinExtents() +{ + mMinWidth = 0; + mMinHeight = 0; + + e_panel_list_t::iterator panel_it; + for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + if (mOrientation == HORIZONTAL) + { + mMinHeight = llmax( mMinHeight, + (*panel_it)->mMinHeight); + mMinWidth += (*panel_it)->mMinWidth; + if (panel_it != mPanels.begin()) + { + mMinWidth += mPanelSpacing; + } + } + else //VERTICAL + { + mMinWidth = llmax( mMinWidth, + (*panel_it)->mMinWidth); + mMinHeight += (*panel_it)->mMinHeight; + if (panel_it != mPanels.begin()) + { + mMinHeight += mPanelSpacing; + } + } + } +} diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h new file mode 100644 index 000000000..9551cb319 --- /dev/null +++ b/indra/llui/lllayoutstack.h @@ -0,0 +1,113 @@ +/** + * @file lllayoutstack.h + * @author Richard Nelson + * @brief LLLayout class - dynamic stacking of UI elements + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Reshasearch, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLLAYOUTSTACK_H +#define LL_LLLAYOUTSTACK_H + +#include "llpanel.h" + + +class LLLayoutPanel; + + +class LLLayoutStack : public LLView, public LLInstanceTracker +{ +public: + typedef enum e_layout_orientation + { + HORIZONTAL, + VERTICAL + } eLayoutOrientation; + + LLLayoutStack(eLayoutOrientation orientation); + virtual ~LLLayoutStack(); + + /*virtual*/ void draw(); + /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; + /*virtual*/ void removeChild(LLView* ctrl); + + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + + S32 getMinWidth() const { return mMinWidth; } + S32 getMinHeight() const { return mMinHeight; } + + typedef enum e_animate + { + NO_ANIMATE, + ANIMATE + } EAnimate; + + void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX); + void removePanel(LLPanel* panel); + void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE); + S32 getNumPanels() { return mPanels.size(); } + + void updateLayout(BOOL force_resize = FALSE); + + S32 getPanelSpacing() const { return mPanelSpacing; } +private: + + void calcMinExtents(); + S32 getDefaultHeight(S32 cur_height); + S32 getDefaultWidth(S32 cur_width); + + const eLayoutOrientation mOrientation; + + typedef std::vector e_panel_list_t; + e_panel_list_t mPanels; + LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const; + + S32 mMinWidth; + S32 mMinHeight; + S32 mPanelSpacing; +}; // end class LLLayoutStack + +class LLLayoutPanel +{ + friend class LLLayoutStack; + friend class LLUICtrlFactory; + friend struct DeletePointer; + LLLayoutPanel(LLPanel* panelp, LLLayoutStack::eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize); + + ~LLLayoutPanel(); + + F32 getCollapseFactor(); + LLPanel* mPanel; + S32 mMinWidth; + S32 mMinHeight; + bool mAutoResize; + bool mUserResize; + bool mCollapsed; + + + F32 mVisibleAmt; + F32 mCollapseAmt; + LLLayoutStack::eLayoutOrientation mOrientation; + class LLResizeBar* mResizeBar; +}; + +#endif //LL_LLLAYOUTSTACK_H \ No newline at end of file diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index b950d5274..41254b0bb 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1562,4 +1562,3 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification) s << notification.summarize(); return s; } - diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 1585f35f4..03869ef4d 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -100,6 +100,7 @@ #include "llinstancetracker.h" // and we need this to manage the notification callbacks +#include "llavatarname.h" #include "llevents.h" #include "llfunctorregistry.h" #include "llui.h" @@ -791,7 +792,5 @@ private: LLNotificationMap mUniqueNotifications; }; - - #endif//LL_LLNOTIFICATIONS_H diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 27d0111fc..f5bb0b5e8 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -62,9 +62,6 @@ #include "llresizebar.h" #include "llcriticaldamp.h" -const S32 RESIZE_BAR_OVERLAP = 1; -const S32 RESIZE_BAR_HEIGHT = 3; - static LLRegisterWidget r1("panel"); void LLPanel::init() @@ -81,6 +78,9 @@ void LLPanel::init() setTabStop(FALSE); mVisibleSignal = NULL; + + mCommitCallbackRegistrar = false; + mEnableCallbackRegistrar = false; } LLPanel::LLPanel() @@ -459,7 +459,12 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac createRect(node, rect, parent, LLRect()); // create a new panel without a border, by default panelp = new LLPanel(name, rect, FALSE); + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); // preserve panel's width and height, but override the location const LLRect& panelrect = panelp->getRect(); S32 w = panelrect.getWidth(); @@ -470,7 +475,14 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac else { if(!factory->builtPanel(panelp)) + { + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); + } else { LLRect new_rect = panelp->getRect(); @@ -1026,651 +1038,3 @@ void LLPanel::storeRectControl() } -// -// LLLayoutStack -// -struct LLLayoutStack::LLEmbeddedPanel -{ - LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : - mPanel(panelp), - mMinWidth(min_width), - mMinHeight(min_height), - mAutoResize(auto_resize), - mUserResize(user_resize), - mOrientation(orientation), - mCollapsed(FALSE), - mCollapseAmt(0.f), - mVisibleAmt(1.f) // default to fully visible - { - LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; - - S32 min_dim; - if (orientation == HORIZONTAL) - { - min_dim = mMinHeight; - } - else - { - min_dim = mMinWidth; - } - LLResizeBar::Params p; - p.name = "resizer"; - p.resizing_view = mPanel; - p.min_size = min_dim; - p.max_size = S32_MAX; - p.side = side; - mResizeBar = LLUICtrlFactory::create(p); - mResizeBar->setEnableSnapping(FALSE); - // panels initialized as hidden should not start out partially visible - if (!mPanel->getVisible()) - { - mVisibleAmt = 0.f; - } - } - - ~LLEmbeddedPanel() - { - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; - } - - F32 getCollapseFactor() - { - if (mOrientation == HORIZONTAL) - { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth())); - return mVisibleAmt * collapse_amt; - } - else - { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinHeight / (F32)llmax(1, mPanel->getRect().getHeight()))); - return mVisibleAmt * collapse_amt; - } - } - - LLPanel* mPanel; - S32 mMinWidth; - S32 mMinHeight; - BOOL mAutoResize; - BOOL mUserResize; - BOOL mCollapsed; - LLResizeBar* mResizeBar; - eLayoutOrientation mOrientation; - F32 mVisibleAmt; - F32 mCollapseAmt; -}; - -static LLRegisterWidget r2("layout_stack"); - -LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : - mOrientation(orientation), - mMinWidth(0), - mMinHeight(0), - mPanelSpacing(RESIZE_BAR_HEIGHT) -{ -} - -LLLayoutStack::~LLLayoutStack() -{ - std::for_each(mPanels.begin(), mPanels.end(), DeletePointer()); -} - -void LLLayoutStack::draw() -{ - updateLayout(); - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - // clip to layout rectangle, not bounding rectangle - LLRect clip_rect = (*panel_it)->mPanel->getRect(); - // scale clipping rectangle by visible amount - if (mOrientation == HORIZONTAL) - { - clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor()); - } - else - { - clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor()); - } - - LLPanel* panelp = (*panel_it)->mPanel; - - LLLocalClipRect clip(clip_rect); - // only force drawing invisible children if visible amount is non-zero - drawChild(panelp, 0, 0, clip_rect.notEmpty()); - } -} - -void LLLayoutStack::removeChild(LLView* ctrl) -{ - LLView::removeChild(ctrl); - LLPanel* panel = dynamic_cast(ctrl); - if(!panel) - return; - LLEmbeddedPanel* embedded_panelp = findEmbeddedPanel(panel); - - if (embedded_panelp) - { - mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); - delete embedded_panelp; - } - - // need to update resizebars - - calcMinExtents(); -} - -LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const -{ - LLXMLNodePtr node = LLView::getXML(); - node->setName(LL_LAYOUT_STACK_TAG); - - if (mOrientation == HORIZONTAL) - { - node->createChild("orientation", TRUE)->setStringValue("horizontal"); - } - else - { - node->createChild("orientation", TRUE)->setStringValue("vertical"); - } - - if (save_children) - { - LLView::child_list_const_reverse_iter_t rit; - for (rit = getChildList()->rbegin(); rit != getChildList()->rend(); ++rit) - { - LLView* childp = *rit; - - if (childp->getSaveToXML()) - { - LLXMLNodePtr xml_node = childp->getXML(); - - if (xml_node->hasName(LL_PANEL_TAG)) - { - xml_node->setName(LL_LAYOUT_PANEL_TAG); - } - - node->addChild(xml_node); - } - } - } - - return node; -} - -//static -LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) -{ - std::string orientation_string("vertical"); - node->getAttributeString("orientation", orientation_string); - - eLayoutOrientation orientation = VERTICAL; - - if (orientation_string == "horizontal") - { - orientation = HORIZONTAL; - } - else if (orientation_string == "vertical") - { - orientation = VERTICAL; - } - else - { - llwarns << "Unknown orientation " << orientation_string << ", using vertical" << llendl; - } - - LLLayoutStack* layout_stackp = new LLLayoutStack(orientation); - - node->getAttributeS32("border_size", layout_stackp->mPanelSpacing); - // don't allow negative spacing values - layout_stackp->mPanelSpacing = llmax(layout_stackp->mPanelSpacing, 0); - - std::string name("stack"); - node->getAttributeString("name", name); - - layout_stackp->setName(name); - layout_stackp->initFromXML(node, parent); - - LLXMLNodePtr child; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - S32 min_width = 0; - S32 min_height = 0; - BOOL auto_resize = TRUE; - - child->getAttributeS32("min_width", min_width); - child->getAttributeS32("min_height", min_height); - child->getAttributeBOOL("auto_resize", auto_resize); - - if (child->hasName("layout_panel")) - { - BOOL user_resize = TRUE; - child->getAttributeBOOL("user_resize", user_resize); - LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory); - if (panelp) - { - panelp->setFollowsNone(); - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); - } - } - else - { - BOOL user_resize = FALSE; - child->getAttributeBOOL("user_resize", user_resize); - - LLPanel* panelp = new LLPanel(std::string("auto_panel")); - LLView* new_child = factory->createWidget(panelp, child); - if (new_child) - { - // put child in new embedded panel - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); - // resize panel to contain widget and move widget to be contained in panel - panelp->setRect(new_child->getRect()); - new_child->setOrigin(0, 0); - } - else - { - panelp->die(); - } - } - } - layout_stackp->updateLayout(); - - return layout_stackp; -} - -S32 LLLayoutStack::getDefaultHeight(S32 cur_height) -{ - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == HORIZONTAL) - { - cur_height = llmax(mMinHeight, getRect().getHeight()); - } - - return cur_height; -} - -S32 LLLayoutStack::getDefaultWidth(S32 cur_width) -{ - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == VERTICAL) - { - cur_width = llmax(mMinWidth, getRect().getWidth()); - } - - return cur_width; -} - -void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index) -{ - // panel starts off invisible (collapsed) - if (animate == ANIMATE) - { - panel->setVisible(FALSE); - } - LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); - - mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); - - addChild(panel); - addChild(embedded_panel->mResizeBar); - - // bring all resize bars to the front so that they are clickable even over the panels - // with a bit of overlap - for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; - sendChildToFront(resize_barp); - } - - // start expanding panel animation - if (animate == ANIMATE) - { - panel->setVisible(TRUE); - } -} - -void LLLayoutStack::removePanel(LLPanel* panel) -{ - removeChild(panel); -} - -void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) -{ - LLEmbeddedPanel* panel_container = findEmbeddedPanel(panel); - if (!panel_container) return; - - panel_container->mCollapsed = collapsed; -} - -void LLLayoutStack::updateLayout(BOOL force_resize) -{ - calcMinExtents(); - - // calculate current extents - S32 total_width = 0; - S32 total_height = 0; - - const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.03f; - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - if (panelp->getVisible()) - { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); - if ((*panel_it)->mVisibleAmt > 0.99f) - { - (*panel_it)->mVisibleAmt = 1.f; - } - } - else // not visible - { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - if ((*panel_it)->mVisibleAmt < 0.001f) - { - (*panel_it)->mVisibleAmt = 0.f; - } - } - - if ((*panel_it)->mCollapsed) - { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - else - { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - - if (mOrientation == HORIZONTAL) - { - // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->mMinWidth) - { - panelp->reshape((*panel_it)->mMinWidth, panelp->getRect().getHeight()); - } - total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor()); - // want n-1 panel gaps for n panels - if (panel_it != mPanels.begin()) - { - total_width += mPanelSpacing; - } - } - else //VERTICAL - { - // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->mMinHeight) - { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinHeight); - } - total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor()); - if (panel_it != mPanels.begin()) - { - total_height += mPanelSpacing; - } - } - } - - S32 num_resizable_panels = 0; - S32 shrink_headroom_available = 0; - S32 shrink_headroom_total = 0; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - // panels that are not fully visible do not count towards shrink headroom - if ((*panel_it)->getCollapseFactor() < 1.f) - { - continue; - } - - // if currently resizing a panel or the panel is flagged as not automatically resizing - // only track total available headroom, but don't use it for automatic resize logic - if ((*panel_it)->mResizeBar->hasMouseCapture() - || (!(*panel_it)->mAutoResize - && !force_resize)) - { - if (mOrientation == HORIZONTAL) - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } - } - else - { - num_resizable_panels++; - if (mOrientation == HORIZONTAL) - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } - } - } - - // calculate how many pixels need to be distributed among layout panels - // positive means panels need to grow, negative means shrink - S32 pixels_to_distribute; - if (mOrientation == HORIZONTAL) - { - pixels_to_distribute = getRect().getWidth() - total_width; - } - else //VERTICAL - { - pixels_to_distribute = getRect().getHeight() - total_height; - } - - // now we distribute the pixels... - S32 cur_x = 0; - S32 cur_y = getRect().getHeight(); - - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - - S32 cur_width = panelp->getRect().getWidth(); - S32 cur_height = panelp->getRect().getHeight(); - S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); - S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); - - S32 delta_size = 0; - - // if panel can automatically resize (not animating, and resize flag set)... - if ((*panel_it)->getCollapseFactor() == 1.f - && (force_resize || (*panel_it)->mAutoResize) - && !(*panel_it)->mResizeBar->hasMouseCapture()) - { - if (mOrientation == HORIZONTAL) - { - // if we're shrinking - if (pixels_to_distribute < 0) - { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_width - (*panel_it)->mMinWidth); - } - else - { - // grow all elements equally - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; - } - pixels_to_distribute -= delta_size; - new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); - } - else - { - new_width = getDefaultWidth(new_width); - } - - if (mOrientation == VERTICAL) - { - if (pixels_to_distribute < 0) - { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_height - (*panel_it)->mMinHeight); - } - else - { - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; - } - pixels_to_distribute -= delta_size; - new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); - } - else - { - new_height = getDefaultHeight(new_height); - } - } - else - { - if (mOrientation == HORIZONTAL) - { - new_height = getDefaultHeight(new_height); - } - else // VERTICAL - { - new_width = getDefaultWidth(new_width); - } - } - - // adjust running headroom count based on new sizes - shrink_headroom_total += delta_size; - - panelp->reshape(new_width, new_height); - panelp->setOrigin(cur_x, cur_y - new_height); - - LLRect panel_rect = panelp->getRect(); - LLRect resize_bar_rect = panel_rect; - if (mOrientation == HORIZONTAL) - { - resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP; - resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + RESIZE_BAR_OVERLAP; - } - else - { - resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP; - resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - RESIZE_BAR_OVERLAP; - } - (*panel_it)->mResizeBar->setRect(resize_bar_rect); - - if (mOrientation == HORIZONTAL) - { - cur_x += llround(new_width * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - else //VERTICAL - { - cur_y -= llround(new_height * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - } - - // update resize bars with new limits - LLResizeBar* last_resize_bar = NULL; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - - if (mOrientation == HORIZONTAL) - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinWidth, - (*panel_it)->mMinWidth + shrink_headroom_total); - } - else //VERTICAL - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinHeight, - (*panel_it)->mMinHeight + shrink_headroom_total); - } - - // toggle resize bars based on panel visibility, resizability, etc - BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; - (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); - - if (resize_bar_enabled) - { - last_resize_bar = (*panel_it)->mResizeBar; - } - } - - // hide last resize bar as there is nothing past it - // resize bars need to be in between two resizable panels - if (last_resize_bar) - { - last_resize_bar->setVisible(FALSE); - } - - // not enough room to fit existing contents - if (force_resize == FALSE - // layout did not complete by reaching target position - && ((mOrientation == VERTICAL && cur_y != -mPanelSpacing) - || (mOrientation == HORIZONTAL && cur_x != getRect().getWidth() + mPanelSpacing))) - { - // do another layout pass with all stacked elements contributing - // even those that don't usually resize - llassert_always(force_resize == FALSE); - updateLayout(TRUE); - } -} // end LLLayoutStack::updateLayout - - -LLLayoutStack::LLEmbeddedPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const -{ - e_panel_list_t::const_iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - if ((*panel_it)->mPanel == panelp) - { - return *panel_it; - } - } - return NULL; -} - -void LLLayoutStack::calcMinExtents() -{ - mMinWidth = 0; - mMinHeight = 0; - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - if (mOrientation == HORIZONTAL) - { - mMinHeight = llmax( mMinHeight, - (*panel_it)->mMinHeight); - mMinWidth += (*panel_it)->mMinWidth; - if (panel_it != mPanels.begin()) - { - mMinWidth += mPanelSpacing; - } - } - else //VERTICAL - { - mMinWidth = llmax( mMinWidth, - (*panel_it)->mMinWidth); - mMinHeight += (*panel_it)->mMinHeight; - if (panel_it != mPanels.begin()) - { - mMinHeight += mPanelSpacing; - } - } - } -} diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index d781dbae3..768476b5f 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -140,6 +140,8 @@ public: const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } + CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } + EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory); void setPanelParameters(LLXMLNodePtr node, LLView *parentp); @@ -226,6 +228,8 @@ protected: // Override to set not found list LLButton* getDefaultButton() { return mDefaultBtn; } LLCallbackMap::map_t mFactoryMap; + CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; + EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() private: @@ -254,56 +258,4 @@ private: }; // end class LLPanel - -class LLLayoutStack : public LLView -{ -public: - typedef enum e_layout_orientation - { - HORIZONTAL, - VERTICAL - } eLayoutOrientation; - - LLLayoutStack(eLayoutOrientation orientation); - virtual ~LLLayoutStack(); - - /*virtual*/ void draw(); - /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ void removeChild(LLView* ctrl); - - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); - - S32 getMinWidth() const { return mMinWidth; } - S32 getMinHeight() const { return mMinHeight; } - - typedef enum e_animate - { - NO_ANIMATE, - ANIMATE - } EAnimate; - - void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX); - void removePanel(LLPanel* panel); - void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE); - S32 getNumPanels() { return mPanels.size(); } - -private: - struct LLEmbeddedPanel; - - void updateLayout(BOOL force_resize = FALSE); - void calcMinExtents(); - S32 getDefaultHeight(S32 cur_height); - S32 getDefaultWidth(S32 cur_width); - - const eLayoutOrientation mOrientation; - - typedef std::vector e_panel_list_t; - e_panel_list_t mPanels; - LLEmbeddedPanel* findEmbeddedPanel(LLPanel* panelp) const; - - S32 mMinWidth; - S32 mMinHeight; - S32 mPanelSpacing; -}; // end class LLLayoutStack - #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index ed749aabb..09d41c589 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -42,6 +42,7 @@ #include "llui.h" #include "lluictrlfactory.h" #include "lluiimage.h" +#include "llurlaction.h" #include "llrect.h" #include "llfocusmgr.h" #include "lltimer.h" @@ -96,10 +97,6 @@ const S32 PREEDIT_STANDOUT_THICKNESS = 2; LLColor4 LLTextEditor::mLinkColor = LLColor4::blue; -void (* LLTextEditor::mURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL; - /////////////////////////////////////////////////////////////////// @@ -4350,11 +4347,15 @@ void LLTextEditor::loadKeywords(const std::string& filename, std::string name = utf8str_trim(funcs[i]); mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] ); } + segment_list_t segment_list; + mKeywords.findSegments(&segment_list, getWText(), mDefaultColor); - mKeywords.findSegments( &mSegments, mWText, mDefaultColor ); - - llassert( mSegments.front()->getStart() == 0 ); - llassert( mSegments.back()->getEnd() == getLength() ); + mSegments.clear(); + segment_list_t::iterator insert_it = mSegments.begin(); + for (segment_list_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + { + insert_it = mSegments.insert(insert_it, *list_it); + } } } @@ -4493,11 +4494,7 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) // and launch it if we did. if (mParseHTML && mHTML.length() > 0) { - //Special handling for slurls - if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) ) - { - if (mURLcallback!=NULL) (*mURLcallback)(mHTML); - } + LLUrlAction::clickAction(mHTML); mHTML.clear(); } } diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 91be64e65..51300450d 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -258,10 +258,6 @@ public: // Callbacks static void setLinkColor(LLColor4 color) { mLinkColor = color; } - static void setURLCallbacks(void (*callback1) (const std::string& url), - bool (*callback2) (const std::string& url), - bool (*callback3) (const std::string& url) ) - { mURLcallback = callback1; mSecondlifeURLcallback = callback2; mSecondlifeURLcallbackRightClick = callback3;} void setOnScrollEndCallback(void (*callback)(void*), void* userdata); @@ -509,9 +505,6 @@ private: // LLKeywords mKeywords; static LLColor4 mLinkColor; - static void (*mURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url); // Concrete LLTextCmd sub-classes used by the LLTextEditor base class class LLTextCmdInsert; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 40f43bf54..5a68c9efe 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -161,19 +161,15 @@ void LLUI::setMousePositionScreen(S32 x, S32 y) screen_x = llround((F32)x * getScaleFactor().mV[VX]); screen_y = llround((F32)y * getScaleFactor().mV[VY]); - LLCoordWindow window_point; - LLView::getWindow()->convertCoords(LLCoordGL(screen_x, screen_y), &window_point); - - LLView::getWindow()->setCursorPosition(window_point); + LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert()); } //static void LLUI::getMousePositionScreen(S32 *x, S32 *y) { LLCoordWindow cursor_pos_window; - LLView::getWindow()->getCursorPosition(&cursor_pos_window); - LLCoordGL cursor_pos_gl; - LLView::getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); + getWindow()->getCursorPosition(&cursor_pos_window); + LLCoordGL cursor_pos_gl(cursor_pos_window.convert()); *x = llround((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); *y = llround((F32)cursor_pos_gl.mY / getScaleFactor().mV[VX]); } diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 44cb0d820..aede0f859 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -32,6 +32,7 @@ #include "llcontrol.h" #include "llcoord.h" #include "v2math.h" +#include "llregistry.h" #include "llrender2dutils.h" #include "llpointer.h" #include "lluiimage.h" @@ -102,6 +103,7 @@ public: static void glRectToScreen(const LLRect& gl, LLRect *screen); // Returns the control group containing the control name, or the default group static LLControlGroup& getControlControlGroup (const std::string& controlname); + static LLWindow* getWindow() { return sWindow; } static void setHtmlHelp(LLHtmlHelp* html_help); // diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 71e226f85..a90263dba 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -467,6 +467,37 @@ void LLUICtrl::initFromXML(LLXMLNodePtr node, LLView* parent) setTabStop(has_tab_stop); + std::string str = node->getName()->mString; + std::string attrib_str; + LLXMLNodePtr child_node; + if(node->getChild((str+".commit_callback").c_str(),child_node,false)) + { + if(child_node->getAttributeString("function",attrib_str)) + { + commit_callback_t* func = (CommitCallbackRegistry::getValue(attrib_str)); + if (func) + { + if(child_node->getAttributeString("parameter",attrib_str)) + setCommitCallback(boost::bind((*func), this, LLSD(attrib_str))); + else + setCommitCallback(commit_signal_t::slot_type(*func)); + } + } + } + if(node->getChild((str+".validate_callback").c_str(),child_node,false)) + { + if(child_node->getAttributeString("function",attrib_str)) + { + enable_callback_t* func = (EnableCallbackRegistry::getValue(attrib_str)); + if (func) + { + if(child_node->getAttributeString("parameter",attrib_str)) + setCommitCallback(boost::bind((*func), this, LLSD(attrib_str))); + else + setCommitCallback(commit_signal_t::slot_type(*func)); + } + } + } LLView::initFromXML(node, parent); } diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index ed3ea6244..87100c452 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -40,6 +40,7 @@ #include #include +#include "llinitparam.h" #include "llviewmodel.h" // *TODO move dependency to .cpp file class LLUICtrl @@ -48,6 +49,7 @@ class LLUICtrl public: typedef boost::function commit_callback_t; typedef boost::signals2::signal commit_signal_t; + typedef boost::function enable_callback_t; typedef boost::signals2::signal enable_signal_t; typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata); @@ -149,6 +151,13 @@ public: } }; + template class CallbackRegistry : public LLRegistrySingleton + {}; + + class CommitCallbackRegistry : public CallbackRegistry{}; + // the enable callback registry is also used for visiblity callbacks + class EnableCallbackRegistry : public CallbackRegistry{}; + protected: commit_signal_t* mCommitSignal; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index d1d3ffd46..126a8ba46 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -285,8 +285,12 @@ void LLUICtrlFactory::buildFloaterInternal(LLFloater *floaterp, LLXMLNodePtr &ro mFactoryStack.push_front(factory_map); } + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + floaterp->getCommitCallbackRegistrar().pushScope(); + floaterp->getEnableCallbackRegistrar().pushScope(); floaterp->initFloaterXML(root, NULL, this, open); /* Flawfinder: ignore */ - + floaterp->getCommitCallbackRegistrar().popScope(); + floaterp->getEnableCallbackRegistrar().popScope(); if (LLUI::sShowXUINames) { floaterp->setToolTip(filename); @@ -381,8 +385,13 @@ BOOL LLUICtrlFactory::buildPanelInternal(LLPanel* panelp, LLXMLNodePtr &root, co mFactoryStack.push_front(factory_map); } + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->getCommitCallbackRegistrar().pushScope(); + panelp->getEnableCallbackRegistrar().pushScope(); didPost = panelp->initPanelXML(root, NULL, this); - + panelp->getCommitCallbackRegistrar().popScope(); + panelp->getEnableCallbackRegistrar().popScope(); + if (LLUI::sShowXUINames) { panelp->setToolTip(filename); diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp new file mode 100644 index 000000000..fd9b3d9a6 --- /dev/null +++ b/indra/llui/llurlaction.cpp @@ -0,0 +1,159 @@ +/** + * @file llurlaction.cpp + * @author Martin Reddy + * @brief A set of actions that can performed on Urls + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llurlaction.h" +#include "llview.h" +#include "llwindow.h" +#include "llurlregistry.h" + +// global state for the callback functions +LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback; +LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback; +LLUrlAction::url_callback_t LLUrlAction::sOpenURLExternalCallback; +LLUrlAction::execute_url_callback_t LLUrlAction::sExecuteSLURLCallback; + + +void LLUrlAction::setOpenURLCallback(url_callback_t cb) +{ + sOpenURLCallback = cb; +} + +void LLUrlAction::setOpenURLInternalCallback(url_callback_t cb) +{ + sOpenURLInternalCallback = cb; +} + +void LLUrlAction::setOpenURLExternalCallback(url_callback_t cb) +{ + sOpenURLExternalCallback = cb; +} + +void LLUrlAction::setExecuteSLURLCallback(execute_url_callback_t cb) +{ + sExecuteSLURLCallback = cb; +} + +void LLUrlAction::openURL(std::string url) +{ + if (sOpenURLCallback) + { + sOpenURLCallback(url); + } +} + +void LLUrlAction::openURLInternal(std::string url) +{ + if (sOpenURLInternalCallback) + { + sOpenURLInternalCallback(url); + } +} + +void LLUrlAction::openURLExternal(std::string url) +{ + if (sOpenURLExternalCallback) + { + sOpenURLExternalCallback(url); + } +} + +void LLUrlAction::executeSLURL(std::string url) +{ + if (sExecuteSLURLCallback) + { + sExecuteSLURLCallback(url); + } +} + +void LLUrlAction::clickAction(std::string url) +{ + // Try to handle as SLURL first, then http Url + if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url) ) + { + if (sOpenURLCallback) + { + sOpenURLCallback(url); + } + } +} + +void LLUrlAction::teleportToLocation(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + if (! match.getLocation().empty()) + { + executeSLURL("secondlife:///app/teleport/" + match.getLocation()); + } + } +} + +void LLUrlAction::showLocationOnMap(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + if (! match.getLocation().empty()) + { + executeSLURL("secondlife:///app/worldmap/" + match.getLocation()); + } + } +} + +void LLUrlAction::copyURLToClipboard(std::string url) +{ + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url)); +} + +void LLUrlAction::copyLabelToClipboard(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(match.getLabel())); + } +} + +void LLUrlAction::showProfile(std::string url) +{ + // Get id from 'secondlife:///app/{cmd}/{id}/{action}' + // and show its profile + LLURI uri(url); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + std::string id_str = path_array.get(2).asString(); + if (LLUUID::validate(id_str)) + { + std::string cmd_str = path_array.get(1).asString(); + executeSLURL("secondlife:///app/" + cmd_str + "/" + id_str + "/about"); + } + } +} diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h new file mode 100644 index 000000000..c34960b82 --- /dev/null +++ b/indra/llui/llurlaction.h @@ -0,0 +1,98 @@ +/** + * @file llurlaction.h + * @author Martin Reddy + * @brief A set of actions that can performed on Urls + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLURLACTION_H +#define LL_LLURLACTION_H + +#include +#include + +/// +/// The LLUrlAction class provides a number of static functions that +/// let you open Urls in web browsers, execute SLURLs, and copy Urls +/// to the clipboard. Many of these functions are not available at +/// the llui level, and must be supplied via a set of callbacks. +/// +/// N.B. The action functions specifically do not use const ref +/// strings so that a url parameter can be used into a boost::bind() +/// call under situations when that input string is deallocated before +/// the callback is executed. +/// +class LLUrlAction +{ +public: + LLUrlAction(); + + /// load a Url in the user's preferred web browser + static void openURL(std::string url); + + /// load a Url in the internal Second Life web browser + static void openURLInternal(std::string url); + + /// load a Url in the operating system's default web browser + static void openURLExternal(std::string url); + + /// execute the given secondlife: SLURL + static void executeSLURL(std::string url); + + /// if the Url specifies an SL location, teleport there + static void teleportToLocation(std::string url); + + /// if the Url specifies an SL location, show it on a map + static void showLocationOnMap(std::string url); + + /// perform the appropriate action for left-clicking on a Url + static void clickAction(std::string url); + + /// copy the label for a Url to the clipboard + static void copyLabelToClipboard(std::string url); + + /// copy a Url to the clipboard + static void copyURLToClipboard(std::string url); + + /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile + static void showProfile(std::string url); + + /// specify the callbacks to enable this class's functionality + typedef boost::function url_callback_t; + typedef boost::function execute_url_callback_t; + + static void setOpenURLCallback(url_callback_t cb); + static void setOpenURLInternalCallback(url_callback_t cb); + static void setOpenURLExternalCallback(url_callback_t cb); + static void setExecuteSLURLCallback(execute_url_callback_t cb); + +private: + // callbacks for operations we can perform on Urls + static url_callback_t sOpenURLCallback; + static url_callback_t sOpenURLInternalCallback; + static url_callback_t sOpenURLExternalCallback; + + static execute_url_callback_t sExecuteSLURLCallback; +}; + +#endif diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp new file mode 100644 index 000000000..d4c83d674 --- /dev/null +++ b/indra/llui/llurlentry.cpp @@ -0,0 +1,1188 @@ +/** + * @file llurlentry.cpp + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llurlentry.h" +#include "lluictrl.h" +#include "lluri.h" +#include "llurlmatch.h" +#include "llurlregistry.h" + +#include "llavatarnamecache.h" +#include "llcachename.h" +#include "lltrans.h" +//#include "lluicolortable.h" +#include "message.h" + +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + +// Utility functions +std::string localize_slapp_label(const std::string& url, const std::string& full_name); + + +LLUrlEntryBase::LLUrlEntryBase() +{} + +LLUrlEntryBase::~LLUrlEntryBase() +{ +} + +std::string LLUrlEntryBase::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +//virtual +std::string LLUrlEntryBase::getIcon(const std::string &url) +{ + return mIcon; +} + +/*LLStyle::Params LLUrlEntryBase::getStyle() const +{ + LLStyle::Params style_params; + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.font.style = "UNDERLINE"; + return style_params; +}*/ + + +std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const +{ + // return the id from a SLURL in the format /app/{cmd}/{id}/about + LLURI uri(url); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + return path_array.get(2).asString(); + } + return ""; +} + +std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const +{ + return LLURI::unescape(url); +} + +std::string LLUrlEntryBase::escapeUrl(const std::string &url) const +{ + static std::string no_escape_chars; + static bool initialized = false; + if (!initialized) + { + no_escape_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~!$?&()*+,@:;=/%#"; + + std::sort(no_escape_chars.begin(), no_escape_chars.end()); + initialized = true; + } + return LLURI::escape(url, no_escape_chars, true); +} + +std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const +{ + // return the label part from [http://www.example.org Label] + const char *text = url.c_str(); + S32 start = 0; + while (! isspace(text[start])) + { + start++; + } + while (text[start] == ' ' || text[start] == '\t') + { + start++; + } + return unescapeUrl(url.substr(start, url.size()-start-1)); +} + +std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const +{ + // return the url part from [http://www.example.org Label] + const char *text = string.c_str(); + S32 end = 0; + while (! isspace(text[end])) + { + end++; + } + return escapeUrl(string.substr(1, end-1)); +} + +void LLUrlEntryBase::addObserver(const std::string &id, + const std::string &url, + const LLUrlLabelCallback &cb) +{ + // add a callback to be notified when we have a label for the uuid + LLUrlEntryObserver observer; + observer.url = url; + observer.signal = new LLUrlLabelSignal(); + if (observer.signal) + { + observer.signal->connect(cb); + mObservers.insert(std::pair(id, observer)); + } +} + +// *NOTE: See also LLUrlEntryAgent::callObservers() +void LLUrlEntryBase::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + (*observer.signal)(it->second.url, label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ + // this allows us to have a global setting to turn off text hyperlink highlighting/action + bool globally_disabled = LLUI::sConfigGroup->getBOOL("DisableTextHyperlinkActions"); + + return globally_disabled; +} + +static std::string getStringAfterToken(const std::string str, const std::string token) +{ + size_t pos = str.find(token); + if (pos == std::string::npos) + { + return ""; + } + + pos += token.size(); + return str.substr(pos, str.size() - pos); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls +// +LLUrlEntryHTTP::LLUrlEntryHTTP() +{ + mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label +// We use the wikipedia syntax of [http://www.example.org Text] +// +LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel() +{ + mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getLabelFromWikiLink(url); + return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); +} + +std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const +{ + return getUrl(string); +} + +std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +// +// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +// +LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() +{ + mPattern = boost::regex("(" + "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR + "|" // or + "(?]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net + ")", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +// +// LLUrlEntrySLURL Describes generic http: and https: Urls +// +LLUrlEntrySLURL::LLUrlEntrySLURL() +{ + // see http://slurl.com/about.php for details on the SLURL format + mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - http://slurl.com/secondlife/Place/X/Y/Z + // - http://slurl.com/secondlife/Place/X/Y + // - http://slurl.com/secondlife/Place/X + // - http://slurl.com/secondlife/Place + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 5) + { + // handle slurl with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 4) + { + // handle slurl with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return location + " (" + x + "," + y + ")"; + } + else if (path_parts == 3) + { + // handle slurl with (X) coordinate + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return location + " (" + x + ")"; + } + else if (path_parts == 2) + { + // handle slurl with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return location; + } + + return url; +} + +std::string LLUrlEntrySLURL::getLocation(const std::string &url) const +{ + // return the part of the Url after slurl.com/secondlife/ + const std::string search_string = "/secondlife"; + size_t pos = url.find(search_string); + if (pos == std::string::npos) + { + return ""; + } + + pos += search_string.size() + 1; + return url.substr(pos, url.size() - pos); +} + +// +// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// +LLUrlEntryAgent::LLUrlEntryAgent() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_agent.xml"; + mIcon = "Generic_Person"; +} + +// virtual +void LLUrlEntryAgent::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + std::string final_label = localize_slapp_label(observer.url, label); + (*observer.signal)(observer.url, final_label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + std::string label = av_name.getCompleteName(); + + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +LLUUID LLUrlEntryAgent::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + +std::string LLUrlEntryAgent::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one + std::string url = getUrl(string); + + if (LLStringUtil::endsWith(url, "/inspect")) + { + return LLTrans::getString("TooltipAgentInspect"); + } + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("TooltipAgentMute"); + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("TooltipAgentUnmute"); + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("TooltipAgentIM"); + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("TooltipAgentPay"); + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("TooltipAgentOfferTeleport"); + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("TooltipAgentRequestFriend"); + } + return LLTrans::getString("TooltipAgentUrl"); +} + +bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect"); +} + +std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + std::string label = av_name.getCompleteName(); + + // handle suffixes like /mute or /offerteleport + label = localize_slapp_label(url, label); + return label; + } + else + { + LLAvatarNameCache::get(agent_id, + boost::bind(&LLUrlEntryAgent::onAvatarNameCache, + this, _1, _2)); + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryAgent::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +}*/ + +std::string localize_slapp_label(const std::string& url, const std::string& full_name) +{ + // customize label string based on agent SLapp suffix + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("SLappAgentMute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("SLappAgentUnmute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("SLappAgentIM") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("SLappAgentPay") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; + } + return full_name; +} + + +std::string LLUrlEntryAgent::getIcon(const std::string &url) +{ + // *NOTE: Could look up a badge here by calling getIDStringFromUrl() + // and looking up the badge for the agent. + return mIcon; +} + +// +// LLUrlEntryAgentName describes a Second Life agent name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// +LLUrlEntryAgentName::LLUrlEntryAgentName() +{} + +void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + std::string label = getName(av_name); + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + return getName(av_name); + } + else + { + LLAvatarNameCache::get(agent_id, + boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache, + this, _1, _2)); + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryAgentName::getStyle() const +{ + // don't override default colors + return LLStyle::Params().is_link(false); +}*/ + +// +// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// +LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getCompleteName(); +} + +// +// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// +LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.mDisplayName; +} + +// +// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// +LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername; +} + +// +// LLUrlEntryGroup Describes a Second Life group Url, e.g., +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// +LLUrlEntryGroup::LLUrlEntryGroup() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_group.xml"; + mIcon = "Generic_Group"; + mTooltip = LLTrans::getString("TooltipGroupUrl"); +} + + + +void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, + const std::string& name, + bool is_group) +{ + // received the group name from the server - tell our observers + callObservers(id.asString(), name, mIcon); +} + +LLUUID LLUrlEntryGroup::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + + +std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at login screen, give something short for layout + return LLTrans::getString("LoadingData"); + } + + std::string group_id_string = getIDStringFromUrl(url); + if (group_id_string.empty()) + { + // something went wrong, give raw url + return unescapeUrl(url); + } + + LLUUID group_id(group_id_string); + std::string group_name; + if (group_id.isNull()) + { + return LLTrans::getString("GroupNameNone"); + } + else if (gCacheName->getGroupName(group_id, group_name)) + { + return group_name; + } + else + { + gCacheName->getGroup(group_id, + boost::bind(&LLUrlEntryGroup::onGroupNameReceived, + this, _1, _2, _3)); + addObserver(group_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryGroup::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +}*/ + + +// +// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +// +LLUrlEntryInventory::LLUrlEntryInventory() +{ + //*TODO: add supporting of inventory item names with whitespaces + //this pattern cann't parse for example + //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_inventory.xml"; +} + +std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getStringAfterToken(url, "name="); + return LLURI::unescape(label.empty() ? url : label); +} + +// +// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +// +LLUrlEntryObjectIM::LLUrlEntryObjectIM() +{ + mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_objectim.xml"; +} + +std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("name")) + return query_map["name"]; + return unescapeUrl(url); +} + +std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("slurl")) + return query_map["slurl"]; + return LLUrlEntryBase::getLocation(url); +} + +// LLUrlEntryParcel statics. +LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); +LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); +LLHost LLUrlEntryParcel::sRegionHost(LLHost::invalid); +bool LLUrlEntryParcel::sDisconnected(false); +std::set LLUrlEntryParcel::sParcelInfoObservers; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +LLUrlEntryParcel::LLUrlEntryParcel() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_parcel.xml"; + mTooltip = LLTrans::getString("TooltipParcelUrl"); + + sParcelInfoObservers.insert(this); +} + +LLUrlEntryParcel::~LLUrlEntryParcel() +{ + sParcelInfoObservers.erase(this); +} + +std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no parcel id + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id + + // Add an observer to call LLUrlLabelCallback when we have parcel name. + addObserver(parcel_id_string, url, cb); + + LLUUID parcel_id(parcel_id_string); + + sendParcelInfoRequest(parcel_id); + + return unescapeUrl(url); +} + +void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) +{ + if (sRegionHost == LLHost::invalid || sDisconnected) return; + + LLMessageSystem *msg = gMessageSystem; + msg->newMessage("ParcelInfoRequest"); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, sAgentID ); + msg->addUUID("SessionID", sSessionID); + msg->nextBlock("Data"); + msg->addUUID("ParcelID", parcel_id); + msg->sendReliable(sRegionHost); +} + +void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label) +{ + callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon); +} + +// static +void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) +{ + std::string label(LLStringUtil::null); + if (!parcel_data.name.empty()) + { + label = parcel_data.name; + } + // If parcel name is empty use Sim_name (x, y, z) for parcel label. + else if (!parcel_data.sim_name.empty()) + { + S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + S32 region_z = llround(parcel_data.global_z); + + label = llformat("%s (%d, %d, %d)", + parcel_data.sim_name.c_str(), region_x, region_y, region_z); + } + + for (std::set::iterator iter = sParcelInfoObservers.begin(); + iter != sParcelInfoObservers.end(); + ++iter) + { + LLUrlEntryParcel* url_entry = *iter; + if (url_entry) + { + url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label); + } + } +} + +// +// LLUrlEntryPlace Describes secondlife:// URLs +// +LLUrlEntryPlace::LLUrlEntryPlace() +{ + mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife://Place/X/Y/Z + // - secondlife://Place/X/Y + // + LLURI uri(url); + std::string location = unescapeUrl(uri.hostName()); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 3) + { + // handle slurl with (X,Y,Z) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + std::string z = path_array[2]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 2) + { + // handle slurl with (X,Y) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + return location + " (" + x + "," + y + ")"; + } + + return url; +} + +std::string LLUrlEntryPlace::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:// part + return ::getStringAfterToken(url, "://"); +} + +// +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ + mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/region/Place/X/Y/Z + // - secondlife:///app/region/Place/X/Y + // - secondlife:///app/region/Place/X + // - secondlife:///app/region/Place + // + + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no region name + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string label = unescapeUrl(path_array[2]); // region name + + if (path_parts > 3) // secondlife:///app/region/Place/X + { + std::string x = path_array[3]; + label += " (" + x; + + if (path_parts > 4) // secondlife:///app/region/Place/X/Y + { + std::string y = path_array[4]; + label += "," + y; + + if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z + { + std::string z = path_array[5]; + label = label + "," + z; + } + } + + label += ")"; + } + + return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ + LLSD path_array = LLURI(url).pathArray(); + std::string region_name = unescapeUrl(path_array[2]); + return region_name; +} + +// +// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +// secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ +// +LLUrlEntryTeleport::LLUrlEntryTeleport() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_teleport.xml"; + mTooltip = LLTrans::getString("TooltipTeleportUrl"); +} + +std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle teleport SLURLs in the following formats: + // - secondlife:///app/teleport/Place/X/Y/Z + // - secondlife:///app/teleport/Place/X/Y + // - secondlife:///app/teleport/Place/X + // - secondlife:///app/teleport/Place + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + std::string host = uri.hostName(); + std::string label = LLTrans::getString("SLurlLabelTeleport"); + if (!host.empty()) + { + label += " " + host; + } + if (path_parts == 6) + { + // handle teleport url with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 5) + { + // handle teleport url with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + ")"; + } + else if (path_parts == 4) + { + // handle teleport url with (X) coordinate only + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return label + " " + location + " (" + x + ")"; + } + else if (path_parts == 3) + { + // handle teleport url with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return label + " " + location; + } + + return url; +} + +std::string LLUrlEntryTeleport::getLocation(const std::string &url) const +{ + // return the part of the Url after ///app/teleport + return ::getStringAfterToken(url, "app/teleport/"); +} + +// +// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +// with secondlife:// (used as a catch-all for cases not matched above) +// +LLUrlEntrySL::LLUrlEntrySL() +{ + mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// +// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +// +LLUrlEntrySLLabel::LLUrlEntrySLLabel() +{ + mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return getLabelFromWikiLink(url); +} + +std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574) + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.getTooltip(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::getTooltip(string); +} + +bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.underlineOnHoverOnly(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::underlineOnHoverOnly(string); +} + +// +// LLUrlEntryWorldMap Describes secondlife:/// URLs +// +LLUrlEntryWorldMap::LLUrlEntryWorldMap() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_map.xml"; + mTooltip = LLTrans::getString("TooltipMapUrl"); +} + +std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/worldmap/PLACE/X/Y/Z + // - secondlife:///app/worldmap/PLACE/X/Y + // - secondlife:///app/worldmap/PLACE/X + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts < 3) + { + return url; + } + + const std::string label = LLTrans::getString("SLurlLabelShowOnMap"); + std::string location = unescapeUrl(path_array[2]); + std::string x = (path_parts > 3) ? path_array[3] : "128"; + std::string y = (path_parts > 4) ? path_array[4] : "128"; + std::string z = (path_parts > 5) ? path_array[5] : "0"; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; +} + +std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:///app/worldmap/ part + return ::getStringAfterToken(url, "app/worldmap/"); +} + +// +// LLUrlEntryNoLink lets us turn of URL detection with ... tags +// +LLUrlEntryNoLink::LLUrlEntryNoLink() +{ + mPattern = boost::regex(".*?", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryNoLink::getUrl(const std::string &url) const +{ + // return the text between the and tags + return url.substr(8, url.size()-8-9); +} + +std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return getUrl(url); +} + +/*LLStyle::Params LLUrlEntryNoLink::getStyle() const +{ + // Don't render as URL (i.e. no context menu or hand cursor). + return LLStyle::Params().is_link(false); +}*/ + + +// +// LLUrlEntryIcon describes an icon with ... tags +// +LLUrlEntryIcon::LLUrlEntryIcon() +{ + mPattern = boost::regex("\\s*([^<]*)?\\s*", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryIcon::getUrl(const std::string &url) const +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getIcon(const std::string &url) +{ + // Grep icon info between ... tags + // matches[1] contains the icon name/path + boost::match_results matches; + mIcon = (boost::regex_match(url, matches, mPattern) && matches[1].matched) + ? matches[1] + : LLStringUtil::null; + LLStringUtil::trim(mIcon); + return mIcon; +} diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h new file mode 100644 index 000000000..0e2f4038f --- /dev/null +++ b/indra/llui/llurlentry.h @@ -0,0 +1,429 @@ +/** + * @file llurlentry.h + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLURLENTRY_H +#define LL_LLURLENTRY_H + +#include "lluuid.h" +#include "lluicolor.h" +#include "llstyle.h" + +#include "llhost.h" // for resolving parcel name by parcel id + +#include +#include +#include +#include + +class LLAvatarName; + +typedef boost::signals2::signal LLUrlLabelSignal; +typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; + +/// +/// LLUrlEntryBase is the base class of all Url types registered in the +/// LLUrlRegistry. Each derived classes provides a regular expression +/// to match the Url type (e.g., http://... or secondlife://...) along +/// with an optional icon to display next to instances of the Url in +/// a text display and a XUI file to use for any context menu popup. +/// Functions are also provided to compute an appropriate label and +/// tooltip/status bar text for the Url. +/// +/// Some derived classes of LLUrlEntryBase may wish to compute an +/// appropriate label for a Url by asking the server for information. +/// You must therefore provide a callback method, so that you can be +/// notified when an updated label has been received from the server. +/// This label should then be used to replace any previous label +/// that you received from getLabel() for the Url in question. +/// +class LLUrlEntryBase +{ +public: + LLUrlEntryBase(); + virtual ~LLUrlEntryBase(); + + /// Return the regex pattern that matches this Url + boost::regex getPattern() const { return mPattern; } + + /// Return the url from a string that matched the regex + virtual std::string getUrl(const std::string &string) const; + + /// Given a matched Url, return a label for the Url + virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } + + /// Return an icon that can be displayed next to Urls of this type + virtual std::string getIcon(const std::string &url); + + /// Return the style to render the displayed text + //virtual LLStyle::Params getStyle() const; + + /// Given a matched Url, return a tooltip string for the hyperlink + virtual std::string getTooltip(const std::string &string) const { return mTooltip; } + + /// Return the name of a XUI file containing the context menu items + std::string getMenuName() const { return mMenuName; } + + /// Return the name of a SL location described by this Url, if any + virtual std::string getLocation(const std::string &url) const { return ""; } + + /// Should this link text be underlined only when mouse is hovered over it? + virtual bool underlineOnHoverOnly(const std::string &string) const { return false; } + + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + + bool isLinkDisabled() const; + +protected: + std::string getIDStringFromUrl(const std::string &url) const; + std::string escapeUrl(const std::string &url) const; + std::string unescapeUrl(const std::string &url) const; + std::string getLabelFromWikiLink(const std::string &url) const; + std::string getUrlFromWikiLink(const std::string &string) const; + void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); + virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); + + typedef struct { + std::string url; + LLUrlLabelSignal *signal; + } LLUrlEntryObserver; + + boost::regex mPattern; + std::string mIcon; + std::string mMenuName; + std::string mTooltip; + std::multimap mObservers; +}; + +/// +/// LLUrlEntryHTTP Describes generic http: and https: Urls +/// +class LLUrlEntryHTTP : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTP(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels +/// +class LLUrlEntryHTTPLabel : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +/// +/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +/// +class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPNoProtocol(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +/// +/// LLUrlEntrySLURL Describes http://slurl.com/... Urls +/// +class LLUrlEntrySLURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySLURL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +class LLUrlEntryAgent : public LLUrlEntryBase +{ +public: + LLUrlEntryAgent(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); + /*virtual*/ std::string getTooltip(const std::string &string) const; + //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +protected: + /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + +/// +/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +/// that displays various forms of user name +/// This is a base class for the various implementations of name display +class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable +{ +public: + LLUrlEntryAgentName(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + //*virtual*/ LLStyle::Params getStyle() const; +protected: + // override this to pull out relevant name fields + virtual std::string getName(const LLAvatarName& avatar_name) = 0; +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + + +/// +/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +/// that displays the full display name + user name for an avatar +/// such as "James Linden (james.linden)" +class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentCompleteName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +/// that displays the just the display name for an avatar +/// such as "James Linden" +class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentDisplayName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +/// that displays the just the display name for an avatar +/// such as "james.linden" +class LLUrlEntryAgentUserName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentUserName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryGroup Describes a Second Life group Url, e.g., +/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +/// +class LLUrlEntryGroup : public LLUrlEntryBase +{ +public: + LLUrlEntryGroup(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; +private: + void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); +}; + +/// +/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +/// +class LLUrlEntryInventory : public LLUrlEntryBase +{ +public: + LLUrlEntryInventory(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +private: +}; + +/// +/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +/// +class LLUrlEntryObjectIM : public LLUrlEntryBase +{ +public: + LLUrlEntryObjectIM(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +private: +}; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +class LLUrlEntryParcel : public LLUrlEntryBase +{ +public: + struct LLParcelData + { + LLUUID parcel_id; + std::string name; + std::string sim_name; + F32 global_x; + F32 global_y; + F32 global_z; + }; + + LLUrlEntryParcel(); + ~LLUrlEntryParcel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + + // Sends a parcel info request to sim. + void sendParcelInfoRequest(const LLUUID& parcel_id); + + // Calls observers of certain parcel id providing them with parcel label. + void onParcelInfoReceived(const std::string &id, const std::string &label); + + // Processes parcel label and triggers notifying observers. + static void processParcelInfo(const LLParcelData& parcel_data); + + // Next 4 setters are used to update agent and viewer connection information + // upon events like user login, viewer disconnect and user changing region host. + // These setters are made public to be accessible from newview and should not be + // used in other cases. + static void setAgentID(const LLUUID& id) { sAgentID = id; } + static void setSessionID(const LLUUID& id) { sSessionID = id; } + static void setRegionHost(const LLHost& host) { sRegionHost = host; } + static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } + +private: + static LLUUID sAgentID; + static LLUUID sSessionID; + static LLHost sRegionHost; + static bool sDisconnected; + static std::set sParcelInfoObservers; +}; + +/// +/// LLUrlEntryPlace Describes a Second Life location Url, e.g., +/// secondlife://Ahern/50/50/50 +/// +class LLUrlEntryPlace : public LLUrlEntryBase +{ +public: + LLUrlEntryPlace(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +class LLUrlEntryRegion : public LLUrlEntryBase +{ +public: + LLUrlEntryRegion(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +/// secondlife:///app/teleport/Ahern/50/50/50/ +/// +class LLUrlEntryTeleport : public LLUrlEntryBase +{ +public: + LLUrlEntryTeleport(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// (used as a catch-all for cases not matched above) +/// +class LLUrlEntrySL : public LLUrlEntryBase +{ +public: + LLUrlEntrySL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +/// +class LLUrlEntrySLLabel : public LLUrlEntryBase +{ +public: + LLUrlEntrySLLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +}; + +/// +/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g., +/// secondlife:///app/worldmap/Ahern/50/50/50 +/// +class LLUrlEntryWorldMap : public LLUrlEntryBase +{ +public: + LLUrlEntryWorldMap(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryNoLink lets us turn of URL detection with ... tags +/// +class LLUrlEntryNoLink : public LLUrlEntryBase +{ +public: + LLUrlEntryNoLink(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + //*virtual*/ LLStyle::Params getStyle() const; +}; + +/// +/// LLUrlEntryIcon describes an icon with ... tags +/// +class LLUrlEntryIcon : public LLUrlEntryBase +{ +public: + LLUrlEntryIcon(); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); +}; + + +#endif diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp new file mode 100644 index 000000000..af4955ac4 --- /dev/null +++ b/indra/llui/llurlmatch.cpp @@ -0,0 +1,62 @@ +/** + * @file llurlmatch.cpp + * @author Martin Reddy + * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llurlmatch.h" + +LLUrlMatch::LLUrlMatch() : + mStart(0), + mEnd(0), + mUrl(""), + mLabel(""), + mTooltip(""), + mIcon(""), + mMenuName(""), + mLocation(""), + mUnderlineOnHoverOnly(false) +{ +} + +void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, + const std::string &label, const std::string &tooltip, + const std::string &icon, /*const LLStyle::Params& style,*/ + const std::string &menu, const std::string &location, + const LLUUID& id, bool underline_on_hover_only) +{ + mStart = start; + mEnd = end; + mUrl = url; + mLabel = label; + mTooltip = tooltip; + mIcon = icon; + //mStyle = style; + //mStyle.link_href = url; + mMenuName = menu; + mLocation = location; + mID = id; + mUnderlineOnHoverOnly = underline_on_hover_only; +} diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h new file mode 100644 index 000000000..af36e6925 --- /dev/null +++ b/indra/llui/llurlmatch.h @@ -0,0 +1,105 @@ +/** + * @file llurlmatch.h + * @author Martin Reddy + * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLURLMATCH_H +#define LL_LLURLMATCH_H + +//#include "linden_common.h" + +#include +#include +#include "llstyle.h" + +/// +/// LLUrlMatch describes a single Url that was matched within a string by +/// the LLUrlRegistry::findUrl() method. It includes the actual Url that +/// was matched along with its first/last character offset in the string. +/// An alternate label is also provided for creating a hyperlink, as well +/// as tooltip/status text, an icon, and a XUI file for a context menu +/// that can be used in a popup for a Url (e.g., Open, Copy URL, etc.) +/// +class LLUrlMatch +{ +public: + LLUrlMatch(); + + /// return true if this object does not contain a valid Url match yet + bool empty() const { return mUrl.empty(); } + + /// return the offset in the string for the first character of the Url + U32 getStart() const { return mStart; } + + /// return the offset in the string for the last character of the Url + U32 getEnd() const { return mEnd; } + + /// return the Url that has been matched in the input string + std::string getUrl() const { return mUrl; } + + /// return a label that can be used for the display of this Url + std::string getLabel() const { return mLabel; } + + /// return a message that could be displayed in a tooltip or status bar + std::string getTooltip() const { return mTooltip; } + + /// return the filename for an icon that can be displayed next to this Url + std::string getIcon() const { return mIcon; } + + /// Return the color to render the displayed text + //LLStyle::Params getStyle() const { return mStyle; } + + /// Return the name of a XUI file containing the context menu items + std::string getMenuName() const { return mMenuName; } + + /// return the SL location that this Url describes, or "" if none. + std::string getLocation() const { return mLocation; } + + /// Should this link text be underlined only when mouse is hovered over it? + bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; } + + /// Change the contents of this match object (used by LLUrlRegistry) + void setValues(U32 start, U32 end, const std::string &url, const std::string &label, + const std::string &tooltip, const std::string &icon, + /*const LLStyle::Params& style, */const std::string &menu, + const std::string &location, const LLUUID& id, + bool underline_on_hover_only = false ); + + const LLUUID& getID() const { return mID; } +private: + U32 mStart; + U32 mEnd; + std::string mUrl; + std::string mLabel; + std::string mTooltip; + std::string mIcon; + std::string mMenuName; + std::string mLocation; + LLUUID mID; + //LLStyle::Params mStyle; + bool mUnderlineOnHoverOnly; +}; + +#endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp new file mode 100644 index 000000000..235b002d0 --- /dev/null +++ b/indra/llui/llurlregistry.cpp @@ -0,0 +1,263 @@ +/** + * @file llurlregistry.cpp + * @author Martin Reddy + * @brief Contains a set of Url types that can be matched in a string + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llurlregistry.h" + +#include + +// default dummy callback that ignores any label updates from the server +void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, const std::string& icon) +{ +} + +LLUrlRegistry::LLUrlRegistry() +{ + mUrlEntry.reserve(20); + + // Urls are matched in the order that they were registered + registerUrl(new LLUrlEntryNoLink()); + registerUrl(new LLUrlEntryIcon()); + registerUrl(new LLUrlEntrySLURL()); + registerUrl(new LLUrlEntryHTTP()); + registerUrl(new LLUrlEntryHTTPLabel()); + registerUrl(new LLUrlEntryAgentCompleteName()); + registerUrl(new LLUrlEntryAgentDisplayName()); + registerUrl(new LLUrlEntryAgentUserName()); + // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since + // LLUrlEntryAgent is a less specific (catchall for agent urls) + registerUrl(new LLUrlEntryAgent()); + registerUrl(new LLUrlEntryGroup()); + registerUrl(new LLUrlEntryParcel()); + registerUrl(new LLUrlEntryTeleport()); + registerUrl(new LLUrlEntryRegion()); + registerUrl(new LLUrlEntryWorldMap()); + registerUrl(new LLUrlEntryObjectIM()); + registerUrl(new LLUrlEntryPlace()); + registerUrl(new LLUrlEntryInventory()); + registerUrl(new LLUrlEntryObjectIM()); + //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern, + //so it should be registered in the end of list + registerUrl(new LLUrlEntrySL()); + registerUrl(new LLUrlEntrySLLabel()); + // most common pattern is a URL without any protocol, + // e.g., "secondlife.com" + registerUrl(new LLUrlEntryHTTPNoProtocol()); +} + +LLUrlRegistry::~LLUrlRegistry() +{ + // free all of the LLUrlEntryBase objects we are holding + std::vector::iterator it; + for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) + { + delete *it; + } +} + +void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front) +{ + if (url) + { + if (force_front) // IDEVO + mUrlEntry.insert(mUrlEntry.begin(), url); + else + mUrlEntry.push_back(url); + } +} + +static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end) +{ + boost::cmatch result; + bool found; + + // regex_search can potentially throw an exception, so check for it + try + { + found = boost::regex_search(text, result, regex); + } + catch (std::runtime_error &) + { + return false; + } + + if (! found) + { + return false; + } + + // return the first/last character offset for the matched substring + start = static_cast(result[0].first - text); + end = static_cast(result[0].second - text) - 1; + + // we allow certain punctuation to terminate a Url but not match it, + // e.g., "http://foo.com/." should just match "http://foo.com/" + if (text[end] == '.' || text[end] == ',') + { + end--; + } + // ignore a terminating ')' when Url contains no matching '(' + // see DEV-19842 for details + else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos) + { + end--; + } + + return true; +} + +static bool stringHasUrl(const std::string &text) +{ + // fast heuristic test for a URL in a string. This is used + // to avoid lots of costly regex calls, BUT it needs to be + // kept in sync with the LLUrlEntry regexes we support. + return (text.find("://") != std::string::npos || + text.find("www.") != std::string::npos || + text.find(".com") != std::string::npos || + text.find(".net") != std::string::npos || + text.find(".edu") != std::string::npos || + text.find(".org") != std::string::npos || + text.find("") != std::string::npos || + text.find("::iterator it; + for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) + { + LLUrlEntryBase *url_entry = *it; + + U32 start = 0, end = 0; + if (matchRegex(text.c_str(), url_entry->getPattern(), start, end)) + { + // does this match occur in the string before any other match + if (start < match_start || match_entry == NULL) + { + match_start = start; + match_end = end; + match_entry = url_entry; + } + } + } + + // did we find a match? if so, return its details in the match object + if (match_entry) + { + // fill in the LLUrlMatch object and return it + std::string url = text.substr(match_start, match_end - match_start + 1); + match.setValues(match_start, match_end, + match_entry->getUrl(url), + match_entry->getLabel(url, cb), + match_entry->getTooltip(url), + match_entry->getIcon(url), + //match_entry->getStyle(), + match_entry->getMenuName(), + match_entry->getLocation(url), + match_entry->getID(url), + match_entry->underlineOnHoverOnly(url)); + return true; + } + + return false; +} + +bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUrlLabelCallback &cb) +{ + // boost::regex_search() only works on char or wchar_t + // types, but wchar_t is only 2-bytes on Win32 (not 4). + // So we use UTF-8 to make this work the same everywhere. + std::string utf8_text = wstring_to_utf8str(text); + if (findUrl(utf8_text, match, cb)) + { + // we cannot blindly return the start/end offsets from + // the UTF-8 string because it is a variable-length + // character encoding, so we need to update the start + // and end values to be correct for the wide string. + LLWString wurl = utf8str_to_wstring(match.getUrl()); + S32 start = text.find(wurl); + if (start == std::string::npos) + { + return false; + } + S32 end = start + wurl.size() - 1; + + match.setValues(start, end, match.getUrl(), + match.getLabel(), + match.getTooltip(), + match.getIcon(), + //match.getStyle(), + match.getMenuName(), + match.getLocation(), + match.getID(), + match.underlineOnHoverOnly()); + return true; + } + return false; +} + +bool LLUrlRegistry::hasUrl(const std::string &text) +{ + LLUrlMatch match; + return findUrl(text, match); +} + +bool LLUrlRegistry::hasUrl(const LLWString &text) +{ + LLUrlMatch match; + return findUrl(text, match); +} + +bool LLUrlRegistry::isUrl(const std::string &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} + +bool LLUrlRegistry::isUrl(const LLWString &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h new file mode 100644 index 000000000..da16171a9 --- /dev/null +++ b/indra/llui/llurlregistry.h @@ -0,0 +1,97 @@ +/** + * @file llurlregistry.h + * @author Martin Reddy + * @brief Contains a set of Url types that can be matched in a string + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLURLREGISTRY_H +#define LL_LLURLREGISTRY_H + +#include "llurlentry.h" +#include "llurlmatch.h" +#include "llsingleton.h" +#include "llstring.h" + +#include +#include + +/// This default callback for findUrl() simply ignores any label updates +void LLUrlRegistryNullCallback(const std::string &url, + const std::string &label, + const std::string &icon); + +/// +/// LLUrlRegistry is a singleton that contains a set of Url types that +/// can be matched in string. E.g., http:// or secondlife:// Urls. +/// +/// Clients call the findUrl() method on a string to locate the first +/// occurence of a supported Urls in that string. If findUrl() returns +/// true, the LLUrlMatch object will be updated to describe the Url +/// that was matched, including a label that can be used to hyperlink +/// the Url, an icon to display next to the Url, and a XUI menu that +/// can be used as a popup context menu for that Url. +/// +/// New Url types can be added to the registry with the registerUrl +/// method. E.g., to add support for a new secondlife:///app/ Url. +/// +/// Computing the label for a Url could involve a roundtrip request +/// to the server (e.g., to find the actual agent or group name). +/// As such, you can provide a callback method that will get invoked +/// when a new label is available for one of your matched Urls. +/// +class LLUrlRegistry : public LLSingleton +{ +public: + ~LLUrlRegistry(); + + /// add a new Url handler to the registry (will be freed on destruction) + /// optionally force it to the front of the list, making it take + /// priority over other regular expression matches for URLs + void registerUrl(LLUrlEntryBase *url, bool force_front = false); + + /// get the next Url in an input string, starting at a given character offset + /// your callback is invoked if the matched Url's label changes in the future + bool findUrl(const std::string &text, LLUrlMatch &match, + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + + /// a slightly less efficient version of findUrl for wide strings + bool findUrl(const LLWString &text, LLUrlMatch &match, + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + + // return true if the given string contains a URL that findUrl would match + bool hasUrl(const std::string &text); + bool hasUrl(const LLWString &text); + + // return true if the given string is a URL that findUrl would match + bool isUrl(const std::string &text); + bool isUrl(const LLWString &text); + +private: + LLUrlRegistry(); + friend class LLSingleton; + + std::vector mUrlEntry; +}; + +#endif diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 78e856d0d..085954448 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -332,15 +332,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_APP_SETTINGS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "app_settings"; + prefix = add(getAppRODataDir(), "app_settings"); break; case LL_PATH_CHARACTER: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "character"; + prefix = add(getAppRODataDir(), "character"); break; case LL_PATH_HELP: @@ -352,9 +348,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SETTINGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; + prefix = add(getOSUserAppDir(), "user_settings"); break; case LL_PATH_PER_SL_ACCOUNT: @@ -370,9 +364,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "logs"; + prefix = add(getOSUserAppDir(), "logs"); break; case LL_PATH_TEMP: @@ -396,9 +388,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOCAL_ASSETS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "local_assets"; + prefix = add(getAppRODataDir(), "local_assets"); break; case LL_PATH_EXECUTABLE: @@ -406,9 +396,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_FONTS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "fonts"; + prefix = add(getAppRODataDir(), "fonts"); break; default: @@ -418,12 +406,12 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd std::string filename = in_filename; if (!subdir2.empty()) { - filename = subdir2 + mDirDelimiter + filename; + filename = add(subdir2, filename); } if (!subdir1.empty()) { - filename = subdir1 + mDirDelimiter + filename; + filename = add(subdir1, filename); } if (prefix.empty()) @@ -436,9 +424,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd { if (!prefix.empty()) { - expanded_filename += prefix; - expanded_filename += mDirDelimiter; - expanded_filename += filename; + expanded_filename = add(prefix, filename); } else { @@ -530,12 +516,7 @@ std::string LLDir::getTempFilename() const random_uuid.generate(); random_uuid.toString(uuid_str); - std::string temp_filename = getTempDir(); - temp_filename += mDirDelimiter; - temp_filename += uuid_str; - temp_filename += ".tmp"; - - return temp_filename; + return add(getTempDir(), uuid_str + ".tmp"); } // static @@ -572,15 +553,9 @@ void LLDir::setLindenUserDir(const std::string &grid, const std::string &first, { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += firstlower; - mLindenUserDir += "_"; - mLindenUserDir += lastlower; + std::string userlower(first+"_"+last); + LLStringUtil::toLower(userlower); + mLindenUserDir = add(getOSUserAppDir(), userlower); if (!grid.empty()) { @@ -617,16 +592,9 @@ void LLDir::setPerAccountChatLogsDir(const std::string &grid, const std::string { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += firstlower; - mPerAccountChatLogsDir += "_"; - mPerAccountChatLogsDir += lastlower; - + std::string userlower(first+"_"+last); + LLStringUtil::toLower(userlower); + mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); if (!grid.empty()) { std::string gridlower(grid); @@ -644,22 +612,18 @@ void LLDir::setPerAccountChatLogsDir(const std::string &grid, const std::string void LLDir::setSkinFolder(const std::string &skin_folder) { mSkinDir = getSkinBaseDir(); - mSkinDir += mDirDelimiter; - mSkinDir += skin_folder; + append(mSkinDir, skin_folder); // user modifications to current skin // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle mUserSkinDir = getOSUserAppDir(); - mUserSkinDir += mDirDelimiter; - mUserSkinDir += "skins_sg1"; - mUserSkinDir += mDirDelimiter; - mUserSkinDir += skin_folder; + append(mUserSkinDir, "skins_sg1"); + append(mUserSkinDir, skin_folder); // base skin which is used as fallback for all skinned files // e.g. c:\program files\secondlife\skins\default mDefaultSkinDir = getSkinBaseDir(); - mDefaultSkinDir += mDirDelimiter; - mDefaultSkinDir += "default"; + append(mDefaultSkinDir, "default"); } bool LLDir::setCacheDir(const std::string &path) @@ -673,7 +637,7 @@ bool LLDir::setCacheDir(const std::string &path) else { LLFile::mkdir(path); - std::string tempname = path + mDirDelimiter + "temp"; + std::string tempname = add(path, "temp"); LLFILE* file = LLFile::fopen(tempname,"wt"); if (file) { @@ -706,6 +670,57 @@ void LLDir::dumpCurrentDirectories() LL_DEBUGS2("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; } +std::string LLDir::add(const std::string& path, const std::string& name) const +{ + std::string destpath(path); + append(destpath, name); + return destpath; +} + +void LLDir::append(std::string& destpath, const std::string& name) const +{ + // Delegate question of whether we need a separator to helper method. + SepOff sepoff(needSep(destpath, name)); + if (sepoff.first) // do we need a separator? + { + destpath += mDirDelimiter; + } + // If destpath ends with a separator, AND name starts with one, skip + // name's leading separator. + destpath += name.substr(sepoff.second); +} + +LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const +{ + if (path.empty() || name.empty()) + { + // If either path or name are empty, we do not need a separator + // between them. + return SepOff(false, 0); + } + // Here we know path and name are both non-empty. But if path already ends + // with a separator, or if name already starts with a separator, we need + // not add one. + std::string::size_type seplen(mDirDelimiter.length()); + bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter); + bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter); + if ((! path_ends_sep) && (! name_starts_sep)) + { + // If neither path nor name brings a separator to the junction, then + // we need one. + return SepOff(true, 0); + } + if (path_ends_sep && name_starts_sep) + { + // But if BOTH path and name bring a separator, we need not add one. + // Moreover, we should actually skip the leading separator of 'name'. + return SepOff(false, seplen); + } + // Here we know that either path_ends_sep or name_starts_sep is true -- + // but not both. So don't add a separator, and don't skip any characters: + // simple concatenation will do the trick. + return SepOff(false, 0); +} void dir_exists_or_crash(const std::string &dir_name) { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index d87249fd6..23f84bb41 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -140,7 +140,17 @@ class LLDir // Utility routine std::string buildSLOSCacheDir() const; + /// Append specified @a name to @a destpath, separated by getDirDelimiter() + /// if both are non-empty. + void append(std::string& destpath, const std::string& name) const; + /// Append specified @a name to @a path, separated by getDirDelimiter() + /// if both are non-empty. Return result, leaving @a path unmodified. + std::string add(const std::string& path, const std::string& name) const; + protected: + // Does an add() or append() call need a directory delimiter? + typedef std::pair SepOff; + SepOff needSep(const std::string& path, const std::string& name) const; std::string mAppName; // install directory under progams/ ie "SecondLife" std::string mExecutablePathAndName; // full path + Filename of .exe std::string mExecutableFilename; // Filename of .exe diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp index d4d444eb2..15acddd98 100644 --- a/indra/llwindow/lldragdropwin32.cpp +++ b/indra/llwindow/lldragdropwin32.cpp @@ -124,10 +124,9 @@ class LLDragDropWin32Target: ScreenToClient( mAppWindowHandle, &pt2 ); LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl ); switch (result) @@ -180,10 +179,9 @@ class LLDragDropWin32Target: ScreenToClient( mAppWindowHandle, &pt2 ); LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_TRACK, mDropUrl ); switch (result) @@ -237,15 +235,13 @@ class LLDragDropWin32Target: LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong( mAppWindowHandle, GWL_USERDATA ); if ( NULL != window_imp ) { - LLCoordGL gl_coord( 0, 0 ); - POINT pt_client; pt_client.x = pt.x; pt_client.y = pt.y; ScreenToClient( mAppWindowHandle, &pt_client ); LLCoordWindow cursor_coord_window( pt_client.x, pt_client.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + LLCoordGL gl_coord(cursor_coord_window.convert()); llinfos << "### (Drop) URL is: " << mDropUrl << llendl; llinfos << "### raw coords are: " << pt.x << " x " << pt.y << llendl; llinfos << "### client coords are: " << pt_client.x << " x " << pt_client.y << llendl; diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 9d0d73b81..8df242331 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -50,14 +50,15 @@ LLSplashScreen *gSplashScreenp = NULL; BOOL gDebugClicks = FALSE; BOOL gDebugWindowProc = FALSE; -const S32 gURLProtocolWhitelistCount = 3; -const std::string gURLProtocolWhitelist[] = { "file:", "http:", "https:" }; +const S32 gURLProtocolWhitelistCount = 4; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:"/*, "file:"*/ }; // CP: added a handler list - this is what's used to open the protocol and is based on registry entry // only meaningful difference currently is that file: protocols are opened using http: // since no protocol handler exists in registry for file: // Important - these lists should match - protocol to handler -const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" }; +// Maestro: This list isn't referenced anywhere that I could find +//const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" }; S32 OSMessageBox(const std::string& text, const std::string& caption, U32 type) @@ -112,6 +113,8 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags) mCursorHidden(FALSE), mBusyCount(0), mIsMouseClipping(FALSE), + mMinWindowWidth(0), + mMinWindowHeight(0), mSwapMethod(SWAP_METHOD_UNDEFINED), mHideCursorPermanent(FALSE), mFlags(flags), @@ -180,6 +183,51 @@ void *LLWindow::getMediaWindow() return getPlatformWindow(); } +BOOL LLWindow::setSize(LLCoordScreen size) +{ + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + +BOOL LLWindow::setSize(LLCoordWindow size) +{ + //HACK: we are inconsistently using minimum window dimensions + // in this case, we are constraining the inner "client" rect and other times + // we constrain the outer "window" rect + // There doesn't seem to be a good way to do this consistently without a bunch of platform + // specific code + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + + +// virtual +void LLWindow::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + mMinWindowWidth = min_width; + mMinWindowHeight = min_height; + + if (enforce_immediately) + { + LLCoordScreen cur_size; + if (!getMaximized() && getSize(&cur_size)) + { + if (cur_size.mX < mMinWindowWidth || cur_size.mY < mMinWindowHeight) + { + setSizeImpl(LLCoordScreen(llmin(cur_size.mX, mMinWindowWidth), llmin(cur_size.mY, mMinWindowHeight))); + } + } + } +} + //virtual void LLWindow::processMiscNativeEvents() { @@ -209,6 +257,8 @@ std::vector LLWindow::getDynamicFallbackFontList() return LLWindowWin32::getDynamicFallbackFontList(); #elif LL_DARWIN return LLWindowMacOSX::getDynamicFallbackFontList(); +#elif LL_MESA_HEADLESS + return std::vector(); #elif LL_SDL return LLWindowSDL::getDynamicFallbackFontList(); #else @@ -350,26 +400,26 @@ LLWindow* LLWindowManager::createWindow( #if LL_MESA_HEADLESS new_window = new LLWindowMesaHeadless(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth); #elif LL_SDL new_window = new LLWindowSDL(callbacks, title, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #elif LL_WINDOWS new_window = new LLWindowWin32(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #elif LL_DARWIN new_window = new LLWindowMacOSX(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #endif } else { new_window = new LLWindowHeadless(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth); } if (FALSE == new_window->isValid()) @@ -404,3 +454,42 @@ BOOL LLWindowManager::isWindowValid(LLWindow *window) { return sWindowList.find(window) != sWindowList.end(); } + +//coordinate conversion utility funcs that forward to llwindow +LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const +{ + const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL out; + windowp->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL from_gl(from); + windowp->convertCoords(from_gl, &self); +} + +LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const +{ + const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL out; + windowp->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL from_gl(from); + windowp->convertCoords(from_gl, &self); +} diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 73723c6f0..575c57816 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -46,7 +46,7 @@ const S32 MIN_WINDOW_HEIGHT = 256; // Refer to llwindow_test in test/common/llwindow for usage example -class LLWindow +class LLWindow : public LLInstanceTracker { public: struct LLWindowResolution @@ -79,7 +79,9 @@ public: virtual BOOL getSize(LLCoordScreen *size) = 0; virtual BOOL getSize(LLCoordWindow *size) = 0; virtual BOOL setPosition(LLCoordScreen position) = 0; - virtual BOOL setSize(LLCoordScreen size) = 0; + BOOL setSize(LLCoordScreen size); + BOOL setSize(LLCoordWindow size); + virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); virtual BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) = 0; virtual BOOL setCursorPosition(LLCoordWindow position) = 0; virtual BOOL getCursorPosition(LLCoordWindow *position) = 0; @@ -180,6 +182,9 @@ protected: // Defaults to true virtual BOOL canDelete(); + virtual BOOL setSizeImpl(LLCoordScreen size) = 0; + virtual BOOL setSizeImpl(LLCoordWindow size) = 0; + protected: LLWindowCallbacks* mCallbacks; @@ -200,6 +205,8 @@ protected: BOOL mHideCursorPermanent; U32 mFlags; U16 mHighSurrogate; + S32 mMinWindowWidth; + S32 mMinWindowHeight; // Handle a UTF-16 encoding unit received from keyboard. // Converting the series of UTF-16 encoding units to UTF-32 data, @@ -283,7 +290,7 @@ extern BOOL gDebugWindowProc; // Protocols, like "http" and "https" we support in URLs extern const S32 gURLProtocolWhitelistCount; extern const std::string gURLProtocolWhitelist[]; -extern const std::string gURLProtocolWhitelistHandler[]; +//extern const std::string gURLProtocolWhitelistHandler[]; void simpleEscapeString ( std::string& stringIn ); diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp index e6e6bc67f..dbdb40f5b 100644 --- a/indra/llwindow/llwindowheadless.cpp +++ b/indra/llwindow/llwindowheadless.cpp @@ -35,7 +35,7 @@ // LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { // Initialize a headless keyboard. diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 1e911d754..72f9684ca 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -46,7 +46,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;}; /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;}; /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;}; - /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;}; /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;}; /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;}; /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;}; @@ -95,7 +96,7 @@ public: S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL ignore_pixel_depth); virtual ~LLWindowHeadless(); private: diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index ed5d7b1e7..7893dedda 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -3,31 +3,25 @@ * @brief Prototypes for functions shared between llwindowmacosx.cpp * and llwindowmacosx-objc.mm. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 4ce225ca3..bebb537cd 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -3,31 +3,25 @@ * @brief Definition of functions shared between llwindowmacosx.cpp * and llwindowmacosx-objc.mm. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 0f4883665..303a23959 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -210,7 +210,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(NULL, fullscreen, flags) @@ -1256,7 +1256,7 @@ BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position) return TRUE; } -BOOL LLWindowMacOSX::setSize(const LLCoordScreen size) +BOOL LLWindowMacOSX::setSizeImpl(const LLCoordScreen size) { if(mWindow) { @@ -1266,6 +1266,31 @@ BOOL LLWindowMacOSX::setSize(const LLCoordScreen size) return TRUE; } +BOOL LLWindowMacOSX::setSizeImpl(const LLCoordWindow size) +{ + Rect client_rect; + if (mWindow) + { + OSStatus err = GetWindowBounds(mWindow, kWindowContentRgn, &client_rect); + if (err == noErr) + { + client_rect.right = client_rect.left + size.mX; + client_rect.bottom = client_rect.top + size.mY; + err = SetWindowBounds(mWindow, kWindowContentRgn, &client_rect); + } + if (err == noErr) + { + return TRUE; + } + else + { + llinfos << "Error setting size" << err << llendl; + return FALSE; + } + } + return FALSE; +} + void LLWindowMacOSX::swapBuffers() { aglSwapBuffers(mContext); @@ -2546,6 +2571,9 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e { // This is where we would constrain move/resize to a particular screen + const S32 MIN_WIDTH = mMinWindowWidth; + const S32 MIN_HEIGHT = mMinWindowHeight; + Rect currentBounds; Rect previousBounds; @@ -2570,14 +2598,14 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e mPreviousWindowRect = previousBounds; } - if ((currentBounds.right - currentBounds.left) < MIN_WINDOW_WIDTH) + if ((currentBounds.right - currentBounds.left) < MIN_WIDTH) { - currentBounds.right = currentBounds.left + MIN_WINDOW_WIDTH; + currentBounds.right = currentBounds.left + MIN_WIDTH; } - if ((currentBounds.bottom - currentBounds.top) < MIN_WINDOW_HEIGHT) + if ((currentBounds.bottom - currentBounds.top) < MIN_HEIGHT) { - currentBounds.bottom = currentBounds.top + MIN_WINDOW_HEIGHT; + currentBounds.bottom = currentBounds.top + MIN_HEIGHT; } SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), ¤tBounds); diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 89eca5750..3f75dc9ab 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -58,7 +58,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -123,7 +124,7 @@ public: protected: LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowMacOSX(); diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp index 11c22ac94..2b668d3fc 100644 --- a/indra/llwindow/llwindowmesaheadless.cpp +++ b/indra/llwindow/llwindowmesaheadless.cpp @@ -41,28 +41,25 @@ U16 *gMesaBuffer = NULL; LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { - if (use_gl) + llinfos << "MESA Init" << llendl; + mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); + + /* Allocate the image buffer */ + mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; + llassert(mMesaBuffer); + + gMesaBuffer = (U16*)mMesaBuffer; + + /* Bind the buffer to the context and make it current */ + if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) { - llinfos << "MESA Init" << llendl; - mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); - - /* Allocate the image buffer */ - mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; - llassert(mMesaBuffer); - - gMesaBuffer = (U16*)mMesaBuffer; - - /* Bind the buffer to the context and make it current */ - if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) - { - llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; - } - - llverify(gGLManager.initGL()); + llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; } + + llverify(gGLManager.initGL()); } diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index db7cb4375..c8d2bf282 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -50,7 +50,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;}; /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;}; /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;}; - /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;}; /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;}; /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;}; /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;}; @@ -97,7 +98,7 @@ public: LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL ignore_pixel_depth); ~LLWindowMesaHeadless(); private: diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 80a272789..60e95f11f 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -189,7 +189,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), Lock_Display(NULL), @@ -200,7 +200,6 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, gKeyboard->setCallbacks(callbacks); // Note that we can't set up key-repeat until after SDL has init'd video - // Ignore use_gl for now, only used for drones on PC mWindow = NULL; mNeedsResize = FALSE; mOverrideAspectRatio = 0.f; @@ -975,7 +974,7 @@ BOOL LLWindowSDL::setPosition(const LLCoordScreen position) return TRUE; } -BOOL LLWindowSDL::setSize(const LLCoordScreen size) +BOOL LLWindowSDL::setSizeImpl(const LLCoordScreen size) { if(mWindow) { @@ -993,6 +992,25 @@ BOOL LLWindowSDL::setSize(const LLCoordScreen size) return FALSE; } +BOOL LLWindowSDL::setSizeImpl(const LLCoordWindow size) +{ + if(mWindow) + { + // Push a resize event onto SDL's queue - we'll handle it + // when it comes out again. + SDL_Event event; + event.type = SDL_VIDEORESIZE; + event.resize.w = size.mX; + event.resize.h = size.mY; + SDL_PushEvent(&event); // copied into queue + + return TRUE; + } + + return FALSE; +} + + void LLWindowSDL::swapBuffers() { if (mWindow) @@ -1045,6 +1063,25 @@ void LLWindowSDL::setMouseClipping( BOOL b ) //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF); } +// virtual +void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + LLWindow::setMinSize(min_width, min_height, enforce_immediately); + +#if LL_X11 + // Set the minimum size limits for X11 window + // so the window manager doesn't allow resizing below those limits. + XSizeHints* hints = XAllocSizeHints(); + hints->flags |= PMinSize; + hints->min_width = mMinWindowWidth; + hints->min_height = mMinWindowHeight; + + XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints); + + XFree(hints); +#endif +} + BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position) { BOOL result = TRUE; @@ -1859,8 +1896,8 @@ void LLWindowSDL::gatherInput() llinfos << "Handling a resize event: " << event.resize.w << "x" << event.resize.h << llendl; - S32 width = llmax(event.resize.w, MIN_WINDOW_WIDTH); - S32 height = llmax(event.resize.h, MIN_WINDOW_HEIGHT); + S32 width = llmax(event.resize.w, (S32)mMinWindowWidth); + S32 height = llmax(event.resize.h, (S32)mMinWindowHeight); if (width != mWindow->w || height != mWindow->h) { @@ -2488,6 +2525,23 @@ void exec_cmd(const std::string& cmd, const std::string& arg) // Must begin with protocol identifier. void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) { + bool found = false; + S32 i; + for (i = 0; i < gURLProtocolWhitelistCount; i++) + { + if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos) + { + found = true; + break; + } + } + + if (!found) + { + llwarns << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << llendl; + return; + } + llinfos << "spawn_web_browser: " << escaped_url << llendl; #if LL_LINUX || LL_SOLARIS @@ -2623,9 +2677,9 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() if (sortpat) { // Sort the list of system fonts from most-to-least-desirable. - FcResult fresult; + FcResult result; fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &fresult); + NULL, &result); FcPatternDestroy(sortpat); } diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 4ec4236af..134554e6e 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -63,7 +63,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -76,6 +77,7 @@ public: /*virtual*/ void captureMouse(); /*virtual*/ void releaseMouse(); /*virtual*/ void setMouseClipping( BOOL b ); + /*virtual*/ void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); /*virtual*/ BOOL isClipboardTextAvailable(); /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst); @@ -147,7 +149,7 @@ public: protected: LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowSDL(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 01d198783..78f54a7a4 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -363,11 +363,15 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags) { + + //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways + LoadLibrary(L"opengl32.dll"); + mFSAASamples = fsaa_samples; mIconResource = gIconResource; mOverrideAspectRatio = 0.f; @@ -860,13 +864,11 @@ BOOL LLWindowWin32::setPosition(const LLCoordScreen position) return FALSE; } getSize(&size); - moveWindow(position, size); - return TRUE; } -BOOL LLWindowWin32::setSize(const LLCoordScreen size) +BOOL LLWindowWin32::setSizeImpl(const LLCoordScreen size) { LLCoordScreen position; @@ -876,11 +878,30 @@ BOOL LLWindowWin32::setSize(const LLCoordScreen size) return FALSE; } - moveWindow(position, size); + WINDOWPLACEMENT placement; + placement.length = sizeof(WINDOWPLACEMENT); + if (!GetWindowPlacement(mWindowHandle, &placement)) return FALSE; + + placement.showCmd = SW_RESTORE; + + if (!SetWindowPlacement(mWindowHandle, &placement)) return FALSE; + + moveWindow(position, size); return TRUE; } +BOOL LLWindowWin32::setSizeImpl(const LLCoordWindow size) +{ + RECT window_rect = {0, 0, size.mX, size.mY }; + DWORD dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dw_style = WS_OVERLAPPEDWINDOW; + + AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style); + + return setSizeImpl(LLCoordScreen(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top)); +} + // changing fullscreen resolution BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp) { @@ -891,12 +912,12 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO DWORD current_refresh; DWORD dw_ex_style; DWORD dw_style; - RECT window_rect; + RECT window_rect = {0, 0, 0, 0}; S32 width = size.mX; S32 height = size.mY; BOOL auto_show = FALSE; - if (mhRC) + if (mhRC) { auto_show = TRUE; resetDisplayResolution(); @@ -1093,6 +1114,37 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO return FALSE; } + // (EXP-1765) dump pixel data to see if there is a pattern that leads to unreproducible crash + LL_INFOS("Window") << "--- begin pixel format dump ---" << llendl ; + LL_INFOS("Window") << "pixel_format is " << pixel_format << llendl ; + LL_INFOS("Window") << "pfd.nSize: " << pfd.nSize << llendl ; + LL_INFOS("Window") << "pfd.nVersion: " << pfd.nVersion << llendl ; + LL_INFOS("Window") << "pfd.dwFlags: 0x" << std::hex << pfd.dwFlags << std::dec << llendl ; + LL_INFOS("Window") << "pfd.iPixelType: " << (int)pfd.iPixelType << llendl ; + LL_INFOS("Window") << "pfd.cColorBits: " << (int)pfd.cColorBits << llendl ; + LL_INFOS("Window") << "pfd.cRedBits: " << (int)pfd.cRedBits << llendl ; + LL_INFOS("Window") << "pfd.cRedShift: " << (int)pfd.cRedShift << llendl ; + LL_INFOS("Window") << "pfd.cGreenBits: " << (int)pfd.cGreenBits << llendl ; + LL_INFOS("Window") << "pfd.cGreenShift: " << (int)pfd.cGreenShift << llendl ; + LL_INFOS("Window") << "pfd.cBlueBits: " << (int)pfd.cBlueBits << llendl ; + LL_INFOS("Window") << "pfd.cBlueShift: " << (int)pfd.cBlueShift << llendl ; + LL_INFOS("Window") << "pfd.cAlphaBits: " << (int)pfd.cAlphaBits << llendl ; + LL_INFOS("Window") << "pfd.cAlphaShift: " << (int)pfd.cAlphaShift << llendl ; + LL_INFOS("Window") << "pfd.cAccumBits: " << (int)pfd.cAccumBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumRedBits: " << (int)pfd.cAccumRedBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumGreenBits: " << (int)pfd.cAccumGreenBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumBlueBits: " << (int)pfd.cAccumBlueBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumAlphaBits: " << (int)pfd.cAccumAlphaBits << llendl ; + LL_INFOS("Window") << "pfd.cDepthBits: " << (int)pfd.cDepthBits << llendl ; + LL_INFOS("Window") << "pfd.cStencilBits: " << (int)pfd.cStencilBits << llendl ; + LL_INFOS("Window") << "pfd.cAuxBuffers: " << (int)pfd.cAuxBuffers << llendl ; + LL_INFOS("Window") << "pfd.iLayerType: " << (int)pfd.iLayerType << llendl ; + LL_INFOS("Window") << "pfd.bReserved: " << (int)pfd.bReserved << llendl ; + LL_INFOS("Window") << "pfd.dwLayerMask: " << pfd.dwLayerMask << llendl ; + LL_INFOS("Window") << "pfd.dwVisibleMask: " << pfd.dwVisibleMask << llendl ; + LL_INFOS("Window") << "pfd.dwDamageMask: " << pfd.dwDamageMask << llendl ; + LL_INFOS("Window") << "--- end pixel format dump ---" << llendl ; + if (pfd.cColorBits < 32) { close(); @@ -1564,7 +1616,8 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO } else { - llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << " context." << llendl; + llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << + (LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << llendl; done = true; if (LLRender::sGLCoreProfile) @@ -1675,24 +1728,15 @@ void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScre BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position) { - LLCoordScreen screen_pos; - mMousePositionModified = TRUE; if (!mWindowHandle) { return FALSE; } - if (!convertCoords(position, &screen_pos)) - { - return FALSE; - } - // Inform the application of the new mouse position (needed for per-frame // hover/picking to function). - LLCoordGL gl_pos; - convertCoords(position, &gl_pos); - mCallbacks->handleMouseMove(this, gl_pos, (MASK)0); + mCallbacks->handleMouseMove(this, position.convert(), (MASK)0); // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking. // Because we have preemptively notified the application of the new @@ -1702,24 +1746,23 @@ BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position) while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { } - return SetCursorPos(screen_pos.mX, screen_pos.mY); + LLCoordScreen screen_pos(position.convert()); + return ::SetCursorPos(screen_pos.mX, screen_pos.mY); } BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position) { POINT cursor_point; - LLCoordScreen screen_pos; - if (!mWindowHandle || - !GetCursorPos(&cursor_point)) + if (!mWindowHandle + || !GetCursorPos(&cursor_point) + || !position) { return FALSE; } - screen_pos.mX = cursor_point.x; - screen_pos.mY = cursor_point.y; - - return convertCoords(screen_pos, position); + *position = LLCoordScreen(cursor_point.x, cursor_point.y).convert(); + return TRUE; } void LLWindowWin32::hideCursor() @@ -1880,7 +1923,7 @@ void LLWindowWin32::gatherInput() MSG msg; int msg_count = 0; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE) + while ((msg_count < MAX_MESSAGE_PER_UPDATE) && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { mCallbacks->handlePingWatchdog(this, "Main:TranslateGatherInput"); TranslateMessage(&msg); @@ -1936,6 +1979,10 @@ static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse"); LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param) { + // Ignore clicks not originated in the client area, i.e. mouse-up events not preceded with a WM_LBUTTONDOWN. + // This helps prevent avatar walking after maximizing the window by double-clicking the title bar. + static bool sHandleLeftMouseUp = true; + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA); @@ -2282,10 +2329,20 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE)); return 0; + case WM_NCLBUTTONDOWN: + { + window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_NCLBUTTONDOWN"); + // A click in a non-client area, e.g. title bar or window border. + sHandleLeftMouseUp = false; + } + break; + case WM_LBUTTONDOWN: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN"); LLFastTimer t2(FTM_MOUSEHANDLER); + sHandleLeftMouseUp = true; + if (LLWinImm::isAvailable() && window_imp->mPreeditor) { window_imp->interruptLanguageTextInput(); @@ -2296,15 +2353,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2326,15 +2383,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2350,6 +2407,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP"); LLFastTimer t2(FTM_MOUSEHANDLER); + + if (!sHandleLeftMouseUp) + { + sHandleLeftMouseUp = true; + break; + } + //if (gDebugClicks) //{ // LL_INFOS("Window") << "WndProc left button up" << LL_ENDL; @@ -2359,15 +2423,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2394,15 +2458,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2423,15 +2487,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2458,15 +2522,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2487,15 +2551,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2567,17 +2631,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_MOUSEMOVE: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MOUSEMOVE"); - window_imp->convertCoords(window_coord, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask); + window_imp->mCallbacks->handleMouseMove(window_imp, window_coord.convert(), mask); return 0; } case WM_GETMINMAXINFO: { LPMINMAXINFO min_max = (LPMINMAXINFO)l_param; - min_max->ptMinTrackSize.x = MIN_WINDOW_WIDTH; - min_max->ptMinTrackSize.y = MIN_WINDOW_HEIGHT; + min_max->ptMinTrackSize.x = window_imp->mMinWindowWidth; + min_max->ptMinTrackSize.y = window_imp->mMinWindowHeight; return 0; } @@ -3483,7 +3546,7 @@ void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position ) LLWinImm::setCompositionWindow( himc, &ime_form ); - sWinIMEWindowPosition.set( win_pos.mX, win_pos.mY ); + sWinIMEWindowPosition = win_pos; } LLWinImm::releaseContext(mWindowHandle, himc); diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index c24fbdca7..9f678b96c 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -57,7 +57,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -120,7 +121,7 @@ public: protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowWin32(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8a0406c18..c71c132bc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -219,7 +219,6 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlandmark.cpp llfloatermap.cpp - llfloatermediabrowser.cpp llfloatermemleak.cpp llfloatermessagelog.cpp llfloatermodelpreview.cpp @@ -260,6 +259,7 @@ set(viewer_SOURCE_FILES llfloaterurlentry.cpp llfloatervoicedevicesettings.cpp llfloaterwater.cpp + llfloaterwebcontent.cpp llfloaterwindlight.cpp llfloaterworldmap.cpp llfolderview.cpp @@ -314,6 +314,7 @@ set(viewer_SOURCE_FILES llmarketplacefunctions.cpp llmarketplacenotifications.cpp llmediactrl.cpp + llmediadataclient.cpp llmediaremotectrl.cpp llmenucommands.cpp llmenuoptionpathfindingrebakenavmesh.cpp @@ -364,7 +365,6 @@ set(viewer_SOURCE_FILES llpanellogin.cpp llpanelmaininventory.cpp llpanelmarketplaceoutboxinventory.cpp - llpanelmediahud.cpp llpanelmorph.cpp llpanelmsgs.cpp llpanelnetwork.cpp @@ -373,6 +373,7 @@ set(viewer_SOURCE_FILES llpanelpermissions.cpp llpanelpick.cpp llpanelplace.cpp + llpanelprimmediacontrols.cpp llpanelprofile.cpp llpanelskins.cpp llpanelvolume.cpp @@ -412,6 +413,7 @@ set(viewer_SOURCE_FILES llscrollingpanelparambase.cpp llselectmgr.cpp llsky.cpp + llslurl.cpp llspatialpartition.cpp llsprite.cpp llstartup.cpp @@ -479,9 +481,7 @@ set(viewer_SOURCE_FILES llviewerlayer.cpp llviewermedia.cpp llviewermedia_streamingaudio.cpp - llviewermediaeventemitter.cpp llviewermediafocus.cpp - llviewermediaobserver.cpp llviewermenu.cpp llviewermenufile.cpp llviewermessage.cpp @@ -716,7 +716,6 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlandmark.h llfloatermap.h - llfloatermediabrowser.h llfloatermemleak.h llfloatermessagelog.h llfloatermodelpreview.h @@ -757,6 +756,7 @@ set(viewer_HEADER_FILES llfloaterurlentry.h llfloatervoicedevicesettings.h llfloaterwater.h + llfloaterwebcontent.h llfloaterwindlight.h llfloaterworldmap.h llfolderview.h @@ -811,6 +811,7 @@ set(viewer_HEADER_FILES llmarketplacefunctions.h llmarketplacenotifications.h llmediactrl.h + llmediadataclient.h llmediaremotectrl.h llmenucommands.h llmenuoptionpathfindingrebakenavmesh.h @@ -861,7 +862,6 @@ set(viewer_HEADER_FILES llpanellogin.h llpanelmaininventory.h llpanelmarketplaceoutboxinventory.h - llpanelmediahud.h llpanelmorph.h llpanelmsgs.h llpanelnetwork.h @@ -870,6 +870,7 @@ set(viewer_HEADER_FILES llpanelpermissions.h llpanelpick.h llpanelplace.h + llpanelprimmediacontrols.h llpanelprofile.h llpanelskins.h llpanelvolume.h @@ -911,6 +912,7 @@ set(viewer_HEADER_FILES llselectmgr.h llsimplestat.h llsky.h + llslurl.h llspatialpartition.h llsprite.h llstartup.h @@ -980,7 +982,6 @@ set(viewer_HEADER_FILES llviewerkeyboard.h llviewerlayer.h llviewermedia.h - llviewermediaeventemitter.h llviewermediafocus.h llviewermediaobserver.h llviewermenu.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 377ee5cf1..fd0109f61 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5430,6 +5430,17 @@ This should be as low as possible, but too low may break functionality Value 0 + DisableTextHyperlinkActions + + Comment + Disable highlighting and linking of URLs in XUI text boxes + Persist + 1 + Type + Boolean + Value + 0 + DisableVerticalSync Comment @@ -16430,6 +16441,28 @@ This should be as low as possible, but too low may break functionality Boolean Value 1 + + SLURLDragNDrop + + Comment + Enable drag and drop of SLURLs onto the viewer + Persist + 1 + Type + Boolean + Value + 1 + + SLURLPassToOtherInstance + + Comment + Pass execution to prevoius viewer instances if there is a given slurl + Persist + 1 + Type + Boolean + Value + 1 soundsbeacon @@ -16508,6 +16541,19 @@ This should be as low as possible, but too low may break functionality Value 180 + + SLURLTeleportDirectly + + Comment + Clicking on a slurl will teleport you directly instead of opening places panel + Persist + 1 + Type + Boolean + Value + 0 + + UseHTTPInventory Comment diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index 895f72b04..5b9d7817a 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -60,6 +60,8 @@ #include "lltooldraganddrop.h" #include "llinventorymodel.h" #include "llselectmgr.h" +#include "llslurl.h" +#include "llurlaction.h" #include @@ -374,7 +376,6 @@ bool cmd_line_chat(std::string revised_text, EChatType type) S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); std::string region_name = LLWeb::escapeURL(revised_text.substr(command.length()+1)); - std::string url; if(!sAscentCmdLineMapToKeepPos) { @@ -383,8 +384,8 @@ bool cmd_line_chat(std::string revised_text, EChatType type) agent_z = 0; } - url = llformat("secondlife:///app/teleport/%s/%d/%d/%d",region_name.c_str(),agent_x,agent_y,agent_z); - LLURLDispatcher::dispatch(url, NULL, true); + LLSLURL slurl(region_name,LLVector3(agent_x,agent_y,agent_z)); + LLUrlAction::teleportToLocation(std::string("secondlife:///app/teleport/")+slurl.getLocationString()); } return false; } diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index b3e0eb56d..15765e321 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -56,6 +56,7 @@ #include "llsdmessage.h" #include "llsdutil.h" #include "llsky.h" +#include "llslurl.h" #include "llsmoothstep.h" #include "llstartup.h" #include "llstatusbar.h" @@ -63,6 +64,8 @@ #include "lltoolpie.h" #include "lltoolmgr.h" #include "lltrans.h" +#include "lluictrl.h" +#include "llurlentry.h" #include "llviewercontrol.h" #include "llviewerdisplay.h" #include "llviewerjoystick.h" @@ -294,6 +297,7 @@ LLAgent::LLAgent() : mAgentAccess(new LLAgentAccess(gSavedSettings)), mGodLevelChangeSignal(), mCanEditParcel(false), + mTeleportSourceSLURL(new LLSLURL), mTeleportRequest(), mTeleportFinishedSlot(), mTeleportFailedSlot(), @@ -434,6 +438,8 @@ LLAgent::~LLAgent() mAgentAccess = NULL; delete mEffectColor; mEffectColor = NULL; + delete mTeleportSourceSLURL; + mTeleportSourceSLURL = NULL; } // Handle any actions that need to be performed when the main app gains focus @@ -898,24 +904,6 @@ const LLHost& LLAgent::getRegionHost() const } } -//----------------------------------------------------------------------------- -// getSLURL() -// returns empty() if getRegion() == NULL -//----------------------------------------------------------------------------- -std::string LLAgent::getSLURL() const -{ - std::string slurl; - LLViewerRegion *regionp = getRegion(); - if (regionp) - { - LLVector3d agentPos = getPositionGlobal(); - S32 x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); - S32 y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); - S32 z = llround( (F32)agentPos.mdV[VZ] ); - slurl = LLURLDispatcher::buildSLURL(regionp->getName(), x, y, z); - } - return slurl; -} //----------------------------------------------------------------------------- // inPrelude() @@ -3865,7 +3853,7 @@ bool LLAgent::teleportCore(bool is_local) LLFloaterLand::hideInstance(); LLViewerParcelMgr::getInstance()->deselectLand(); - LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL); + LLViewerMediaFocus::getInstance()->clearFocus(); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC @@ -4257,7 +4245,7 @@ void LLAgent::setTeleportState(ETeleportState state) case TELEPORT_MOVING: // We're outa here. Save "back" slurl. - mTeleportSourceSLURL = getSLURL(); + LLAgentUI::buildSLURL(*mTeleportSourceSLURL); break; case TELEPORT_ARRIVING: @@ -4683,6 +4671,10 @@ void LLAgent::parseTeleportMessages(const std::string& xml_filename) }//end for (all message sets in xml file) } +const void LLAgent::getTeleportSourceSLURL(LLSLURL& slurl) const +{ + slurl = *mTeleportSourceSLURL; +} void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& directory_visibility ) { @@ -4736,14 +4728,13 @@ void LLAgent::renderAutoPilotTarget() } } -void LLAgent::showLureDestination(const std::string fromname, const int global_x, const int global_y, const int x, const int y, const int z, const std::string maturity) +void LLAgent::showLureDestination(const std::string fromname, U64& handle, U32 x, U32 y, U32 z) { - const LLVector3d posglobal = LLVector3d(F64(global_x), F64(global_y), F64(0)); - LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(posglobal); + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(mPendingLure) delete mPendingLure; - mPendingLure = new SHLureRequest(fromname,posglobal,x,y,z); + mPendingLure = new SHLureRequest(fromname,handle,x,y,z); if(siminfo) //We already have an entry? Go right on to displaying it. { @@ -4751,8 +4742,8 @@ void LLAgent::showLureDestination(const std::string fromname, const int global_x } else { - U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS); - U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS); + U32 grid_x, grid_y; + grid_from_region_handle(handle,&grid_x,&grid_y); LLWorldMapMessage::getInstance()->sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true); //Will call onFoundLureDestination on response } } @@ -4763,7 +4754,7 @@ void LLAgent::onFoundLureDestination(LLSimInfo *siminfo) return; if(!siminfo) - siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(mPendingLure->mPosGlobal); + siminfo = LLWorldMap::getInstance()->simInfoFromHandle(mPendingLure->mRegionHandle); if(siminfo) { const std::string sim_name = siminfo->getName(); @@ -4772,7 +4763,7 @@ void LLAgent::onFoundLureDestination(LLSimInfo *siminfo) llinfos << mPendingLure->mAvatarName << "'s teleport lure is to " << sim_name << " (" << maturity << ")" << llendl; LLStringUtil::format_map_t args; args["[NAME]"] = mPendingLure->mAvatarName; - args["[DESTINATION]"] = LLURLDispatcher::buildSLURL(sim_name, (S32)mPendingLure->mPosLocal[0], (S32)mPendingLure->mPosLocal[1], (S32)mPendingLure->mPosLocal[2] ); + args["[DESTINATION]"] = LLSLURL(sim_name,mPendingLure->mPosLocal).getSLURLString(); std::string msg = LLTrans::getString("TeleportOfferMaturity", args); if (!maturity.empty()) { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index bcc469f5e..1f5bf92f9 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -68,6 +68,7 @@ class LLPickInfo; class LLViewerObject; class LLAgentDropGroupViewerNode; class LLAgentAccess; +class LLSLURL; class LLSimInfo; class LLTeleportRequest; @@ -245,20 +246,18 @@ public: LLViewerRegion *getRegion() const { return mRegionp; } const LLHost& getRegionHost() const; BOOL inPrelude(); - std::string getSLURL() const; //Return uri for current region - + // struct SHLureRequest { - SHLureRequest(const std::string& avatar_name, const LLVector3d& pos_global, const int x, const int y, const int z) : - mAvatarName(avatar_name), mPosGlobal(pos_global) - { mPosLocal[0] = x; mPosLocal[1] = y; mPosLocal[2] = z;} + SHLureRequest(const std::string& avatar_name, U64& handle, const U32 x, const U32 y, const U32 z) : + mAvatarName(avatar_name), mRegionHandle(handle), mPosLocal(x,y,z) {} const std::string mAvatarName; - const LLVector3d mPosGlobal; - int mPosLocal[3]; + const U64 mRegionHandle; + LLVector3 mPosLocal; }; SHLureRequest *mPendingLure; - void showLureDestination(const std::string fromname, const int global_x, const int global_y, const int x, const int y, const int z, const std::string maturity); + void showLureDestination(const std::string fromname, U64& handle, U32 x, U32 y, U32 z); void onFoundLureDestination(LLSimInfo *siminfo = NULL); // @@ -568,13 +567,14 @@ public: public: static void parseTeleportMessages(const std::string& xml_filename); - const std::string& getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } + const void getTeleportSourceSLURL(LLSLURL& slurl) const; public: // ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings. static std::map sTeleportErrorMessages; static std::map sTeleportProgressMessages; -public: - std::string mTeleportSourceSLURL; // SLURL where last TP began. +private: + LLSLURL * mTeleportSourceSLURL; // SLURL where last TP began + //-------------------------------------------------------------------- // Teleport Actions //-------------------------------------------------------------------- diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index eb6b1835b..0fcc5fb2b 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -37,6 +37,7 @@ #include "llviewerregion.h" #include "llviewerparcelmgr.h" #include "llvoavatarself.h" +#include "llslurl.h" // [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.0d) #include "rlvhandler.h" // [/RLVa:KB] @@ -48,9 +49,8 @@ void LLAgentUI::buildFullname(std::string& name) name = gAgentAvatarp->getFullname(); } -/* //static -void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /= true/ ) +void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /*= true*/) { LLSLURL return_slurl; LLViewerRegion *regionp = gAgent.getRegion(); @@ -59,7 +59,7 @@ void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /= true/ ) return_slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); } slurl = return_slurl; -}*/ +} //static BOOL LLAgentUI::checkAgentDistance(const LLVector3& pole, F32 radius) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6cc328c4f..34255feba 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -56,6 +56,7 @@ #include "llmodaldialog.h" #include "llpumpio.h" #include "llmimetypes.h" +#include "llslurl.h" #include "llstartup.h" #include "llfocusmgr.h" #include "llviewerjoystick.h" @@ -80,6 +81,7 @@ #include "llvector4a.h" #include "llimpanel.h" // For LLVoiceClient and LLVoiceChannel #include "llvoavatarself.h" +#include "llurlmatch.h" #include "llprogressview.h" #include "llvocache.h" #include "llvopartgroup.h" @@ -94,6 +96,8 @@ #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" +#include "llurlaction.h" +#include "llurlentry.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include @@ -149,7 +153,6 @@ #include "llworld.h" #include "llhudeffecttrail.h" #include "llvectorperfoptions.h" -#include "llurlsimstring.h" #include "llwatchdog.h" // Included so that constants/settings might be initialized @@ -755,9 +758,11 @@ bool LLAppViewer::init() LLWeb::initClass(); // do this after LLUI - LLTextEditor::setURLCallbacks(&LLWeb::loadURL, - &LLURLDispatcher::dispatchFromTextEditor, - &LLURLDispatcher::dispatchFromTextEditor); + // Provide the text fields with callbacks for opening Urls + LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); + LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated @@ -1779,7 +1784,6 @@ bool LLAppViewer::cleanup() //Note: //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() //because some new image might be generated during cleaning up media. --bao - LLViewerMediaFocus::cleanupClass(); LLViewerMedia::cleanupClass(); LLViewerParcelMedia::cleanupClass(); gTextureList.shutdown(); // shutdown again in case a callback added something @@ -2329,30 +2333,17 @@ bool LLAppViewer::initConfiguration() // injection and steal passwords. Phoenix. SL-55321 if(clp.hasOption("url")) { - std::string slurl = clp.getOption("url")[0]; - if (LLURLDispatcher::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + gHippoGridManager->setCurrentGrid(LLStartUp::getStartSLURL().getGrid()); + + } } else if(clp.hasOption("slurl")) { - std::string slurl = clp.getOption("slurl")[0]; - if(LLURLDispatcher::isSLURL(slurl)) - { - if (LLURLDispatcher::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } - } + LLSLURL start_slurl(clp.getOption("slurl")[0]); + LLStartUp::setStartSLURL(start_slurl); } const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); @@ -2431,18 +2422,11 @@ bool LLAppViewer::initConfiguration() // don't call anotherInstanceRunning() when doing URL handoff, as // it relies on checking a marker file which will not work when running // out of different directories - std::string slurl; - if (!LLStartUp::sSLURLCommand.empty()) + + if (LLStartUp::getStartSLURL().isValid() && + (gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) { - slurl = LLStartUp::sSLURLCommand; - } - else if (LLURLSimString::parse()) - { - slurl = LLURLSimString::getURL(); - } - if (!slurl.empty()) - { - if (sendURLToOtherInstance(slurl)) + if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) { // successfully handed off URL to existing instance, exit return false; @@ -2498,9 +2482,10 @@ bool LLAppViewer::initConfiguration() // need to do this here - need to have initialized global settings first std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); - if ( nextLoginLocation.length() ) + if ( !nextLoginLocation.empty() ) { - LLURLSimString::setString( nextLoginLocation ); + LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "< diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index b8fa0a1bf..50aa2921e 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -896,7 +896,7 @@ class LLChatHandler : public LLCommandHandler { public: // not allowed from outside the app - LLChatHandler() : LLCommandHandler("chat", true) { } + LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index a04182a91..679a30891 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -4,46 +4,47 @@ * which manipulate user interface. For example, the command * "agent (uuid) about" will open the UI for an avatar's profile. * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llcommandhandler.h" +#include "llnotificationsutil.h" +//#include "llcommanddispatcherlistener.h" +#include "stringize.h" // system includes #include +#define THROTTLE_PERIOD 5 // required seconds between throttled commands + +//static LLCommandDispatcherListener sCommandDispatcherListener; + //--------------------------------------------------------------------------- // Underlying registry for command handlers, not directly accessible. //--------------------------------------------------------------------------- struct LLCommandHandlerInfo { - bool mRequireTrustedBrowser; + LLCommandHandler::EUntrustedAccess mUntrustedBrowserAccess; LLCommandHandler* mHandler; // safe, all of these are static objects }; @@ -51,14 +52,18 @@ class LLCommandHandlerRegistry { public: static LLCommandHandlerRegistry& instance(); - void add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler); + void add(const char* cmd, + LLCommandHandler::EUntrustedAccess untrusted_access, + LLCommandHandler* handler); bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); private: + friend LLSD LLCommandDispatcher::enumerate(); std::map mMap; }; @@ -72,10 +77,12 @@ LLCommandHandlerRegistry& LLCommandHandlerRegistry::instance() return instance; } -void LLCommandHandlerRegistry::add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler) +void LLCommandHandlerRegistry::add(const char* cmd, + LLCommandHandler::EUntrustedAccess untrusted_access, + LLCommandHandler* handler) { LLCommandHandlerInfo info; - info.mRequireTrustedBrowser = require_trusted_browser; + info.mUntrustedBrowserAccess = untrusted_access; info.mHandler = handler; mMap[cmd] = info; @@ -85,17 +92,60 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { + static bool slurl_blocked = false; + static bool slurl_throttled = false; + static F64 last_throttle_time = 0.0; + F64 cur_time = 0.0; std::map::iterator it = mMap.find(cmd); if (it == mMap.end()) return false; const LLCommandHandlerInfo& info = it->second; - if (!trusted_browser && info.mRequireTrustedBrowser) + if (!trusted_browser) { - // block request from external browser, but report as - // "handled" because it was well formatted. - LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; - return true; + switch (info.mUntrustedBrowserAccess) + { + case LLCommandHandler::UNTRUSTED_ALLOW: + // fall through and let the command be handled + break; + + case LLCommandHandler::UNTRUSTED_BLOCK: + // block request from external browser, but report as + // "handled" because it was well formatted. + LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_blocked) + { + LLNotificationsUtil::add("BlockedSLURL"); + slurl_blocked = true; + } + return true; + + case LLCommandHandler::UNTRUSTED_THROTTLE: + // if users actually click on a link, we don't need to throttle it + // (throttling mechanism is used to prevent an avalanche of clicks via + // javascript + if ( nav_type == "clicked" ) + { + break; + } + + cur_time = LLTimer::getElapsedSeconds(); + if (cur_time < last_throttle_time + THROTTLE_PERIOD) + { + // block request from external browser if it happened + // within THROTTLE_PERIOD seconds of the last command + LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_throttled) + { + LLNotificationsUtil::add("ThrottledSLURL"); + slurl_throttled = true; + } + return true; + } + last_throttle_time = cur_time; + break; + } } if (!info.mHandler) return false; return info.mHandler->handle(params, query_map, web); @@ -106,10 +156,9 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, //--------------------------------------------------------------------------- LLCommandHandler::LLCommandHandler(const char* cmd, - bool require_trusted_browser) + EUntrustedAccess untrusted_access) { - LLCommandHandlerRegistry::instance().add( - cmd, require_trusted_browser, this); + LLCommandHandlerRegistry::instance().add(cmd, untrusted_access, this); } LLCommandHandler::~LLCommandHandler() @@ -127,8 +176,62 @@ bool LLCommandDispatcher::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { return LLCommandHandlerRegistry::instance().dispatch( - cmd, params, query_map, web, trusted_browser); + cmd, params, query_map, web, nav_type, trusted_browser); +} + +static std::string lookup(LLCommandHandler::EUntrustedAccess value); + +LLSD LLCommandDispatcher::enumerate() +{ + LLSD response; + LLCommandHandlerRegistry& registry(LLCommandHandlerRegistry::instance()); + for (std::map::const_iterator chi(registry.mMap.begin()), + chend(registry.mMap.end()); + chi != chend; ++chi) + { + LLSD info; + info["untrusted"] = chi->second.mUntrustedBrowserAccess; + info["untrusted_str"] = lookup(chi->second.mUntrustedBrowserAccess); + response[chi->first] = info; + } + return response; +} + +/*------------------------------ lookup stuff ------------------------------*/ +struct symbol_info +{ + const char* name; + LLCommandHandler::EUntrustedAccess value; +}; + +#define ent(SYMBOL) \ + { \ + #SYMBOL + 28, /* skip "LLCommandHandler::UNTRUSTED_" prefix */ \ + SYMBOL \ + } + +symbol_info symbols[] = +{ + ent(LLCommandHandler::UNTRUSTED_ALLOW), // allow commands from untrusted browsers + ent(LLCommandHandler::UNTRUSTED_BLOCK), // ignore commands from untrusted browsers + ent(LLCommandHandler::UNTRUSTED_THROTTLE) // allow untrusted, but only a few per min. +}; + +#undef ent + +static std::string lookup(LLCommandHandler::EUntrustedAccess value) +{ + for (symbol_info *sii(symbols), *siend(symbols + (sizeof(symbols)/sizeof(symbols[0]))); + sii != siend; ++sii) + { + if (sii->value == value) + { + return sii->name; + } + } + return STRINGIZE("UNTRUSTED_" << value); } diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index 5cb3ee73d..1e0895565 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -4,36 +4,32 @@ * which manipulate user interface. For example, the command * "agent (uuid) about" will open the UI for an avatar's profile. * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LLCOMMANDHANDLER_H #define LLCOMMANDHANDLER_H +#include "llsd.h" + /* Example: secondlife:///app/foo/ Command "foo" that takes one parameter, a UUID. @@ -43,7 +39,7 @@ public: // Inform the system you handle commands starting // with "foo" and they are only allowed from // "trusted" (pointed at Linden content) browsers - LLFooHandler() : LLCommandHandler("foo", true) { } + LLFooHandler() : LLCommandHandler("foo", UNTRUSTED_BLOCK) { } // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, @@ -65,7 +61,14 @@ class LLMediaCtrl; class LLCommandHandler { public: - LLCommandHandler(const char* command, bool allow_from_untrusted_browser); + enum EUntrustedAccess + { + UNTRUSTED_ALLOW, // allow commands from untrusted browsers + UNTRUSTED_BLOCK, // ignore commands from untrusted browsers + UNTRUSTED_THROTTLE // allow untrusted, but only a few per min. + }; + + LLCommandHandler(const char* command, EUntrustedAccess untrusted_access); // Automatically registers object to get called when // command is executed. All commands can be processed // in links from LLMediaCtrl, but some (like teleport) @@ -92,10 +95,14 @@ public: const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); // Execute a command registered via the above mechanism, // passing string parameters. // Returns true if command was found and executed correctly. + /// Return an LLSD::Map of registered LLCommandHandlers and associated + /// info (e.g. EUntrustedAccess). + static LLSD enumerate(); }; #endif diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 95245b8f0..5d0371601 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -190,8 +190,9 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) mImportanceToCamera = 0.f ; mBoundingSphereRadius = 0.0f ; -} + mHasMedia = FALSE ; +} void LLFace::destroy() { @@ -2065,6 +2066,20 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, return TRUE; } +//check if the face has a media +BOOL LLFace::hasMedia() const +{ + if(mHasMedia) + { + return TRUE ; + } + if(mTexture.notNull()) + { + return mTexture->hasParcelMedia() ; //if has a parcel media + } + + return FALSE ; //no media. +} const F32 LEAST_IMPORTANCE = 0.05f ; const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ; @@ -2134,7 +2149,7 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) t.load3(camera->getOrigin().mV); lookAt.setSub(center, t); F32 dist = lookAt.getLength3().getF32(); - dist = llmax(dist-size.getLength3().getF32(), 0.f); + dist = llmax(dist-size.getLength3().getF32(), 0.001f); lookAt.normalize3fast() ; //get area of circle around node @@ -2145,9 +2160,33 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) x_axis.load3(camera->getXAxis().mV); cos_angle_to_view_dir = lookAt.dot3(x_axis).getF32(); + //if has media, check if the face is out of the view frustum. + if(hasMedia()) + { + if(!camera->AABBInFrustum(center, size)) + { + mImportanceToCamera = 0.f ; + return false ; + } + if(cos_angle_to_view_dir > camera->getCosHalfFov()) //the center is within the view frustum + { + cos_angle_to_view_dir = 1.0f ; + } + else + { + LLVector4a d; + d.setSub(lookAt, x_axis); + + if(dist * dist * d.dot3(d) < size_squared) + { + cos_angle_to_view_dir = 1.0f ; + } + } + } + if(dist < mBoundingSphereRadius) //camera is very close { - cos_angle_to_view_dir = 1.0f; + cos_angle_to_view_dir = 1.0f ; mImportanceToCamera = 1.0f; } else diff --git a/indra/newview/llface.h b/indra/newview/llface.h index b59dff8f0..68cdcadde 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -224,6 +224,9 @@ public: F32 getTextureVirtualSize() ; F32 getImportanceToCamera()const {return mImportanceToCamera ;} + + void setHasMedia(bool has_media) { mHasMedia = has_media ;} + BOOL hasMedia() const ; //vertex buffer tracking void setVertexBuffer(LLVertexBuffer* buffer); void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL @@ -289,6 +292,7 @@ private: //based on the distance from the face to the view point and the angle from the face center to the view direction. F32 mImportanceToCamera ; F32 mBoundingSphereRadius ; + bool mHasMedia ; protected: static BOOL sSafeRenderSelect; diff --git a/indra/newview/llfloateravatarinfo.cpp b/indra/newview/llfloateravatarinfo.cpp index cd0cbbc3c..642da1d4a 100644 --- a/indra/newview/llfloateravatarinfo.cpp +++ b/indra/newview/llfloateravatarinfo.cpp @@ -97,7 +97,7 @@ public: } // requires trusted browser to trigger - LLAgentHandler() : LLCommandHandler("agent", true) { } + LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) diff --git a/indra/newview/llfloaterclassified.cpp b/indra/newview/llfloaterclassified.cpp index 07603039b..f695da399 100644 --- a/indra/newview/llfloaterclassified.cpp +++ b/indra/newview/llfloaterclassified.cpp @@ -55,7 +55,7 @@ class LLClassifiedHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLClassifiedHandler() : LLCommandHandler("classified", true) { } + LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index 0ec2a7637..b526767f8 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -56,7 +56,7 @@ class LLEventHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLEventHandler() : LLCommandHandler("event", true) { } + LLEventHandler() : LLCommandHandler("event", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloatergroupinfo.cpp b/indra/newview/llfloatergroupinfo.cpp index 3e2e845e3..1926563f6 100644 --- a/indra/newview/llfloatergroupinfo.cpp +++ b/indra/newview/llfloatergroupinfo.cpp @@ -54,11 +54,12 @@ const LLRect FGI_RECT(0, 530, 420, 0); // std::map LLFloaterGroupInfo::sInstances; +//moved to llgroupactions.cpp in v2 class LLGroupHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLGroupHandler() : LLCommandHandler("group", true) { } + LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterhandler.h b/indra/newview/llfloaterhandler.h index 31ea80c12..cd9d8b537 100644 --- a/indra/newview/llfloaterhandler.h +++ b/indra/newview/llfloaterhandler.h @@ -38,7 +38,7 @@ class LLFloaterHandler : public LLCommandHandler { public: - LLFloaterHandler() : LLCommandHandler("floater", true) { } + LLFloaterHandler() : LLCommandHandler("floater", UNTRUSTED_BLOCK) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web); }; diff --git a/indra/newview/llfloaterhtmlsimple.cpp b/indra/newview/llfloaterhtmlsimple.cpp index 8091c1e97..67650be48 100644 --- a/indra/newview/llfloaterhtmlsimple.cpp +++ b/indra/newview/llfloaterhtmlsimple.cpp @@ -63,5 +63,5 @@ void LLFloaterHtmlSimple::navigateTo(const std::string &url) void LLFloaterHtmlSimple::setTrusted(bool trusted) { LLMediaCtrl* web = getChild("browser"); - web->setTrusted(trusted); + web->setTrustedContent(trusted); } diff --git a/indra/newview/llfloaterhud.cpp b/indra/newview/llfloaterhud.cpp index cb8071588..f2743e0cf 100644 --- a/indra/newview/llfloaterhud.cpp +++ b/indra/newview/llfloaterhud.cpp @@ -78,7 +78,7 @@ LLFloaterHUD::LLFloaterHUD() if (mWebBrowser) { // Open links in internal browser - mWebBrowser->setOpenInExternalBrowser(false); + //mWebBrowser->setOpenInExternalBrowser(false); // This is a "chrome" floater, so we don't want anything to // take focus (as the user needs to be able to walk with diff --git a/indra/newview/llfloaternotificationsconsole.h b/indra/newview/llfloaternotificationsconsole.h index 1a436b8bf..037255318 100644 --- a/indra/newview/llfloaternotificationsconsole.h +++ b/indra/newview/llfloaternotificationsconsole.h @@ -34,6 +34,7 @@ #define LL_LLFLOATER_NOTIFICATIONS_CONSOLE_H #include "llfloater.h" +#include "lllayoutstack.h" #include "llnotifications.h" class LLFloaterNotificationConsole : diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index 3ecc140b7..f2b2049a4 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -42,10 +42,13 @@ #include "llfloatergroupinfo.h" #include "llfloatermute.h" #include "llmutelist.h" -#include "llsdutil.h" +#include "llslurl.h" +#include "lltrans.h" +#include "llui.h" +#include "lluictrl.h" #include "lluictrlfactory.h" -#include "llurldispatcher.h" -#include "llviewercontrol.h" +#include "llurlaction.h" +#include "llweb.h" // [RLVa:KB] - Version: 1.23.4 #include "rlvhandler.h" @@ -61,7 +64,7 @@ public: BOOL postBuild(void); - void update(const LLUUID& id, const std::string& name, const std::string& slurl, const LLUUID& owner, bool owner_is_group); + void update(LLSD& payload); // UI Handlers static void onClickMap(void* data); @@ -72,15 +75,14 @@ public: private: LLUUID mObjectID; - std::string mObjectName; - std::string mSlurl; LLUUID mOwnerID; - std::string mOwnerName; - bool mOwnerIsGroup; + std::string mSLurl; + std::string mName; + bool mGroupOwned; }; LLFloaterObjectIMInfo::LLFloaterObjectIMInfo(const LLSD& seed) -: mObjectID(), mObjectName(), mSlurl(), mOwnerID(), mOwnerName(), mOwnerIsGroup(false) +: mObjectID(), mName(), mSLurl(), mOwnerID(), mGroupOwned(false) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_object_im_info.xml"); @@ -100,34 +102,37 @@ BOOL LLFloaterObjectIMInfo::postBuild(void) return true; } -void LLFloaterObjectIMInfo::update(const LLUUID& object_id, const std::string& name, const std::string& slurl, const LLUUID& owner_id, bool owner_is_group) +void LLFloaterObjectIMInfo::update(LLSD& data) { + // Extract appropriate object information from input LLSD + // (Eventually, it might be nice to query server for details + // rather than require caller to pass in the information.) + mObjectID = data["object_id"].asUUID(); + mName = data["name"].asString(); + mOwnerID = data["owner_id"].asUUID(); + mGroupOwned = data["group_owned"].asBoolean(); + mSLurl = data["slurl"].asString(); + // When talking to an old region we won't have a slurl. // The object id isn't really the object id either but we don't use it so who cares. //bool have_slurl = !slurl.empty(); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-04 (RLVa-1.0.0a) | Added: RLVa-0.2.0g - bool have_slurl = (!slurl.empty()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); + bool have_slurl = (!mSLurl.empty()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); // [/RLVa:KB] childSetVisible("Unknown_Slurl",!have_slurl); childSetVisible("Slurl",have_slurl); - childSetText("ObjectName",name); - childSetText("Slurl",slurl); + childSetText("ObjectName",mName); + childSetText("Slurl",mSLurl); childSetText("OwnerName",std::string("")); // bool my_object = (owner_id == gAgentID); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g - bool my_object = (owner_id == gAgentID) || ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(owner_id))); + bool my_object = (mOwnerID == gAgentID) || ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(mOwnerID))); // [/RLVa:KB] childSetEnabled("Mute",!my_object); - mObjectID = object_id; - mObjectName = name; - mSlurl = slurl; - mOwnerID = owner_id; - mOwnerIsGroup = owner_is_group; - - if (gCacheName) gCacheName->get(owner_id,owner_is_group,boost::bind(&LLFloaterObjectIMInfo::nameCallback,this,_1,_2,_3)); + if (gCacheName) gCacheName->get(mOwnerID,mGroupOwned,boost::bind(&LLFloaterObjectIMInfo::nameCallback,this,_1,_2,_3)); } //static @@ -135,17 +140,15 @@ void LLFloaterObjectIMInfo::onClickMap(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - std::ostringstream link; - link << "secondlife://" << self->mSlurl; - class LLMediaCtrl* web = NULL; - LLURLDispatcher::dispatch(link.str(), web, true); + std::string url = "secondlife://" + self->mSLurl; + LLUrlAction::showLocationOnMap(url); } //static void LLFloaterObjectIMInfo::onClickOwner(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - if (self->mOwnerIsGroup) + if (self->mGroupOwned) { LLFloaterGroupInfo::showFromUUID(self->mOwnerID); } @@ -163,7 +166,7 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - LLMute::EType mute_type = (self->mOwnerIsGroup) ? LLMute::GROUP : LLMute::AGENT; + LLMute::EType mute_type = (self->mGroupOwned) ? LLMute::GROUP : LLMute::AGENT; // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g if ( (LLMute::GROUP != mute_type) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(self->mOwnerID)) ) { @@ -171,7 +174,7 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) } // [/RLVa:KB] - LLMute mute(self->mOwnerID, self->mOwnerName, mute_type); + LLMute mute(self->mOwnerID, self->mName, mute_type); LLMuteList::getInstance()->add(mute); LLFloaterMute::showInstance(); self->close(); @@ -180,49 +183,50 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) //static void LLFloaterObjectIMInfo::nameCallback(const LLUUID& id, const std::string& full_name, bool is_group) { - mOwnerName = full_name; + mName = full_name; // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g if ( (!is_group) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(id)) ) { - mOwnerName = RlvStrings::getAnonym(mOwnerName); + mName = RlvStrings::getAnonym(mName); } // [/RLVa:KB] - childSetText("OwnerName", mOwnerName); -} - -//////////////////////////////////////////////////////////////////////////// -// LLObjectIMInfo -void LLObjectIMInfo::show(const LLUUID &object_id, const std::string &name, const std::string &location, const LLUUID &owner_id, bool owner_is_group) -{ - LLFloaterObjectIMInfo* im_info_floater = LLFloaterObjectIMInfo::showInstance(); - im_info_floater->update(object_id,name,location,owner_id,owner_is_group); + childSetText("OwnerName", mName); } //////////////////////////////////////////////////////////////////////////// // LLObjectIMInfoHandler +//moved to llchathistory.cpp in v2 class LLObjectIMInfoHandler : public LLCommandHandler { public: - LLObjectIMInfoHandler() : LLCommandHandler("objectim", true) { } + LLObjectIMInfoHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web); + bool handle(const LLSD& params, const LLSD& query_map,LLMediaCtrl* web) + { + if (params.size() < 1) + { + return false; + } + + LLUUID object_id; + if (!object_id.set(params[0], FALSE)) + { + return false; + } + + LLSD payload; + payload["object_id"] = object_id; + payload["owner_id"] = query_map["owner"]; + payload["name"] = query_map["name"]; + payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]); + payload["group_owned"] = query_map["groupowned"]; + + LLFloaterObjectIMInfo::showInstance()->update(payload); + + return true; + } }; // Creating the object registers with the dispatcher. LLObjectIMInfoHandler gObjectIMHandler; - -// ex. secondlife:///app/objectim/9426adfc-9c17-8765-5f09-fdf19957d003?owner=a112d245-9095-4e9c-ace4-ffa31717f934&groupowned=true&slurl=ahern/123/123/123&name=Object -bool LLObjectIMInfoHandler::handle(const LLSD &tokens, const LLSD &query_map, LLMediaCtrl* web) -{ - LLUUID task_id = tokens[0].asUUID(); - std::string name = query_map["name"].asString(); - std::string slurl = query_map["slurl"].asString(); - LLUUID owner = query_map["owner"].asUUID(); - bool group_owned = query_map.has("groupowned"); - - LLObjectIMInfo::show(task_id,name,slurl,owner,group_owned); - - return true; -} diff --git a/indra/newview/llfloaterparcel.cpp b/indra/newview/llfloaterparcel.cpp index a61f3b912..93afb820f 100644 --- a/indra/newview/llfloaterparcel.cpp +++ b/indra/newview/llfloaterparcel.cpp @@ -49,11 +49,12 @@ LLMap< const LLUUID, LLFloaterParcelInfo* > gPlaceInfoInstances; +//moved to llpanelplaces.cpp in v2 class LLParcelHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLParcelHandler() : LLCommandHandler("parcel", true) { } + LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ed0b07be4..dbbd06f76 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -93,7 +93,7 @@ class LLPreferencesHandler : public LLCommandHandler { public: // requires trusted browser - LLPreferencesHandler() : LLCommandHandler("preferences", true) { } + LLPreferencesHandler() : LLCommandHandler("preferences", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { @@ -469,7 +469,7 @@ void LLFloaterPreference::onBtnOK( void* userdata ) llinfos << "Can't close preferences!" << llendl; } - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } @@ -480,14 +480,14 @@ void LLFloaterPreference::onBtnApply( void* userdata ) if (fp->hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (cur_focus->acceptsTextInput()) + if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } } fp->apply(); - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 83ab8b0e8..d41cbf17a 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -51,9 +51,11 @@ // viewer project includes #include "llagent.h" +#include "llagentui.h" #include "llbutton.h" #include "lltexturectrl.h" #include "llscrolllistctrl.h" +#include "llslurl.h" #include "lldispatcher.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -122,7 +124,9 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) // virtual BOOL LLFloaterReporter::postBuild() { - getChild("abuse_location_edit")->setValue(gAgent.getSLURL()); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + getChild("abuse_location_edit")->setValue(slurl.getSLURLString()); enableControls(TRUE); diff --git a/indra/newview/llfloaterteleporthistory.cpp b/indra/newview/llfloaterteleporthistory.cpp index bdb089e3c..a4ffe91fa 100644 --- a/indra/newview/llfloaterteleporthistory.cpp +++ b/indra/newview/llfloaterteleporthistory.cpp @@ -44,15 +44,17 @@ #include "llappviewer.h" #include "llfloaterteleporthistory.h" #include "llfloaterworldmap.h" +#include "llslurl.h" #include "lltimer.h" #include "lluictrlfactory.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" #include "llviewercontrol.h" #include "llviewerwindow.h" #include "llwindow.h" #include "llweb.h" #include "llsdserialize.h" +#include "llurlaction.h" + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -137,12 +139,12 @@ void LLFloaterTeleportHistory::addPendingEntry(std::string regionName, S16 x, S1 // Set pending position mPendingPosition = llformat("%d, %d, %d", x, y, z); + LLSLURL slurl(regionName, LLVector3(x, y, z)); // prepare simstring for later parsing - mPendingSimString = regionName + llformat("/%d/%d/%d", x, y, z); - mPendingSimString = LLWeb::escapeURL(mPendingSimString); + mPendingSimString = LLWeb::escapeURL(slurl.getLocationString()); // Prepare the SLURL - mPendingSLURL = LLURLDispatcher::buildSLURL(regionName, x, y, z); + mPendingSLURL = slurl.getSLURLString(); } void LLFloaterTeleportHistory::addEntry(std::string parcelName) @@ -332,9 +334,8 @@ void LLFloaterTeleportHistory::onTeleport(void* data) LLFloaterTeleportHistory* self = (LLFloaterTeleportHistory*) data; // build secondlife::/app link from simstring for instant teleport to destination - std::string slapp = "secondlife:///app/teleport/" + self->mPlacesList->getFirstSelected()->getColumn(LIST_SIMSTRING)->getValue().asString(); - LLMediaCtrl* web = NULL; - LLURLDispatcher::dispatch(slapp, web, TRUE); + std::string slapp = "secondlife:///app/teleport/" + self->mPlacesList->getFirstSelected()->getColumn(LIST_SLURL)->getValue().asString(); + LLUrlAction::teleportToLocation(slapp); } // static @@ -344,15 +345,11 @@ void LLFloaterTeleportHistory::onShowOnMap(void* data) // get simstring from selected entry and parse it for its components std::string simString = self->mPlacesList->getFirstSelected()->getColumn(LIST_SIMSTRING)->getValue().asString(); - std::string region = ""; - S32 x = 128; - S32 y = 128; - S32 z = 20; - LLURLSimString::parse(simString, ®ion, &x, &y, &z); + LLSLURL slurl(simString); // point world map at position - gFloaterWorldMap->trackURL(region, x, y, z); + gFloaterWorldMap->trackURL(slurl.getRegion(), slurl.getPosition().mV[VX], slurl.getPosition().mV[VY], slurl.getPosition().mV[VZ]); LLFloaterWorldMap::show(true); } diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index dd6ea2864..65171b007 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -32,12 +32,15 @@ #include "llviewerprecompiledheaders.h" +#include "llhttpclient.h" + #include "llfloaterurlentry.h" #include "llpanellandmedia.h" +#include "llpanelface.h" -// project includes #include "llcombobox.h" +#include "llmimetypes.h" #include "llnotificationsutil.h" #include "llurlhistory.h" #include "lluictrlfactory.h" @@ -83,7 +86,7 @@ public: { // Set empty type to none/none. Empty string is reserved for legacy parcels // which have no mime type set. - std::string resolved_mime_type = ! mime_type.empty() ? mime_type : "none/none"; + std::string resolved_mime_type = ! mime_type.empty() ? mime_type : LLMIMETypes::getDefaultMimeType(); LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mParent.get(); if ( floater_url_entry ) floater_url_entry->headerFetchComplete( status, resolved_mime_type ); @@ -165,6 +168,16 @@ void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_ panel_media->setMediaType(mime_type); panel_media->setMediaURL(mMediaURLEdit->getValue().asString()); } + else + { + LLPanelFace* panel_face = dynamic_cast(mPanelLandMediaHandle.get()); + if(panel_face) + { + panel_face->setMediaType(mime_type); + panel_face->setMediaURL(mMediaURLEdit->getValue().asString()); + } + + } // Decrement the cursor getWindow()->decBusyCount(); getChildView("loading_label")->setVisible( false); @@ -172,26 +185,18 @@ void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_ } // static -LLHandle LLFloaterURLEntry::show(LLHandle parent) +LLHandle LLFloaterURLEntry::show(LLHandle parent, const std::string media_url) { if (!sInstance) { sInstance = new LLFloaterURLEntry(parent); } sInstance->open(); - sInstance->updateFromLandMediaPanel(); + sInstance->addURLToCombobox(media_url); return sInstance->getHandle(); } -void LLFloaterURLEntry::updateFromLandMediaPanel() -{ - LLPanelLandMedia* panel_media = (LLPanelLandMedia*)mPanelLandMediaHandle.get(); - if (panel_media) - { - std::string media_url = panel_media->getMediaURL(); - addURLToCombobox(media_url); - } -} + bool LLFloaterURLEntry::addURLToCombobox(const std::string& media_url) { diff --git a/indra/newview/llfloaterurlentry.h b/indra/newview/llfloaterurlentry.h index 0aeca823b..6dd9c8453 100644 --- a/indra/newview/llfloaterurlentry.h +++ b/indra/newview/llfloaterurlentry.h @@ -44,10 +44,8 @@ class LLFloaterURLEntry : public LLFloater public: // Can only be shown by LLPanelLandMedia, and pushes data back into // that panel via the handle. - static LLHandle show(LLHandle panel_land_media_handle); + static LLHandle show(LLHandle panel_land_media_handle, const std::string media_url); /*virtual*/ BOOL postBuild(); - void updateFromLandMediaPanel(); - void headerFetchComplete(U32 status, const std::string& mime_type); bool addURLToCombobox(const std::string& media_url); diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp new file mode 100644 index 000000000..c05ddd23f --- /dev/null +++ b/indra/newview/llfloaterwebcontent.cpp @@ -0,0 +1,517 @@ +/** + * @file llfloaterwebcontent.cpp + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llcombobox.h" +#include "lliconctrl.h" +#include "lllayoutstack.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "lltextbox.h" +#include "llurlhistory.h" +#include "llviewercontrol.h" +#include "llweb.h" +#include "llwindow.h" +#include "lluictrlfactory.h" + +#include "llfloaterwebcontent.h" + +LLFloaterWebContent::_Params::_Params() +: url("url"), + target("target"), + id("id"), + window_class("window_class", "web_content"), + show_chrome("show_chrome", true), + allow_address_entry("allow_address_entry", true), + preferred_media_size("preferred_media_size"), + trusted_content("trusted_content", false), + show_page_title("show_page_title", true) +{} + +LLFloaterWebContent::LLFloaterWebContent( const Params& params ) +: LLFloater( params.id ), + LLInstanceTracker(params.id()), + mWebBrowser(NULL), + mAddressCombo(NULL), + mSecureLockIcon(NULL), + mStatusBarText(NULL), + mStatusBarProgress(NULL), + mBtnBack(NULL), + mBtnForward(NULL), + mBtnReload(NULL), + mBtnStop(NULL), + mUUID(params.id()), + mShowPageTitle(params.show_page_title), + mKey(params) +{ + mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); + mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); + mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); + mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); + mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); + mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_media_browser.xml"); + mAgeTimer.reset(); +} + +BOOL LLFloaterWebContent::postBuild() +{ + // these are used in a bunch of places so cache them + mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" ); + mAddressCombo = getChild< LLComboBox >( "address" ); + mStatusBarText = getChild< LLTextBox >( "statusbartext" ); + mStatusBarProgress = getChild("statusbarprogress" ); + + mBtnBack = getChildView( "back" ); + mBtnForward = getChildView( "forward" ); + mBtnReload = getChildView( "reload" ); + mBtnStop = getChildView( "stop" ); + + // observe browser events + mWebBrowser->addObserver( this ); + + // these buttons are always enabled + mBtnReload->setEnabled( true ); + getChildView("popexternal")->setEnabled( true ); + + // cache image for secure browsing + mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); + + // initialize the URL history using the system URL History manager + initializeURLHistory(); + + return TRUE; +} + +void LLFloaterWebContent::initializeURLHistory() +{ + // start with an empty list + LLCtrlListInterface* url_list = childGetListInterface("address"); + if (url_list) + { + url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + } + + // Get all of the entries in the "browser" collection + LLSD browser_history = LLURLHistory::getURLHistory("browser"); + LLSD::array_iterator iter_history = browser_history.beginArray(); + LLSD::array_iterator end_history = browser_history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) + url_list->addSimpleElement(url); + } +} + +bool LLFloaterWebContent::matchesKey(const LLSD& key) +{ + Params p(mKey); + Params other_p(key); + if (!other_p.target().empty() && other_p.target() != "_blank") + { + return other_p.target() == p.target(); + } + else + { + return other_p.id() == p.id(); + } +} + +//static +void LLFloaterWebContent::showInstance(const std::string& window_class, Params& p) +{ + p.window_class(window_class); + + LLSD key = p; + + instance_iter it = beginInstances(); + for(;it!=endInstances();++it) + { + if(it->mKey["window_class"].asString() == window_class) + { + if(it->matchesKey(key)) + { + it->mKey = key; + it->setKey(p.id()); + it->mAgeTimer.reset(); + it->open(); + return; + } + } + } + LLFloaterWebContent* old_inst = getInstance(p.id()); + if(old_inst) + { + llwarns << "Replacing unexpected duplicate floater: " << p.id() << llendl; + old_inst->mKey = key; + old_inst->mAgeTimer.reset(); + old_inst->open(); + } + assert(!old_inst); + + if(!old_inst) + LLFloaterWebContent::create(p); +} + +//static +LLFloater* LLFloaterWebContent::create( Params p) +{ + preCreate(p); + return new LLFloaterWebContent(p); +} + +//static +void LLFloaterWebContent::closeRequest(const std::string &uuid) +{ + LLFloaterWebContent* floaterp = instance_tracker_t::getInstance(uuid); + if (floaterp) + { + floaterp->close(); + } +} + +//static +void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height) +{ + LLFloaterWebContent* floaterp = instance_tracker_t::getInstance(uuid); + if (floaterp) + { + floaterp->geometryChanged(x, y, width, height); + } +} + +void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) +{ + // Make sure the layout of the browser control is updated, so this calculation is correct. + getChild("stack1")->updateLayout(); + + // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + + // Adjust width and height for the size of the chrome on the web Browser window. + LLRect browser_rect; + mWebBrowser->localRectToOtherView(mWebBrowser->getLocalRect(), &browser_rect, this); + + S32 requested_browser_bottom = window_size.mY - (y + height); + LLRect geom; + geom.setOriginAndSize(x - browser_rect.mLeft, + requested_browser_bottom - browser_rect.mBottom, + width + getRect().getWidth() - browser_rect.getWidth(), + height + getRect().getHeight() - browser_rect.getHeight()); + + lldebugs << "geometry change: " << geom << llendl; + + LLRect new_rect; + getParent()->screenRectToLocal(geom, &new_rect); + setShape(new_rect); +} + +// static +void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) +{ + lldebugs << "url = " << p.url() << ", target = " << p.target() << ", uuid = " << p.id() << llendl; + + if (!p.id.isProvided()) + { + p.id = LLUUID::generateNewID().asString(); + } + + if(p.target().empty() || p.target() == "_blank") + { + p.target = p.id(); + } + + S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit"); + if(browser_window_limit != 0) + { + // showInstance will open a new window. Figure out how many web browsers are already open, + // and close the least recently opened one if this will put us over the limit. + + std::vector instances; + instances.reserve(instanceCount()); + instance_iter it = beginInstances(); + for(;it!=endInstances();++it) + { + if(it->mKey["window_class"].asString() == p.window_class.getValue()) + instances.push_back(&*it); + } + + std::sort(instances.begin(), instances.end(), CompareAgeDescending()); + + //LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList(p.window_class); + lldebugs << "total instance count is " << instances.size() << llendl; + + for(std::vector::const_iterator iter = instances.begin(); iter != instances.end(); iter++) + { + lldebugs << " " << (*iter)->mKey["target"] << llendl; + } + + if(instances.size() >= (size_t)browser_window_limit) + { + // Destroy the least recently opened instance + (*instances.begin())->close(); + } + } +} + +void LLFloaterWebContent::open_media(const Params& p) +{ + // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. + LLViewerMedia::proxyWindowOpened(p.target(), p.id()); + mWebBrowser->setHomePageUrl(p.url, "text/html"); + mWebBrowser->setTarget(p.target); + mWebBrowser->navigateTo(p.url, "text/html"); + + set_current_url(p.url); + + getChild("status_bar")->setVisible(p.show_chrome); + getChild("nav_controls")->setVisible(p.show_chrome); + bool address_entry_enabled = p.allow_address_entry && !p.trusted_content; + getChildView("address")->setEnabled(address_entry_enabled); + getChildView("popexternal")->setEnabled(address_entry_enabled); + + if (!address_entry_enabled) + { + mWebBrowser->setFocus(TRUE); + } + + if (!p.show_chrome) + { + setResizeLimits(100, 100); + } + + if (!p.preferred_media_size().isEmpty()) + { + getChild("stack1")->updateLayout(); + LLRect browser_rect = mWebBrowser->calcScreenRect(); + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + + geometryChanged(browser_rect.mLeft, window_size.mY - browser_rect.mTop, p.preferred_media_size().getWidth(), p.preferred_media_size().getHeight()); + } + +} + +void LLFloaterWebContent::onOpen() +{ + Params params(mKey); + + if (!params.validateBlock()) + { + close(); + return; + } + + mWebBrowser->setTrustedContent(params.trusted_content); + + // tell the browser instance to load the specified URL + open_media(params); +} + +//virtual +void LLFloaterWebContent::onClose(bool app_quitting) +{ + LLViewerMedia::proxyWindowClosed(mUUID); + destroy(); +} + +// virtual +void LLFloaterWebContent::draw() +{ + // this is asynchronous so we need to keep checking + mBtnBack->setEnabled( mWebBrowser->canNavigateBack() ); + mBtnForward->setEnabled( mWebBrowser->canNavigateForward() ); + + LLFloater::draw(); +} + +// virtual +void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + if(event == MEDIA_EVENT_LOCATION_CHANGED) + { + const std::string url = self->getLocation(); + + if ( url.length() ) + mStatusBarText->setText( url ); + + set_current_url( url ); + } + else if(event == MEDIA_EVENT_NAVIGATE_BEGIN) + { + // flags are sent with this event + mBtnBack->setEnabled( self->getHistoryBackAvailable() ); + mBtnForward->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + mBtnReload->setVisible( false ); + mBtnStop->setVisible( true ); + + // turn "on" progress bar now we're about to start loading + mStatusBarProgress->setVisible( true ); + } + else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) + { + // flags are sent with this event + mBtnBack->setEnabled( self->getHistoryBackAvailable() ); + mBtnForward->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + mBtnReload->setVisible( true ); + mBtnStop->setVisible( false ); + + // turn "off" progress bar now we're loaded + mStatusBarProgress->setVisible( false ); + + // we populate the status bar with URLs as they change so clear it now we're done + const std::string end_str = ""; + mStatusBarText->setText( end_str ); + + // decide if secure browsing icon should be displayed + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + mSecureLockIcon->setVisible(true); + } + else + { + mSecureLockIcon->setVisible(false); + } + } + else if(event == MEDIA_EVENT_CLOSE_REQUEST) + { + // The browser instance wants its window closed. + close(); + } + else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) + { + geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); + } + else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED ) + { + const std::string text = self->getStatusText(); + if ( text.length() ) + mStatusBarText->setText( text ); + } + else if(event == MEDIA_EVENT_PROGRESS_UPDATED ) + { + int percent = (int)self->getProgressPercent(); + mStatusBarProgress->setValue( percent ); + } + else if(event == MEDIA_EVENT_NAME_CHANGED ) + { + std::string page_title = self->getMediaName(); + // simulate browser behavior - title is empty, use the current URL + if (mShowPageTitle) + { + if ( page_title.length() > 0 ) + setTitle( page_title ); + else + setTitle( mCurrentURL ); + } + } + else if(event == MEDIA_EVENT_LINK_HOVERED ) + { + const std::string link = self->getHoverLink(); + mStatusBarText->setText( link ); + } +} + +void LLFloaterWebContent::set_current_url(const std::string& url) +{ + mCurrentURL = url; + + // serialize url history into the system URL History manager + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); + + mAddressCombo->remove( mCurrentURL ); + mAddressCombo->add( mCurrentURL ); + mAddressCombo->selectByValue( mCurrentURL ); +} + +void LLFloaterWebContent::onClickForward() +{ + mWebBrowser->navigateForward(); +} + +void LLFloaterWebContent::onClickBack() +{ + mWebBrowser->navigateBack(); +} + +void LLFloaterWebContent::onClickReload() +{ + + if( mWebBrowser->getMediaPlugin() ) + { + bool ignore_cache = true; + mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache ); + } + else + { + mWebBrowser->navigateTo(mCurrentURL); + } +} + +void LLFloaterWebContent::onClickStop() +{ + if( mWebBrowser->getMediaPlugin() ) + mWebBrowser->getMediaPlugin()->browse_stop(); + + // still should happen when we catch the navigate complete event + // but sometimes (don't know why) that event isn't sent from Qt + // and we ghetto a point where the stop button stays active. + mBtnReload->setVisible( true ); + mBtnStop->setVisible( false ); +} + +void LLFloaterWebContent::onEnterAddress() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + mWebBrowser->navigateTo( url, "text/html"); + }; +} + +void LLFloaterWebContent::onPopExternal() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + LLWeb::loadURLExternal( url ); + }; +} diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h new file mode 100644 index 000000000..00d8c9532 --- /dev/null +++ b/indra/newview/llfloaterwebcontent.h @@ -0,0 +1,125 @@ +/** + * @file llfloaterwebcontent.h + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERWEBCONTENT_H +#define LL_LLFLOATERWEBCONTENT_H + +#include "llfloater.h" +#include "llmediactrl.h" +#include "llsdparam.h" + +class LLMediaCtrl; +class LLComboBox; +class LLTextBox; +class LLProgressBar; +class LLIconCtrl; + +class LLFloaterWebContent : + public LLFloater, + public LLViewerMediaObserver, + public LLInstanceTracker +{ +public: + typedef LLInstanceTracker instance_tracker_t; + LOG_CLASS(LLFloaterWebContent); + + struct _Params : public LLInitParam::Block<_Params> + { + Optional url, + target, + window_class, + id; + Optional show_chrome, + allow_address_entry, + trusted_content, + show_page_title; + Optional preferred_media_size; + + _Params(); + }; + + typedef LLSDParamAdapter<_Params> Params; + + LLFloaterWebContent(const Params& params); + + void initializeURLHistory(); + + static LLFloater* create(Params); + + static void showInstance(const std::string& window_class, Params& p); + static void closeRequest(const std::string &uuid); + static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height); + void geometryChanged(S32 x, S32 y, S32 width, S32 height); + + /* virtual */ BOOL postBuild(); + /* virtual */ void onOpen(); + /* virtual */ bool matchesKey(const LLSD& key); + /* virtual */ void onClose(bool app_quitting); + /* virtual */ void draw(); + +protected: + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + void onClickBack(); + void onClickForward(); + void onClickReload(); + void onClickStop(); + void onEnterAddress(); + void onPopExternal(); + + static void preCreate(Params& p); + void open_media(const Params& ); + void set_current_url(const std::string& url); + + LLMediaCtrl* mWebBrowser; + LLComboBox* mAddressCombo; + LLIconCtrl* mSecureLockIcon; + LLTextBox* mStatusBarText; + LLProgressBar* mStatusBarProgress; + + LLView* mBtnBack; + LLView* mBtnForward; + LLView* mBtnReload; + LLView* mBtnStop; + + std::string mCurrentURL; + std::string mUUID; + bool mShowPageTitle; + + LLSD mKey; + LLTimer mAgeTimer; + + struct CompareAgeDescending + { + bool operator()(const LLFloaterWebContent* const& lhs, const LLFloaterWebContent* const& rhs) + { + return lhs->mAgeTimer.getElapsedTimeF64() > lhs->mAgeTimer.getElapsedTimeF64(); + } + }; +}; + +#endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index e3532cfe8..0f6eb2cdf 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -75,6 +75,7 @@ #include "llmapimagetype.h" #include "llweb.h" #include "llwindow.h" // copyTextToClipboard() +#include "llslurl.h" // [RLVa:KB] @@ -728,7 +729,7 @@ void LLFloaterWorldMap::updateLocation() // Figure out where user is // Set the current SLURL - mSLURL = LLURLDispatcher::buildSLURL(agent_sim_name, x, y, z); + mSLURL = LLSLURL(agent_sim_name, LLVector3(x, y, z)).getSLURLString(); // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) @@ -774,7 +775,7 @@ void LLFloaterWorldMap::updateLocation() S32 x = llround( (F32)fmod( (F32)coord_pos[VX], (F32)REGION_WIDTH_METERS ) ); S32 y = llround( (F32)fmod( (F32)coord_pos[VY], (F32)REGION_WIDTH_METERS ) ); S32 z = llround( (F32)coord_pos[VZ] ); - mSLURL = LLURLDispatcher::buildSLURL(sim_name, x, y, z); + mSLURL = LLSLURL(sim_name, LLVector3(x, y, z)).getSLURLString(); } else { // Empty SLURL will disable the "Copy SLURL to clipboard" button diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 447087276..40f25011d 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -36,8 +36,8 @@ // viewer includes #include "llpanellogin.h" // save_password_to_disk() +#include "llslurl.h" #include "llstartup.h" // getStartupState() -#include "llurlsimstring.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo @@ -79,17 +79,15 @@ void LLLoginHandler::parse(const LLSD& queryMap) if (startLocation == "specify") { - LLURLSimString::setString(queryMap["region"].asString()); + LLStartUp::setStartSLURL(queryMap["region"].asString()); } else if (startLocation == "home") { - gSavedSettings.setBOOL("LoginLastLocation", FALSE); - LLURLSimString::setString(LLStringUtil::null); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); } else if (startLocation == "last") { - gSavedSettings.setBOOL("LoginLastLocation", TRUE); - LLURLSimString::setString(LLStringUtil::null); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); } } diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index 0844b80c7..d36ceaf3c 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -39,7 +39,7 @@ class LLLoginHandler : public LLCommandHandler { public: // allow from external browsers - LLLoginHandler() : LLCommandHandler("login", false) { } + LLLoginHandler() : LLCommandHandler("login", UNTRUSTED_ALLOW) { } /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web); // Fill in our internal fields from a SLURL like diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 17cf01b2f..354c8d7d9 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -40,68 +40,98 @@ #include "llfloaterworldmap.h" #include "lluictrlfactory.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" #include "llviewborder.h" #include "llviewercontrol.h" #include "llviewermedia.h" +#include "llviewertexture.h" #include "llviewerwindow.h" -#include "llnotificationsutil.h" #include "llweb.h" #include "llrender.h" #include "llpluginclassmedia.h" +#include "llslurl.h" +#include "lluictrlfactory.h" // LLRegisterWidget +#include "llkeyboard.h" +#include "llviewermenu.h" // linden library includes #include "llfocusmgr.h" +#include "llsdutil.h" +#include "lltextbox.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llnotifications.h" +#include "lllineeditor.h" extern BOOL gRestoreGL; -// Setting the mozilla buffer width to 2048 exactly doesn't work, since it pads its rowbytes a bit, pushing the texture width over 2048. -// 2000 should give enough headroom for any amount of padding it cares to add. -const S32 MAX_DIMENSION = 2000; -const S32 MAX_TEXTURE_DIMENSION = 2048; - static LLRegisterWidget r("web_browser"); -LLMediaCtrl::LLMediaCtrl( const std::string& name, const LLRect& rect ) : - LLUICtrl( name, rect, FALSE), +LLMediaCtrl::Params::Params() +: start_url("start_url"), + border_visible("border_visible", true), + decouple_texture_size("decouple_texture_size", false), + texture_width("texture_width", 1024), + texture_height("texture_height", 1024), + caret_color("caret_color"), + initial_mime_type("initial_mime_type"), + error_page_url("error_page_url"), + media_id("media_id"), + trusted_content("trusted_content", false), + focus_on_click("focus_on_click", true) +{ +} + +LLMediaCtrl::LLMediaCtrl( const Params& p) : + LLPanel( p.name, p.rect, FALSE), LLInstanceTracker(LLUUID::generateNewID()), mTextureDepthBytes( 4 ), - mWebBrowserImage( 0 ), mBorder(NULL), mFrequentUpdates( true ), mForceUpdate( false ), - mOpenLinksInExternalBrowser( false ), - mOpenLinksInInternalBrowser( false ), - mTrusted( false ), mHomePageUrl( "" ), - mIgnoreUIScale( true ), mAlwaysRefresh( false ), - mExternalUrl( "" ), mMediaSource( 0 ), - mTakeFocusOnClick( true ), + mTakeFocusOnClick( p.focus_on_click ), mCurrentNavUrl( "about:blank" ), - mLastSetCursor( UI_CURSOR_ARROW ), mStretchToFill( true ), mMaintainAspectRatio ( true ), mDecoupleTextureSize ( false ), mTextureWidth ( 1024 ), mTextureHeight ( 1024 ), - mHideLoading (false) + mClearCache(false), + mHomePageMimeType(p.initial_mime_type), + mErrorPageURL(p.error_page_url), + mTrusted(p.trusted_content), + mHoverTextChanged(false) { + { + LLColor4 color = p.caret_color().get(); + setCaretColor( (unsigned int)color.mV[0], (unsigned int)color.mV[1], (unsigned int)color.mV[2] ); + } + + setHomePageUrl(p.start_url, p.initial_mime_type); + + setBorderVisible(p.border_visible); + + setDecoupleTextureSize(p.decouple_texture_size); + + setTextureSize(p.texture_width, p.texture_height); + if(!getDecoupleTextureSize()) { - S32 screen_width = mIgnoreUIScale ? - llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]) : getRect().getWidth(); - S32 screen_height = mIgnoreUIScale ? - llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]) : getRect().getHeight(); - + S32 screen_width = llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]); + setTextureSize(screen_width, screen_height); } + + mMediaTextureID = getKey(); + // We don't need to create the media source up front anymore unless we have a non-empty home URL to navigate to. - if(!mHomePageUrl.empty()) + /*if(!mHomePageUrl.empty()) { navigateHome(); - } + }*/ LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 ); @@ -109,18 +139,13 @@ LLMediaCtrl::LLMediaCtrl( const std::string& name, const LLRect& rect ) : addChild( mBorder ); } -//////////////////////////////////////////////////////////////////////////////// -// note: this is now a singleton and destruction happens via initClass() now LLMediaCtrl::~LLMediaCtrl() { - if (mMediaSource) { mMediaSource->remObserver( this ); mMediaSource = NULL; } - - mWebBrowserImage = NULL; } //////////////////////////////////////////////////////////////////////////////// @@ -140,39 +165,24 @@ void LLMediaCtrl::setTakeFocusOnClick( bool take_focus ) mTakeFocusOnClick = take_focus; } - -//////////////////////////////////////////////////////////////////////////////// -// set flag that forces the embedded browser to open links in the external system browser -void LLMediaCtrl::setOpenInExternalBrowser( bool valIn ) -{ - mOpenLinksInExternalBrowser = valIn; -}; - -//////////////////////////////////////////////////////////////////////////////// -// set flag that forces the embedded browser to open links in the internal browser floater -void LLMediaCtrl::setOpenInInternalBrowser( bool valIn ) -{ - mOpenLinksInInternalBrowser = valIn; -}; - -//////////////////////////////////////////////////////////////////////////////// -void LLMediaCtrl::setTrusted( bool valIn ) -{ - mTrusted = valIn; -} - //////////////////////////////////////////////////////////////////////////////// // BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleHover(x, y, mask)) return TRUE; + if (LLPanel::handleHover(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) { mMediaSource->mouseMove(x, y, mask); - - gViewerWindow->setCursor(mLastSetCursor); + gViewerWindow->setCursor(mMediaSource->getLastSetCursor()); + } + + // TODO: Is this the right way to handle hover text changes driven by the plugin? + if(mHoverTextChanged) + { + mHoverTextChanged = false; + //handleToolTip(x, y, mask); } return TRUE; @@ -182,9 +192,36 @@ BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) { - if (LLUICtrl::handleScrollWheel(x, y, clicks)) return TRUE; + if (LLPanel::handleScrollWheel(x, y, clicks)) return TRUE; if (mMediaSource && mMediaSource->hasMedia()) - mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, MASK_NONE); + mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, gKeyboard->currentMask(TRUE)); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL LLMediaCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) +{ + std::string hover_text; + + if (mMediaSource && mMediaSource->hasMedia()) + hover_text = mMediaSource->getMediaPlugin()->getHoverText(); + + if(hover_text.empty()) + { + return FALSE; + } + else + { + msg = hover_text; + + S32 screen_x, screen_y; + + localPointToScreen(x, y, &screen_x, &screen_y); + LLRect sticky_rect_screen; + sticky_rect_screen.setCenterAndSize(screen_x, screen_y, 20, 20); + } return TRUE; } @@ -193,20 +230,12 @@ BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) // BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleMouseUp(x, y, mask)) return TRUE; + if (LLPanel::handleMouseUp(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) { mMediaSource->mouseUp(x, y, mask); - - /*// *HACK: LLMediaImplLLMozLib automatically takes focus on mouseup, - // in addition to the onFocusReceived() call below. Undo this. JC - if (!mTakeFocusOnClick) - { - mMediaSource->focus(false); - gViewerWindow->focusClient(); - }*/ } gFocusMgr.setMouseCapture( NULL ); @@ -218,7 +247,7 @@ BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleMouseDown(x, y, mask)) return TRUE; + if (LLPanel::handleMouseDown(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -238,7 +267,7 @@ BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) { - /*if (LLPanel::handleRightMouseUp(x, y, mask)) return TRUE; + if (LLPanel::handleRightMouseUp(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -255,7 +284,7 @@ BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) } gFocusMgr.setMouseCapture( NULL ); - */ + return TRUE; } @@ -263,7 +292,7 @@ BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleRightMouseDown(x, y, mask)) return TRUE; + if (LLPanel::handleRightMouseDown(x, y, mask)) return TRUE; S32 media_x = x, media_y = y; convertInputCoords(media_x, media_y); @@ -285,7 +314,7 @@ BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleDoubleClick( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleDoubleClick(x, y, mask)) return TRUE; + if (LLPanel::handleDoubleClick(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -313,7 +342,7 @@ void LLMediaCtrl::onFocusReceived() LLEditMenuHandler::gEditMenuHandler = mMediaSource; } - LLUICtrl::onFocusReceived(); + LLPanel::onFocusReceived(); } //////////////////////////////////////////////////////////////////////////////// @@ -333,15 +362,14 @@ void LLMediaCtrl::onFocusLost() gViewerWindow->focusClient(); - LLUICtrl::onFocusLost(); + LLPanel::onFocusLost(); } //////////////////////////////////////////////////////////////////////////////// // - BOOL LLMediaCtrl::postBuild () { - //setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); + setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); return true; } @@ -350,43 +378,20 @@ void LLMediaCtrl::onOpenWebInspector() if (mMediaSource && mMediaSource->hasMedia()) mMediaSource->getMediaPlugin()->showWebInspector( true ); } + //////////////////////////////////////////////////////////////////////////////// // BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask ) { BOOL result = FALSE; - // FIXME: THIS IS SO WRONG. - // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it... - if (mMediaSource) { - if( MASK_CONTROL & mask ) - { - if( 'C' == key ) - { - mMediaSource->copy(); - result = TRUE; - } - else - if( 'V' == key ) - { - mMediaSource->paste(); - result = TRUE; - } - else - if( 'X' == key ) - { - mMediaSource->cut(); - result = TRUE; - } - } - - if(!result) - { - result = mMediaSource->handleKeyHere(key, mask); - } + result = mMediaSource->handleKeyHere(key, mask); } + + if ( ! result ) + result = LLPanel::handleKeyHere(key, mask); return result; } @@ -400,14 +405,10 @@ void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility ) { mMediaSource->setVisible( new_visibility ); } - LLUICtrl::handleVisibilityChange( new_visibility ); - //Hack due to not being derived from LLPanel yet - LLMediaCtrl::onVisibilityChange(LLSD(new_visibility)); } //////////////////////////////////////////////////////////////////////////////// // - BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) { BOOL result = FALSE; @@ -420,6 +421,9 @@ BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) result = mMediaSource->handleUnicodeCharHere(uni_char); } + if ( ! result ) + result = LLPanel::handleUnicodeCharHere(uni_char); + return result; } @@ -444,8 +448,8 @@ void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { if(!getDecoupleTextureSize()) { - S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::getScaleFactor().mV[VX]) : width; - S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::getScaleFactor().mV[VY]) : height; + S32 screen_width = llround((F32)width * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llround((F32)height * LLUI::getScaleFactor().mV[VY]); // when floater is minimized, these sizes are negative if ( screen_height > 0 && screen_width > 0 ) @@ -497,6 +501,21 @@ bool LLMediaCtrl::canNavigateForward() return false; } +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::clearCache() +{ + if(mMediaSource) + { + mMediaSource->clearCache(); + } + else + { + mClearCache = true; + } + +} + //////////////////////////////////////////////////////////////////////////////// // void LLMediaCtrl::set404RedirectUrl( std::string redirect_url ) @@ -541,31 +560,18 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type) void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) { std::string language = LLUI::getLanguage(); - std::string delim = gDirUtilp->getDirDelimiter(); - std::string filename; - - filename += subdir; - filename += delim; - filename += filename_in; + std::string filename(gDirUtilp->add(subdir, filename_in)); std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); - if (! gDirUtilp->fileExists(expanded_filename)) + if (expanded_filename.empty() && language != "en-us") { - if (language != "en-us") - { - expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); - if (! gDirUtilp->fileExists(expanded_filename)) - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } - } - else - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } + expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); + } + if(expanded_filename.empty()) + { + llwarns << "File " << filename << "not found" << llendl; + return; } if (ensureMediaSourceExists()) { @@ -589,15 +595,34 @@ void LLMediaCtrl::navigateHome() //////////////////////////////////////////////////////////////////////////////// // -void LLMediaCtrl::setHomePageUrl( const std::string urlIn ) +void LLMediaCtrl::setHomePageUrl( const std::string& urlIn, const std::string& mime_type ) { mHomePageUrl = urlIn; if (mMediaSource) { - mMediaSource->setHomeURL(mHomePageUrl); + mMediaSource->setHomeURL(mHomePageUrl, mime_type); } } +void LLMediaCtrl::setTarget(const std::string& target) +{ + mTarget = target; + if (mMediaSource) + { + mMediaSource->setTarget(mTarget); + } +} + +void LLMediaCtrl::setErrorPageURL(const std::string& url) +{ + mErrorPageURL = url; +} + +const std::string& LLMediaCtrl::getErrorPageURL() +{ + return mErrorPageURL; +} + //////////////////////////////////////////////////////////////////////////////// // bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue) @@ -616,7 +641,6 @@ void LLMediaCtrl::setTextureSize(S32 width, S32 height) if(mMediaSource) { mMediaSource->setSize(mTextureWidth, mTextureHeight); - mWebBrowserImage->resize( mTextureWidth, mTextureHeight ); mForceUpdate = true; } } @@ -634,14 +658,24 @@ bool LLMediaCtrl::ensureMediaSourceExists() { if(mMediaSource.isNull()) { - mMediaSource = LLViewerMedia::newMediaImpl(mHomePageUrl, LLUUID::null, mTextureWidth, mTextureWidth, false, false, "text/html"); + // If we don't already have a media source, try to create one. + mMediaSource = LLViewerMedia::newMediaImpl(mMediaTextureID, mTextureWidth, mTextureHeight); if ( mMediaSource ) { - // create a new texture (based on LLDynamic texture) that will be used to display the output - mWebBrowserImage = new LLWebBrowserTexture( mTextureWidth, mTextureWidth, this, mMediaSource ); - mMediaSource->setHomeURL(mHomePageUrl); + mMediaSource->setUsedInUI(true); + mMediaSource->setHomeURL(mHomePageUrl, mHomePageMimeType); + mMediaSource->setTarget(mTarget); mMediaSource->setVisible( getVisible() ); mMediaSource->addObserver( this ); + mMediaSource->setBackgroundColor( getBackgroundColor() ); + mMediaSource->setTrustedBrowser(mTrusted); + mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); + + if(mClearCache) + { + mMediaSource->clearCache(); + mClearCache = false; + } } else { @@ -658,7 +692,6 @@ bool LLMediaCtrl::ensureMediaSourceExists() void LLMediaCtrl::unloadMediaSource() { mMediaSource = NULL; - mWebBrowserImage = NULL; //release the dynamic texture too. } //////////////////////////////////////////////////////////////////////////////// @@ -672,15 +705,13 @@ LLPluginClassMedia* LLMediaCtrl::getMediaPlugin() // void LLMediaCtrl::draw() { - if ( ! mWebBrowserImage || mWebBrowserImage->getNeedsUpdate()) - return; if ( gRestoreGL == 1 ) { LLRect r = getRect(); reshape( r.getWidth(), r.getHeight(), FALSE ); return; - }; + } // NOTE: optimization needed here - probably only need to do this once // unless tearoffs change the parent which they probably do. @@ -701,7 +732,7 @@ void LLMediaCtrl::draw() bool draw_media = false; LLPluginClassMedia* media_plugin = NULL; - LLWebBrowserTexture* media_texture = mWebBrowserImage; + LLViewerMediaTexture* media_texture = NULL; if(mMediaSource && mMediaSource->hasMedia()) { @@ -709,29 +740,22 @@ void LLMediaCtrl::draw() if(media_plugin && (media_plugin->textureValid())) { - media_texture = mWebBrowserImage; + media_texture = LLViewerTextureManager::findMediaTexture(mMediaTextureID); if(media_texture) { draw_media = true; } } } + + bool background_visible = isBackgroundVisible(); + bool background_opaque = isBackgroundOpaque(); + if(draw_media) { gGL.pushUIMatrix(); { - /*if (mIgnoreUIScale) - { - gGL.pushUIMatrix(); - gGL.loadUIIdentity(); - gGL.pushMatrix(); - gGL.loadIdentity(); - // font system stores true screen origin, need to scale this by UI scale factor - // to get render origin for this view (with unit scale) - gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::getScaleFactor().mV[VX]), - floorf(LLFontGL::sCurOrigin.mY * LLUI::getScaleFactor().mV[VY]), - LLFontGL::sCurDepth); - }*/ + mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); // scale texture to fit the space using texture coords gGL.getTexUnit(0)->bind(media_texture); @@ -822,20 +846,26 @@ void LLMediaCtrl::draw() } gGL.end(); gGL.setSceneBlendType(LLRender::BT_ALPHA); - /*if (mIgnoreUIScale) - { - gGL.popUIMatrix(); - gGL.popMatrix(); - }*/ } gGL.popUIMatrix(); + } + else + { + // Setting these will make LLPanel::draw draw the opaque background color. + setBackgroundVisible(true); + setBackgroundOpaque(true); + } + // highlight if keyboard focus here. (TODO: this needs some work) if ( mBorder && mBorder->getVisible() ) mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); - - LLUICtrl::draw(); + LLPanel::draw(); + + // Restore the previous values + setBackgroundVisible(background_visible); + setBackgroundOpaque(background_opaque); } //////////////////////////////////////////////////////////////////////////////// @@ -849,31 +879,17 @@ void LLMediaCtrl::convertInputCoords(S32& x, S32& y) coords_opengl = mMediaSource->getMediaPlugin()->getTextureCoordsOpenGL(); } - x = mIgnoreUIScale ? llround((F32)x * LLUI::getScaleFactor().mV[VX]) : x; + x = llround((F32)x * LLUI::getScaleFactor().mV[VX]); if ( ! coords_opengl ) { - y = mIgnoreUIScale ? llround((F32)(y) * LLUI::getScaleFactor().mV[VY]) : y; + y = llround((F32)(y) * LLUI::getScaleFactor().mV[VY]); } else { - y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]) : getRect().getHeight() - y; + y = llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]); }; } -//////////////////////////////////////////////////////////////////////////////// -// static -bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD& response ) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - if ( 0 == option ) - { - // open in external browser because we don't support - // creation of our own secondary browser windows - LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() ); - } - return false; -} - //////////////////////////////////////////////////////////////////////////////// // inherited from LLViewerMediaObserver //virtual @@ -903,28 +919,14 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_CURSOR_CHANGED: { - LL_INFOS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; - - std::string cursor = self->getCursorName(); - - if(cursor == "arrow") - mLastSetCursor = UI_CURSOR_ARROW; - else if(cursor == "ibeam") - mLastSetCursor = UI_CURSOR_IBEAM; - else if(cursor == "splith") - mLastSetCursor = UI_CURSOR_SIZEWE; - else if(cursor == "splitv") - mLastSetCursor = UI_CURSOR_SIZENS; - else if(cursor == "hand") - mLastSetCursor = UI_CURSOR_HAND; - else // for anything else, default to the arrow - mLastSetCursor = UI_CURSOR_ARROW; - }; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; + } break; case MEDIA_EVENT_NAVIGATE_BEGIN: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN, url is " << self->getNavigateURI() << LL_ENDL; + hideNotification(); }; break; @@ -959,20 +961,39 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE" << LL_ENDL; + if ( mErrorPageURL.length() > 0 ) + { + navigateTo(mErrorPageURL, "text/html"); + }; }; break; case MEDIA_EVENT_CLICK_LINK_HREF: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; - onClickLinkHref(self); + // retrieve the event parameters + std::string url = self->getClickURL(); + std::string target = self->getClickTarget(); + std::string uuid = self->getClickUUID(); + + LLNotification::Params notify_params("PopupAttempt"); + notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", mMediaTextureID); + notify_params.functor(boost::bind(&LLMediaCtrl::onPopup, this, _1, _2)); + + if (mTrusted) + { + LLNotifications::instance().forceResponse(notify_params, 0); + } + else + { + LLNotifications::instance().add(notify_params); + } + break; }; - break; case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL; - onClickLinkNoFollow(self); }; break; @@ -997,30 +1018,42 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_CLOSE_REQUEST: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLOSE_REQUEST" << LL_ENDL; - }; + } break; case MEDIA_EVENT_PICK_FILE_REQUEST: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PICK_FILE_REQUEST" << LL_ENDL; - }; + } break; case MEDIA_EVENT_GEOMETRY_CHANGE: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE" << LL_ENDL; - }; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL; + } break; case MEDIA_EVENT_AUTH_REQUEST: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_AUTH_REQUEST" << LL_ENDL; + LLNotification::Params auth_request_params("AuthRequest"); + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( self->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = self->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mMediaTextureID); + auth_request_params.functor(boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2)); + LLNotifications::instance().add(auth_request_params); }; break; - + case MEDIA_EVENT_LINK_HOVERED: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED" << LL_ENDL; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL; + mHoverTextChanged = true; }; break; @@ -1042,65 +1075,28 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) //////////////////////////////////////////////////////////////////////////////// // -void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self ) +std::string LLMediaCtrl::getCurrentNavUrl() { - // retrieve the event parameters - std::string target = self->getClickTarget(); - std::string url = self->getClickURL(); - - // if there is a value for the target - if ( !target.empty() ) - { - if ( target == "_external" ) - { - mExternalUrl = url; - LLSD payload; - payload["external_url"] = mExternalUrl; - LLNotifications::instance().add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget); - return; - } - } - - const std::string protocol1( "http://" ); - const std::string protocol2( "https://" ); - if( mOpenLinksInExternalBrowser ) - { - if ( !url.empty() ) - { - if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || - LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) - { - LLWeb::loadURLExternal( url ); - } - } - } - else - if( mOpenLinksInInternalBrowser ) - { - if ( !url.empty() ) - { - if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || - LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) - { - // If we spawn a new LLFloaterHTML, assume we want it to - // follow this LLMediaCtrl's trust for whether or - // not to open secondlife:///app/ links. JC. -// const bool open_links_externally = false; -// LLFloaterHtml::getInstance()->show( -// event_in.mStringPayload, -// "Second Life Browser", -// open_links_externally, -// mTrusted); - } - } - } + return mCurrentNavUrl; } -//////////////////////////////////////////////////////////////////////////////// -// -void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self ) +bool LLMediaCtrl::onPopup(const LLSD& notification, const LLSD& response) { - std::string url = self->getClickURL(); + if (response["open"]) + { + LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]); + } + else + { + // Make sure the opening instance knows its window open request was denied, so it can clean things up. + LLViewerMedia::proxyWindowClosed(notification["payload"]["uuid"]); + } + return FALSE; +} + +void LLMediaCtrl::showNotification(LLNotificationPtr notify) +{ +/* std::string url = self->getClickURL(); if (LLURLDispatcher::isSLURLCommand(url) && !mTrusted) { @@ -1109,241 +1105,24 @@ void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self ) return; } - LLURLDispatcher::dispatch(url, this, mTrusted); + LLURLDispatcher::dispatch(url, this, mTrusted);*/ + LLNotifications::instance().add(notify); } -//////////////////////////////////////////////////////////////////////////////// -// -LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ) : - LLViewerDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ), - mNeedsUpdate( true ), - mNeedsResize( false ), - mTextureCoordsOpenGL( true ), - mWebBrowserCtrl( browserCtrl ), - mMediaSource(media_source) -{ - mElapsedTime.start(); - resize( width, height ); +void LLMediaCtrl::hideNotification() +{ } -//////////////////////////////////////////////////////////////////////////////// -// -LLWebBrowserTexture::~LLWebBrowserTexture() +void LLMediaCtrl::setTrustedContent(bool trusted) { - mElapsedTime.stop(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLWebBrowserTexture::needsRender() -{ - bool texture_dirty = false; - - if ( mWebBrowserCtrl->getFrequentUpdates() || - mWebBrowserCtrl->getAlwaysRefresh() || - mWebBrowserCtrl->getForceUpdate() ) + mTrusted = trusted; + if (mMediaSource) { - // All of these force an update - return TRUE; + mMediaSource->setTrustedBrowser(trusted); } - - // If the texture needs updating, render needs to be called. - if (mMediaSource && mMediaSource->hasMedia()) - { - LLPluginClassMedia* media = mMediaSource->getMediaPlugin(); - - if(media->textureValid() && media->getDirty()) - { - texture_dirty = true; - } - } - - - return texture_dirty; } -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLWebBrowserTexture::render() -{ - if(updateBrowserTexture()) - { - // updateBrowserTexture already verified that the media plugin is there and the texture is valid. - LLPluginClassMedia* media_plugin = mMediaSource->getMediaPlugin(); - LLRect dirty_rect; - - if(mNeedsUpdate) - { - // If we need an update, use the whole rect instead of the dirty rect. - dirty_rect.mLeft = 0; - dirty_rect.mBottom = 0; - dirty_rect.mRight = media_plugin->getWidth(); - dirty_rect.mTop = media_plugin->getHeight(); - } - else - { - mNeedsUpdate = media_plugin->getDirty(&dirty_rect); - } - - if ( mNeedsUpdate ) - { - mNeedsUpdate = false; - mWebBrowserCtrl->setForceUpdate(false); - - // Constrain the dirty rect to be inside the texture - S32 x_pos = llmax(dirty_rect.mLeft, 0); - S32 y_pos = llmax(dirty_rect.mBottom, 0); - S32 width = llmin(dirty_rect.mRight, getWidth()) - x_pos; - S32 height = llmin(dirty_rect.mTop, getHeight()) - y_pos; - - if(width > 0 && height > 0) - { - U8* data = media_plugin->getBitsData(); - - // Offset the pixels pointer to match x_pos and y_pos - data += ( x_pos * media_plugin->getTextureDepth() * media_plugin->getBitsWidth() ); - data += ( y_pos * media_plugin->getTextureDepth() ); - - setSubImage( - data, - media_plugin->getBitsWidth(), - media_plugin->getBitsHeight(), - x_pos, - y_pos, - width, - height, - TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) - } - - media_plugin->resetDirty(); - - return TRUE; - }; - }; - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// -S32 LLWebBrowserTexture::getMediaWidth() -{ - return mMediaWidth; -} - -//////////////////////////////////////////////////////////////////////////////// -// -S32 LLWebBrowserTexture::getMediaHeight() -{ - return mMediaHeight; -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLWebBrowserTexture::setNeedsUpdate() -{ - mNeedsUpdate = true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool LLWebBrowserTexture::getNeedsUpdate() -{ - return mNeedsUpdate; -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool LLWebBrowserTexture::getTextureCoordsOpenGL() -{ - return mTextureCoordsOpenGL; -} - - -//////////////////////////////////////////////////////////////////////////////// -// -void LLWebBrowserTexture::resize( S32 new_width, S32 new_height ) -{ - F32 scale_ratio = 1.f; - if (new_width > MAX_DIMENSION) - { - scale_ratio = (F32)MAX_DIMENSION / (F32)new_width; - } - if (new_height > MAX_DIMENSION) - { - scale_ratio = llmin(scale_ratio, (F32)MAX_DIMENSION / (F32)new_height); - } - - mMediaWidth = llround(scale_ratio * (F32)new_width); - mMediaHeight = llround(scale_ratio * (F32)new_height); - - adjustSize(); -} - -bool LLWebBrowserTexture::adjustSize() -{ - if (mMediaSource && mMediaSource->hasMedia()) - { - int natural_width = mMediaSource->getMediaPlugin()->getNaturalWidth(); - int natural_height = mMediaSource->getMediaPlugin()->getNaturalHeight(); - - if(natural_width != 0) - { - // If the media has a "natural size", use it. - mMediaWidth = natural_width; - mMediaHeight = natural_height; - } - - mMediaSource->setSize(mMediaWidth, mMediaHeight); - mNeedsResize = false; - - return true; - } - else - { - // The media isn't fully initialized yet, delay the resize until later. - mNeedsResize = true; - } - - return false; -} - -bool LLWebBrowserTexture::updateBrowserTexture() -{ - if (!adjustSize()) - return false; - - LLPluginClassMedia* media = mMediaSource->getMediaPlugin(); - - if(!media->textureValid()) - return false; - - if(mMediaSource->mNeedsNewTexture - || media->getTextureWidth() != getFullWidth() - || media->getTextureHeight() != getFullHeight() ) - { - //releaseGLTexture(); - - mFullWidth = media->getTextureWidth(); - mFullHeight = media->getTextureHeight(); - mTextureCoordsOpenGL = media->getTextureCoordsOpenGL(); - - const LLColor4U fill_color(0,0,0,255); - // will create mWidth * mHeight sized texture, using the texture params specified by the media. - generateGLTexture( - media->getTextureFormatInternal(), - media->getTextureFormatPrimary(), - media->getTextureFormatType(), - media->getTextureFormatSwapBytes(), - &fill_color); //Initialize the texture to black. - - - mMediaSource->mNeedsNewTexture = false; - } - - return true; -} // virtual LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const { @@ -1356,48 +1135,50 @@ LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const LLView* LLMediaCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("web_browser"); - node->getAttributeString("name", name); - - std::string start_url(""); - node->getAttributeString("start_url", start_url ); - - BOOL border_visible = true; - node->getAttributeBOOL("border_visible", border_visible); - + LLMediaCtrl::Params p; + + BOOL bval; + LLColor4 color; + S32 ival; LLRect rect; + + std::string sval("web_browser"); + node->getAttributeString("name", sval); + p.name = sval; createRect(node, rect, parent, LLRect()); + p.rect = rect; - LLMediaCtrl* web_browser = new LLMediaCtrl( name, rect ); + if(node->getAttributeString("start_url", sval )) + p.start_url = sval; + if(node->getAttributeString("error_page_url", sval )) + p.error_page_url = sval; + if(node->getAttributeString("media_id", sval )) + p.media_id = sval; + if(node->getAttributeString("initial_mime_type", sval )) + p.initial_mime_type = sval; + if(node->getAttributeBOOL("border_visible", bval)) + p.border_visible = bval; + if(node->getAttributeBOOL("focus_on_click", bval)) + p.focus_on_click = bval; + if(node->getAttributeBOOL("decouple_texture_size", bval)) + p.focus_on_click = bval; + if(node->getAttributeBOOL("trusted_content", bval)) + p.trusted_content = bval; + if(node->getAttributeS32("texture_width", ival)) + p.texture_width = ival; + if(node->getAttributeBOOL("texture_height", ival)) + p.texture_height = ival; + if(LLUICtrlFactory::getAttributeColor(node, "caret_color", color)) + p.caret_color = color; - if(node->hasAttribute("caret_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "caret_color", color); - LLColor4U colorU = LLColor4U(color); - web_browser->setCaretColor( colorU.mV[0], colorU.mV[1], colorU.mV[2] ); - } - - //BOOL ignore_ui_scale = web_browser->getIgnoreUIScale(); - //node->getAttributeBOOL("ignore_ui_scale", ignore_ui_scale); - //web_browser->setIgnoreUIScale((bool)ignore_ui_scale); + LLMediaCtrl* web_browser = LLUICtrlFactory::create(p,parent); web_browser->initFromXML(node, parent); - web_browser->setHomePageUrl( start_url ); - - web_browser->setBorderVisible( border_visible ); - - if(! start_url.empty()) + if(!p.start_url.getValue().empty()) { web_browser->navigateHome(); } return web_browser; } - -std::string LLMediaCtrl::getCurrentNavUrl() -{ - return mCurrentNavUrl; -} - diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index ef25ae0e3..f843fe075 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -34,25 +34,50 @@ #define LL_LLMediaCtrl_H #include "llviewermedia.h" -#include "llviewermediaobserver.h" + #include "lluictrl.h" #include "llframetimer.h" -#include "lldynamictexture.h" class LLViewBorder; -class LLWebBrowserTexture; class LLUICtrlFactory; //////////////////////////////////////////////////////////////////////////////// // class LLMediaCtrl : - public LLUICtrl, + public LLPanel, public LLViewerMediaObserver, public LLViewerMediaEventEmitter, public LLInstanceTracker { - public: - LLMediaCtrl( const std::string& name, const LLRect& rect ); + LOG_CLASS(LLMediaCtrl); +public: + struct Params : public LLInitParam::Block + { + Optional start_url; + + Optional border_visible, + hide_loading, + decouple_texture_size, + trusted_content, + focus_on_click; + + Optional texture_width, + texture_height; + + Optional caret_color; + + Optional initial_mime_type; + Optional media_id; + Optional error_page_url; + + Params(); + }; + +protected: + LLMediaCtrl(const Params&); + friend class LLUICtrlFactory; + +public: virtual ~LLMediaCtrl(); void setBorderVisible( BOOL border_visible ); @@ -74,6 +99,7 @@ class LLMediaCtrl : virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen); // navigation void navigateTo( std::string url_in, std::string mime_type = ""); @@ -83,8 +109,6 @@ class LLMediaCtrl : void navigateToLocalPage( const std::string& subdir, const std::string& filename_in ); bool canNavigateBack(); bool canNavigateForward(); - void setOpenInExternalBrowser( bool valIn ); - void setOpenInInternalBrowser( bool valIn ); std::string getCurrentNavUrl(); // By default, we do not handle "secondlife:///app/" SLURLs, because @@ -93,12 +117,17 @@ class LLMediaCtrl : // Javascript or some other mechanism. However, we need the search // floater and login page to handle these URLs. Those are safe // because we control the page content. See DEV-9530. JC. - void setTrusted( bool valIn ); - - void setHomePageUrl( const std::string urlIn ); + void setHomePageUrl( const std::string& urlIn, const std::string& mime_type = LLStringUtil::null ); std::string getHomePageUrl(); - // set/clear URL to visit when a 404 page is reached + void setTarget(const std::string& target); + + void setErrorPageURL(const std::string& url); + const std::string& getErrorPageURL(); + + // Clear the browser cache when the instance gets loaded + void clearCache(); + void set404RedirectUrl( std::string redirect_url ); void clr404RedirectUrl(); @@ -106,9 +135,6 @@ class LLMediaCtrl : bool getFrequentUpdates() { return mFrequentUpdates; }; void setFrequentUpdates( bool frequentUpdatesIn ) { mFrequentUpdates = frequentUpdatesIn; }; - void setIgnoreUIScale(bool ignore) { mIgnoreUIScale = ignore; } - bool getIgnoreUIScale() { return mIgnoreUIScale; } - void setAlwaysRefresh(bool refresh) { mAlwaysRefresh = refresh; } bool getAlwaysRefresh() { return mAlwaysRefresh; } @@ -127,6 +153,11 @@ class LLMediaCtrl : void setTextureSize(S32 width, S32 height); + void showNotification(boost::shared_ptr notify); + void hideNotification(); + + void setTrustedContent(bool trusted); + // over-rides virtual BOOL handleKeyHere( KEY key, MASK mask); virtual void handleVisibilityChange ( BOOL new_visibility ); @@ -142,80 +173,42 @@ class LLMediaCtrl : // Incoming media event dispatcher virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); - // handlers for individual events (could be done inside the switch in handleMediaEvent, they're just individual functions for clarity) - void onClickLinkHref( LLPluginClassMedia* self ); - - void onClickLinkNoFollow( LLPluginClassMedia* self ); - // right click debugging item void onOpenWebInspector(); + LLUUID getTextureID() {return mMediaTextureID;} + protected: void convertInputCoords(S32& x, S32& y); private: void onVisibilityChange ( const LLSD& new_visibility ); - static bool onClickLinkExternalTarget( const LLSD&, const LLSD& ); + bool onPopup(const LLSD& notification, const LLSD& response); const S32 mTextureDepthBytes; LLUUID mMediaTextureID; - LLPointer mWebBrowserImage; LLViewBorder* mBorder; - bool mFrequentUpdates; - bool mForceUpdate; - bool mOpenLinksInExternalBrowser; - bool mOpenLinksInInternalBrowser; - bool mTrusted; - std::string mHomePageUrl; - std::string mExternalUrl; - std::string mCurrentNavUrl; - bool mIgnoreUIScale; - bool mAlwaysRefresh; - viewer_media_t mMediaSource; - bool mTakeFocusOnClick; - ECursorType mLastSetCursor; - bool mStretchToFill; - bool mMaintainAspectRatio; - bool mHideLoading; - bool mHidingInitialLoad; - bool mDecoupleTextureSize; - S32 mTextureWidth; - S32 mTextureHeight; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -class LLWebBrowserTexture : public LLViewerDynamicTexture -{ -LOG_CLASS(LLWebBrowserTexture); - public: - LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ); - virtual ~LLWebBrowserTexture(); - - virtual BOOL needsRender(); - virtual void preRender( BOOL clear_depth = TRUE ) {}; - virtual void postRender( BOOL success ) {}; - virtual BOOL render(); - - bool adjustSize(); - S32 getMediaWidth(); - S32 getMediaHeight(); - bool getNeedsUpdate(); - void setNeedsUpdate(); - bool getTextureCoordsOpenGL(); - - void resize( S32 new_width, S32 new_height ); - bool updateBrowserTexture(); - - protected: - S32 mMediaWidth; - S32 mMediaHeight; - bool mNeedsUpdate; - bool mNeedsResize; - bool mTextureCoordsOpenGL; - LLFrameTimer mElapsedTime; - LLMediaCtrl* mWebBrowserCtrl; + bool mFrequentUpdates, + mForceUpdate, + mTrusted, + mAlwaysRefresh, + mTakeFocusOnClick, + mStretchToFill, + mMaintainAspectRatio, + mHideLoading, + mHidingInitialLoad, + mClearCache, + mHoverTextChanged, + mDecoupleTextureSize; + + std::string mHomePageUrl, + mHomePageMimeType, + mCurrentNavUrl, + mErrorPageURL, + mTarget; viewer_media_t mMediaSource; + S32 mTextureWidth, + mTextureHeight; }; #endif // LL_LLMediaCtrl_H diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp new file mode 100644 index 000000000..31038b4aa --- /dev/null +++ b/indra/newview/llmediadataclient.cpp @@ -0,0 +1,1070 @@ +/** + * @file llmediadataclient.cpp + * @brief class for queueing up requests for media data + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmediadataclient.h" + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +#include + +#include "llhttpstatuscodes.h" +#include "llsdutil.h" +#include "llmediaentry.h" +#include "lltextureentry.h" +#include "llviewerregion.h" + +// +// When making a request +// - obtain the "overall interest score" of the object. +// This would be the sum of the impls' interest scores. +// - put the request onto a queue sorted by this score +// (highest score at the front of the queue) +// - On a timer, once a second, pull off the head of the queue and send +// the request. +// - Any request that gets a 503 still goes through the retry logic +// + +/*************************************************************************************************************** + What's up with this queueing code? + + First, a bit of background: + + Media on a prim was added into the system in the Viewer 2.0 timeframe. In order to avoid changing the + network format of objects, an unused field in the object (the "MediaURL" string) was repurposed to + indicate that the object had media data, and also hold a sequence number and the UUID of the agent + who last updated the data. The actual media data for objects is accessed via the "ObjectMedia" capability. + Due to concerns about sim performance, requests to this capability are rate-limited to 5 requests every + 5 seconds per agent. + + The initial implementation of LLMediaDataClient used a single queue to manage requests to the "ObjectMedia" cap. + Requests to the cap were queued so that objects closer to the avatar were loaded in first, since they were most + likely to be the ones the media performance manager would load. + + This worked in some cases, but we found that it was possible for a scripted object that constantly updated its + media data to starve other objects, since the same queue contained both requests to load previously unseen media + data and requests to fetch media data in response to object updates. + + The solution for this we came up with was to have two queues. The sorted queue contains requests to fetch media + data for objects that don't have it yet, and the round-robin queue contains requests to update media data for + objects that have already completed their initial load. When both queues are non-empty, the code ping-pongs + between them so that updates can't completely block initial load-in. +**************************************************************************************************************/ + +// +// Forward decls +// +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 4; +const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000; +const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; + +// << operators +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q); +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q); + +template +static typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) + { + if(request->isMatch(*iter, match_type)) + { + return iter; + } + } + + return c.end(); +} + +template +static typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) + { + if(((*iter)->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == (*iter)->getType()))) + { + return iter; + } + } + + return c.end(); +} + +// NOTE: remove_matching_requests will not work correctly for containers where deleting an element may invalidate iterators +// to other elements in the container (such as std::vector). +// If the implementation is changed to use a container with this property, this will need to be revisited. +template +static void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end();) + { + typename T::value_type i = *iter; + typename T::iterator next = iter; + next++; + if((i->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == i->getType()))) + { + i->markDead(); + c.erase(iter); + } + iter = next; + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries, + U32 max_sorted_queue_size, + U32 max_round_robin_queue_size) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mMaxSortedQueueSize(max_sorted_queue_size), + mMaxRoundRobinQueueSize(max_round_robin_queue_size), + mQueueTimerIsRunning(false) +{ +} + +LLMediaDataClient::~LLMediaDataClient() +{ + stopQueueTimer(); +} + +bool LLMediaDataClient::isEmpty() const +{ + return mQueue.empty(); +} + +bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + if(find_matching_request(mQueue, object->getID()) != mQueue.end()) + return true; + + if(find_matching_request(mUnQueuedRequests, object->getID()) != mUnQueuedRequests.end()) + return true; + + return false; +} + +void LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + LL_DEBUGS("LLMediaDataClient") << "removing requests matching ID " << object->getID() << LL_ENDL; + remove_matching_requests(mQueue, object->getID()); + remove_matching_requests(mUnQueuedRequests, object->getID()); +} + +void LLMediaDataClient::startQueueTimer() +{ + if (! mQueueTimerIsRunning) + { + LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; + // LLEventTimer automagically takes care of the lifetime of this object + new QueueTimer(mQueueTimerDelay, this); + } + else { + LL_DEBUGS("LLMediaDataClient") << "queue timer is already running" << LL_ENDL; + } +} + +void LLMediaDataClient::stopQueueTimer() +{ + mQueueTimerIsRunning = false; +} + +bool LLMediaDataClient::processQueueTimer() +{ + if(isEmpty()) + return true; + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue size is: " << mQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mQueue << LL_ENDL; + + serviceQueue(); + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue size is: " << mQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mQueue << LL_ENDL; + + return isEmpty(); +} + +LLMediaDataClient::request_ptr_t LLMediaDataClient::dequeue() +{ + request_ptr_t request; + request_queue_t *queue_p = getQueue(); + + if (queue_p->empty()) + { + LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; + } + else + { + request = queue_p->front(); + + if(canServiceRequest(request)) + { + // We will be returning this request, so remove it from the queue. + queue_p->pop_front(); + } + else + { + // Don't return this request -- it's not ready to be serviced. + request = NULL; + } + } + + return request; +} + +void LLMediaDataClient::pushBack(request_ptr_t request) +{ + request_queue_t *queue_p = getQueue(); + queue_p->push_front(request); +} + +void LLMediaDataClient::trackRequest(request_ptr_t request) +{ + request_set_t::iterator iter = mUnQueuedRequests.find(request); + + if(iter != mUnQueuedRequests.end()) + { + LL_WARNS("LLMediaDataClient") << "Tracking already tracked request: " << *request << LL_ENDL; + } + else + { + mUnQueuedRequests.insert(request); + } +} + +void LLMediaDataClient::stopTrackingRequest(request_ptr_t request) +{ + request_set_t::iterator iter = mUnQueuedRequests.find(request); + + if (iter != mUnQueuedRequests.end()) + { + mUnQueuedRequests.erase(iter); + } + else + { + LL_WARNS("LLMediaDataClient") << "Removing an untracked request: " << *request << LL_ENDL; + } +} + +void LLMediaDataClient::serviceQueue() +{ + // Peel one off of the items from the queue and execute it + request_ptr_t request; + + do + { + request = dequeue(); + + if(request.isNull()) + { + // Queue is empty. + return; + } + + if(request->isDead()) + { + LL_INFOS("LLMediaDataClient") << "Skipping dead request " << *request << LL_ENDL; + continue; + } + + } while(false); + + // try to send the HTTP message to the cap url + std::string url = request->getCapability(); + if (!url.empty()) + { + const LLSD &sd_payload = request->getPayload(); + LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; + + // Add this request to the non-queued tracking list + trackRequest(request); + + // and make the post + LLHTTPClient::post(url, sd_payload, request->createResponder()); + } + else + { + // Cap url doesn't exist. + + if(request->getRetryCount() < mMaxNumRetries) + { + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " (empty cap url), will retry." << LL_ENDL; + // Put this request back at the head of its queue, and retry next time the queue timer fires. + request->incRetryCount(); + pushBack(request); + } + else + { + // This request has exceeded its maxumim retry count. It will be dropped. + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << mMaxNumRetries << " tries, dropping request." << LL_ENDL; + } + + } +} + + +// dump the queue +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) +{ + int i = 0; + LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); + LLMediaDataClient::request_queue_t::const_iterator end = q.end(); + while (iter != end) + { + s << "\t" << i << "]: " << (*iter)->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")"; + iter++; + i++; + } + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::QueueTimer +// Queue of LLMediaDataClientObject smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) +: LLEventTimer(time), mMDC(mdc) +{ + mMDC->setIsRunning(true); +} + +// virtual +BOOL LLMediaDataClient::QueueTimer::tick() +{ + BOOL result = TRUE; + + if (!mMDC.isNull()) + { + result = mMDC->processQueueTimer(); + + if(result) + { + // This timer won't fire again. + mMDC->setIsRunning(false); + mMDC = NULL; + } + } + + return result; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder::RetryTimer +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::RetryTimer::RetryTimer(F32 time, request_ptr_t request) +: LLEventTimer(time), mRequest(request) +{ + mRequest->startTracking(); +} + +// virtual +BOOL LLMediaDataClient::RetryTimer::tick() +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for dead request: " << *mRequest << ", aborting." << LL_ENDL; + } + else + { + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *mRequest << ", retrying." << LL_ENDL; + mRequest->reEnqueue(); + } + + // Release the ref to the request. + mRequest = NULL; + + // Don't fire again + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Request +// +////////////////////////////////////////////////////////////////////////////////////// +/*static*/U32 LLMediaDataClient::Request::sNum = 0; + +LLMediaDataClient::Request::Request(Type in_type, + LLMediaDataClientObject *obj, + LLMediaDataClient *mdc, + S32 face) +: mType(in_type), + mObject(obj), + mNum(++sNum), + mRetryCount(0), + mMDC(mdc), + mScore((F64)0.0), + mFace(face) +{ + mObjectID = mObject->getID(); +} + +const char *LLMediaDataClient::Request::getCapName() const +{ + if(mMDC) + return mMDC->getCapabilityName(); + + return ""; +} + +std::string LLMediaDataClient::Request::getCapability() const +{ + if(mMDC) + { + return getObject()->getCapabilityUrl(getCapName()); + } + + return ""; +} + +const char *LLMediaDataClient::Request::getTypeAsString() const +{ + Type t = getType(); + switch (t) + { + case GET: + return "GET"; + break; + case UPDATE: + return "UPDATE"; + break; + case NAVIGATE: + return "NAVIGATE"; + break; + case ANY: + return "ANY"; + break; + } + return ""; +} + + +void LLMediaDataClient::Request::reEnqueue() +{ + if(mMDC) + { + mMDC->enqueue(this); + } +} + +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ + if(mMDC) + return mMDC->mRetryTimerDelay; + + return 0.0f; +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ + if(mMDC) + return mMDC->mMaxNumRetries; + + return 0; +} + +void LLMediaDataClient::Request::updateScore() +{ + F64 tmp = mObject->getMediaInterest(); + if (tmp != mScore) + { + LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL; + mScore = tmp; + } +} + +void LLMediaDataClient::Request::markDead() +{ + mMDC = NULL; +} + +bool LLMediaDataClient::Request::isDead() +{ + return ((mMDC == NULL) || mObject->isDead()); +} + +void LLMediaDataClient::Request::startTracking() +{ + if(mMDC) + mMDC->trackRequest(this); +} + +void LLMediaDataClient::Request::stopTracking() +{ + if(mMDC) + mMDC->stopTrackingRequest(this); +} + +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) +{ + s << "request: num=" << r.getNum() + << " type=" << r.getTypeAsString() + << " ID=" << r.getID() + << " face=" << r.getFace() + << " #retries=" << r.getRetryCount(); + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::Responder(const request_ptr_t &request) +: mRequest(request) +{ +} + +/*virtual*/ +void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; + return; + } + + if (status == HTTP_SERVICE_UNAVAILABLE) + { + F32 retry_timeout = mRequest->getRetryTimerDelay(); + + mRequest->incRetryCount(); + + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), mRequest); + } + else + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " + << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } + } + else + { + std::string msg = boost::lexical_cast(status) + ": " + reason; + LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL; + } +} + +/*virtual*/ +void LLMediaDataClient::Responder::result(const LLSD& content) +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; + return; + } + + LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaDataClient +// Subclass of LLMediaDataClient for the ObjectMedia cap +// +////////////////////////////////////////////////////////////////////////////////////// + +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) +{ + // Create a get request and put it in the queue. + enqueue(new RequestGet(object, this)); +} + +const char *LLObjectMediaDataClient::getCapabilityName() const +{ + return "ObjectMedia"; +} + +LLObjectMediaDataClient::request_queue_t *LLObjectMediaDataClient::getQueue() +{ + return (mCurrentQueueIsTheSortedQueue) ? &mQueue : &mRoundRobinQueue; +} + +void LLObjectMediaDataClient::sortQueue() +{ + if(!mQueue.empty()) + { + // score all elements in the sorted queue. + for(request_queue_t::iterator iter = mQueue.begin(); iter != mQueue.end(); iter++) + { + (*iter)->updateScore(); + } + + // Re-sort the list... + mQueue.sort(compareRequestScores); + + // ...then cull items over the max + U32 size = mQueue.size(); + if (size > mMaxSortedQueueSize) + { + U32 num_to_cull = (size - mMaxSortedQueueSize); + LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling " + << num_to_cull << " items" << LL_ENDL; + while (num_to_cull-- > 0) + { + mQueue.back()->markDead(); + mQueue.pop_back(); + } + } + } + +} + +// static +bool LLObjectMediaDataClient::compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2) +{ + if (o2.isNull()) return true; + if (o1.isNull()) return false; + return ( o1->getScore() > o2->getScore() ); +} + +void LLObjectMediaDataClient::enqueue(Request *request) +{ + if(request->isDead()) + { + LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL; + return; + } + + // Invariants: + // new requests always go into the sorted queue. + // + + bool is_new = request->isNew(); + + if(!is_new && (request->getType() == Request::GET)) + { + // For GET requests that are not new, if a matching request is already in the round robin queue, + // in flight, or being retried, leave it at its current position. + request_queue_t::iterator iter = find_matching_request(mRoundRobinQueue, request->getID(), Request::GET); + request_set_t::iterator iter2 = find_matching_request(mUnQueuedRequests, request->getID(), Request::GET); + + if( (iter != mRoundRobinQueue.end()) || (iter2 != mUnQueuedRequests.end()) ) + { + LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; + + return; + } + } + + // TODO: should an UPDATE cause pending GET requests for the same object to be removed from the queue? + // IF the update will cause an object update message to be sent out at some point in the future, it probably should. + + // Remove any existing requests of this type for this object + remove_matching_requests(mQueue, request->getID(), request->getType()); + remove_matching_requests(mRoundRobinQueue, request->getID(), request->getType()); + remove_matching_requests(mUnQueuedRequests, request->getID(), request->getType()); + + if (is_new) + { + LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL; + + mQueue.push_back(request); + + LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mQueue << LL_ENDL; + } + else + { + if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize) + { + LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL; + return; + } + + LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL; + // Push the request on the pending queue + mRoundRobinQueue.push_back(request); + + LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL; + } + // Start the timer if not already running + startQueueTimer(); +} + +bool LLObjectMediaDataClient::canServiceRequest(request_ptr_t request) +{ + if(mCurrentQueueIsTheSortedQueue) + { + if(!request->getObject()->isInterestingEnough()) + { + LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; + return false; + } + } + + return true; +}; + +void LLObjectMediaDataClient::swapCurrentQueue() +{ + // Swap + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + // If its empty, swap back + if (getQueue()->empty()) + { + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + } +} + +bool LLObjectMediaDataClient::isEmpty() const +{ + return mQueue.empty() && mRoundRobinQueue.empty(); +} + +bool LLObjectMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + // First, call parent impl. + if(LLMediaDataClient::isInQueue(object)) + return true; + + if(find_matching_request(mRoundRobinQueue, object->getID()) != mRoundRobinQueue.end()) + return true; + + return false; +} + +void LLObjectMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + // First, call parent impl. + LLMediaDataClient::removeFromQueue(object); + + remove_matching_requests(mRoundRobinQueue, object->getID()); +} + +bool LLObjectMediaDataClient::processQueueTimer() +{ + if(isEmpty()) + return true; + + LL_DEBUGS("LLMediaDataClient") << "started, SORTED queue size is: " << mQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL; + +// purgeDeadRequests(); + + sortQueue(); + + LL_DEBUGS("LLMediaDataClientQueue") << "after sort, SORTED queue is: " << mQueue << LL_ENDL; + + serviceQueue(); + + swapCurrentQueue(); + + LL_DEBUGS("LLMediaDataClient") << "finished, SORTED queue size is: " << mQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL; + + return isEmpty(); +} + +LLObjectMediaDataClient::RequestGet::RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): + LLMediaDataClient::Request(LLMediaDataClient::Request::GET, obj, mdc) +{ +} + +LLSD LLObjectMediaDataClient::RequestGet::getPayload() const +{ + LLSD result; + result["verb"] = "GET"; + result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID(); + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestGet::createResponder() +{ + return new LLObjectMediaDataClient::Responder(this); +} + + +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) +{ + // Create an update request and put it in the queue. + enqueue(new RequestUpdate(object, this)); +} + +LLObjectMediaDataClient::RequestUpdate::RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): + LLMediaDataClient::Request(LLMediaDataClient::Request::UPDATE, obj, mdc) +{ +} + +LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const +{ + LLSD result; + result["verb"] = "UPDATE"; + result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID(); + + LLSD object_media_data; + int i = 0; + int end = mObject->getMediaDataCount(); + for ( ; i < end ; ++i) + { + object_media_data.append(mObject->getMediaDataLLSD(i)); + } + + result[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestUpdate::createResponder() +{ + // This just uses the base class's responder. + return new LLMediaDataClient::Responder(this); +} + + +/*virtual*/ +void LLObjectMediaDataClient::Responder::result(const LLSD& content) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + // This responder is only used for GET requests, not UPDATE. + + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + + // XXX Warn user? + } + else + { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + content[LLTextureEntry::MEDIA_VERSION_KEY]); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaNavigateClient +// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap +// +////////////////////////////////////////////////////////////////////////////////////// + +const char *LLObjectMediaNavigateClient::getCapabilityName() const +{ + return "ObjectMediaNavigate"; +} + +void LLObjectMediaNavigateClient::enqueue(Request *request) +{ + if(request->isDead()) + { + LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL; + return; + } + + // If there's already a matching request in the queue, remove it. + request_queue_t::iterator iter = find_matching_request(mQueue, request); + if(iter != mQueue.end()) + { + LL_DEBUGS("LLMediaDataClient") << "removing matching queued request " << (**iter) << LL_ENDL; + mQueue.erase(iter); + } + else + { + request_set_t::iterator set_iter = find_matching_request(mUnQueuedRequests, request); + if(set_iter != mUnQueuedRequests.end()) + { + LL_DEBUGS("LLMediaDataClient") << "removing matching unqueued request " << (**set_iter) << LL_ENDL; + mUnQueuedRequests.erase(set_iter); + } + } + +#if 0 + // Sadly, this doesn't work. It ends up creating a race condition when the user navigates and then hits the "back" button + // where the navigate-back appears to be spurious and doesn't get broadcast. + if(request->getObject()->isCurrentMediaUrl(request->getFace(), request->getURL())) + { + // This navigate request is trying to send the face to the current URL. Drop it. + LL_DEBUGS("LLMediaDataClient") << "dropping spurious request " << (*request) << LL_ENDL; + } + else +#endif + { + LL_DEBUGS("LLMediaDataClient") << "queueing new request " << (*request) << LL_ENDL; + mQueue.push_back(request); + + // Start the timer if not already running + startQueueTimer(); + } +} + +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) +{ + +// LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL; + + // Create a get request and put it in the queue. + enqueue(new RequestNavigate(object, this, texture_index, url)); +} + +LLObjectMediaNavigateClient::RequestNavigate::RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url): + LLMediaDataClient::Request(LLMediaDataClient::Request::NAVIGATE, obj, mdc, (S32)texture_index), + mURL(url) +{ +} + +LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const +{ + LLSD result; + result[LLTextureEntry::OBJECT_ID_KEY] = getID(); + result[LLMediaEntry::CURRENT_URL_KEY] = mURL; + result[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)getFace(); + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaNavigateClient::RequestNavigate::createResponder() +{ + return new LLObjectMediaNavigateClient::Responder(this); +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base + // class + if (status == HTTP_SERVICE_UNAVAILABLE) + { + LLMediaDataClient::Responder::error(status, reason); + } + else + { + // bounce the face back + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL; + + if (content.has("error")) + { + const LLSD &error = content["error"]; + int error_code = error["code"]; + + if (ERROR_PERMISSION_DENIED_CODE == error_code) + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } + else + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + } + + // XXX Warn user? + } + else + { + // No action required. + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " result : " << ll_print_sd(content) << LL_ENDL; + } +} diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h new file mode 100644 index 000000000..19ea5c6ce --- /dev/null +++ b/indra/newview/llmediadataclient.h @@ -0,0 +1,416 @@ +/** + * @file llmediadataclient.h + * @brief class for queueing up requests to the media service + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMEDIADATACLIENT_H +#define LL_LLMEDIADATACLIENT_H + +#include "llhttpclient.h" +#include +#include "llrefcount.h" +#include "llpointer.h" +#include "lleventtimer.h" + +extern AIHTTPTimeoutPolicy mediaDataClientResponder_timeout; + +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: + // Get the number of media data items + virtual U8 getMediaDataCount() const = 0; + // Get the media data at index, as an LLSD + virtual LLSD getMediaDataLLSD(U8 index) const = 0; + // Return true if the current URL for the face in the media data matches the specified URL. + virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const = 0; + // Get this object's UUID + virtual LLUUID getID() const = 0; + // Navigate back to previous URL + virtual void mediaNavigateBounceBack(U8 index) = 0; + // Does this object have media? + virtual bool hasMedia() const = 0; + // Update the object's media data to the given array + virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0; + // Return the total "interest" of the media (on-screen area) + virtual F64 getMediaInterest() const = 0; + // Return the given cap url + virtual std::string getCapabilityUrl(const std::string &name) const = 0; + // Return whether the object has been marked dead + virtual bool isDead() const = 0; + // Returns a media version number for the object + virtual U32 getMediaVersion() const = 0; + // Returns whether the object is "interesting enough" to fetch + virtual bool isInterestingEnough() const = 0; + // Returns whether we've seen this object yet or not + virtual bool isNew() const = 0; + + // smart pointer + typedef LLPointer ptr_t; +}; + + +// This object creates a priority queue for requests. +// Abstracts the Cap URL, the request, and the responder +class LLMediaDataClient : public LLRefCount +{ +public: + LOG_CLASS(LLMediaDataClient); + + const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) + const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs + const static U32 MAX_RETRIES;// = 4; + const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000; + const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000; + + // Constructor + LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE); + + F32 getRetryTimerDelay() const { return mRetryTimerDelay; } + + // Returns true iff the queue is empty + virtual bool isEmpty() const; + + // Returns true iff the given object is in the queue + virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + // Called only by the Queue timer and tests (potentially) + virtual bool processQueueTimer(); + +protected: + // Destructor + virtual ~LLMediaDataClient(); // use unref + + class Responder; + + // Request (pure virtual base class for requests in the queue) + class Request : public LLRefCount + { + public: + // Subclasses must implement this to build a payload for their request type. + virtual LLSD getPayload() const = 0; + // and must create the correct type of responder. + virtual Responder *createResponder() = 0; + + virtual std::string getURL() { return ""; } + + enum Type { + GET, + UPDATE, + NAVIGATE, + ANY + }; + + protected: + // The only way to create one of these is through a subclass. + Request(Type in_type, LLMediaDataClientObject *obj, LLMediaDataClient *mdc, S32 face = -1); + public: + LLMediaDataClientObject *getObject() const { return mObject; } + + U32 getNum() const { return mNum; } + U32 getRetryCount() const { return mRetryCount; } + void incRetryCount() { mRetryCount++; } + Type getType() const { return mType; } + F64 getScore() const { return mScore; } + + // Note: may return empty string! + std::string getCapability() const; + const char *getCapName() const; + const char *getTypeAsString() const; + + // Re-enqueue thyself + void reEnqueue(); + + F32 getRetryTimerDelay() const; + U32 getMaxNumRetries() const; + + bool isObjectValid() const { return mObject.notNull() && (!mObject->isDead()); } + bool isNew() const { return isObjectValid() && mObject->isNew(); } + void updateScore(); + + void markDead(); + bool isDead(); + void startTracking(); + void stopTracking(); + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + + const LLUUID &getID() const { return mObjectID; } + S32 getFace() const { return mFace; } + + bool isMatch (const Request* other, Type match_type = ANY) const + { + return ((match_type == ANY) || (mType == other->mType)) && + (mFace == other->mFace) && + (mObjectID == other->mObjectID); + } + protected: + LLMediaDataClientObject::ptr_t mObject; + private: + Type mType; + // Simple tracking + U32 mNum; + static U32 sNum; + U32 mRetryCount; + F64 mScore; + + LLUUID mObjectID; + S32 mFace; + + // Back pointer to the MDC...not a ref! + LLMediaDataClient *mMDC; + }; + typedef LLPointer request_ptr_t; + + // Responder + class Responder : public LLHTTPClient::ResponderWithResult + { + public: + + Responder(const request_ptr_t &request); + //If we get back an error (not found, etc...), handle it here + virtual void error(U32 status, const std::string& reason); + //If we get back a normal response, handle it here. Default just logs it. + virtual void result(const LLSD& content); + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mediaDataClientResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "LLMediaDataClientResponder"; } + + request_ptr_t &getRequest() { return mRequest; } + + private: + request_ptr_t mRequest; + }; + + class RetryTimer : public LLEventTimer + { + public: + RetryTimer(F32 time, request_ptr_t); + virtual BOOL tick(); + private: + // back-pointer + request_ptr_t mRequest; + }; + + +protected: + typedef std::list request_queue_t; + typedef std::set request_set_t; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const = 0; + + // Puts the request into a queue, appropriately handling duplicates, etc. + virtual void enqueue(Request*) = 0; + + virtual void serviceQueue(); + + virtual request_queue_t *getQueue() { return &mQueue; }; + + // Gets the next request, removing it from the queue + virtual request_ptr_t dequeue(); + + virtual bool canServiceRequest(request_ptr_t request) { return true; }; + + // Returns a request to the head of the queue (should only be used for requests that came from dequeue + virtual void pushBack(request_ptr_t request); + + void trackRequest(request_ptr_t request); + void stopTrackingRequest(request_ptr_t request); + + request_queue_t mQueue; + + const F32 mQueueTimerDelay; + const F32 mRetryTimerDelay; + const U32 mMaxNumRetries; + const U32 mMaxSortedQueueSize; + const U32 mMaxRoundRobinQueueSize; + + // Set for keeping track of requests that aren't in either queue. This includes: + // Requests that have been sent and are awaiting a response (pointer held by the Responder) + // Requests that are waiting for their retry timers to fire (pointer held by the retry timer) + request_set_t mUnQueuedRequests; + + void startQueueTimer(); + void stopQueueTimer(); + +private: + + static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + friend std::ostream& operator<<(std::ostream &s, const request_queue_t &q); + + class QueueTimer : public LLEventTimer + { + public: + QueueTimer(F32 time, LLMediaDataClient *mdc); + virtual BOOL tick(); + private: + // back-pointer + LLPointer mMDC; + }; + + void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + bool mQueueTimerIsRunning; + + template friend typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + template friend typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + template friend void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + +}; + +// MediaDataClient specific for the ObjectMedia cap +class LLObjectMediaDataClient : public LLMediaDataClient +{ +public: + LOG_CLASS(LLObjectMediaDataClient); + LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries), + mCurrentQueueIsTheSortedQueue(true) + {} + + void fetchMedia(LLMediaDataClientObject *object); + void updateMedia(LLMediaDataClientObject *object); + + class RequestGet: public Request + { + public: + RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + }; + + class RequestUpdate: public Request + { + public: + RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + }; + + // Returns true iff the queue is empty + virtual bool isEmpty() const; + + // Returns true iff the given object is in the queue + virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + virtual bool processQueueTimer(); + + virtual bool canServiceRequest(request_ptr_t request); + +protected: + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + virtual request_queue_t *getQueue(); + + // Puts the request into the appropriate queue + virtual void enqueue(Request*); + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void result(const LLSD &content); + }; +private: + // The Get/Update data client needs a second queue to avoid object updates starving load-ins. + void swapCurrentQueue(); + + request_queue_t mRoundRobinQueue; + bool mCurrentQueueIsTheSortedQueue; + + // Comparator for sorting + static bool compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2); + void sortQueue(); +}; + + +// MediaDataClient specific for the ObjectMediaNavigate cap +class LLObjectMediaNavigateClient : public LLMediaDataClient +{ +public: + LOG_CLASS(LLObjectMediaNavigateClient); + // NOTE: from llmediaservice.h + static const int ERROR_PERMISSION_DENIED_CODE = 8002; + + LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} + + void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); + + // Puts the request into the appropriate queue + virtual void enqueue(Request*); + + class RequestNavigate: public Request + { + public: + RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + /*virtual*/ std::string getURL() { return mURL; } + private: + std::string mURL; + }; + +protected: + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void error(U32 status, const std::string& reason); + virtual void result(const LLSD &content); + private: + void mediaNavigateBounceBack(); + }; + +}; + + +#endif // LL_LLMEDIADATACLIENT_H diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 7af76fe68..831b4afa0 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -610,7 +610,7 @@ BOOL LLPanelAvatarWeb::postBuild(void) mWebBrowser->addObserver(this); // links open in internally - mWebBrowser->setOpenInExternalBrowser( false ); + //mWebBrowser->setOpenInExternalBrowser( false ); return TRUE; } diff --git a/indra/newview/llpaneldirfind.cpp b/indra/newview/llpaneldirfind.cpp index b380942f7..c181497cc 100644 --- a/indra/newview/llpaneldirfind.cpp +++ b/indra/newview/llpaneldirfind.cpp @@ -151,13 +151,9 @@ BOOL LLPanelDirFind::postBuild() if (mWebBrowser) { mWebBrowser->addObserver(this); - - // new pages appear in same window as the results page now - mWebBrowser->setOpenInInternalBrowser( false ); - mWebBrowser->setOpenInExternalBrowser( false ); // need to handle secondlife:///app/ URLs for direct teleports - mWebBrowser->setTrusted( true ); + mWebBrowser->setTrustedContent( true ); // redirect 404 pages from S3 somewhere else mWebBrowser->set404RedirectUrl( getString("redirect_404_url") ); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 7f43b4ce3..2d7da3bc7 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -53,6 +53,8 @@ #include "llface.h" #include "llinventorymodel.h" //Perms check for texture params #include "lllineeditor.h" +#include "llmediaentry.h" +#include "llnotificationsutil.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llspinctrl.h" @@ -66,8 +68,10 @@ #include "llviewerobject.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llvovolume.h" #include "lluictrlfactory.h" #include "llpluginclassmedia.h" +#include "llviewertexturelist.h" // // Methods @@ -549,20 +553,43 @@ void LLPanelFace::getState() LLUUID id; struct f1 : public LLSelectedTEGetFunctor { - LLUUID get(LLViewerObject* object, S32 te) + LLUUID get(LLViewerObject* object, S32 te_index) { + LLUUID id; //LLViewerTexture* image = object->getTEImage(te); - LLTextureEntry* image = object->getTE(te); //Singu Note: Use this instead of the above. - //The above actually returns LLViewerFetchedTexture::sDefaultImagep when - //the texture id is null, which gives us IMG_DEFAULT, not LLUUID::null - //Such behavior prevents the 'None' button from ever greying out in the face panel. - return image ? image->getID() : LLUUID::null; - + LLTextureEntry* image = object->getTE(te_index); //Singu Note: Use this instead of the above. + //The above actually returns LLViewerFetchedTexture::sDefaultImagep when + //the texture id is null, which gives us IMG_DEFAULT, not LLUUID::null + //Such behavior prevents the 'None' button from ever greying out in the face panel. + if (image) id = image->getID(); + if (!id.isNull() && LLViewerMedia::textureHasMedia(id)) + { + LLTextureEntry *te = object->getTE(te_index); + if (te) + { + LLViewerTexture* tex = te->getID().notNull() ? gTextureList.findImage(te->getID()) : NULL ; + if(!tex) + { + tex = LLViewerFetchedTexture::sDefaultImagep; + } + if (tex) + { + id = tex->getID(); + } + } + } + return id; } } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, id ); + if(LLViewerMedia::textureHasMedia(id)) + { + getChildView("textbox autofix")->setEnabled(editable); + getChildView("button align")->setEnabled(editable); + } + if (identical) { // All selected have the same texture @@ -593,13 +620,6 @@ void LLPanelFace::getState() } } } - - if(LLViewerMedia::textureHasMedia(id)) - { - childSetEnabled("textbox autofix",editable); - childSetEnabled("button align",editable); - } - } @@ -1149,9 +1169,21 @@ struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor { virtual bool apply(LLViewerObject* object, S32 te) { - // TODO: the media impl pointer should actually be stored by the texture - viewer_media_t pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(object->getTE ( te )->getID()); - // only do this if it's a media texture + viewer_media_t pMediaImpl; + + const LLTextureEntry* tep = object->getTE(te); + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if ( mep ) + { + pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + } + + if ( pMediaImpl.isNull()) + { + // If we didn't find face media for this face, check whether this face is showing parcel media. + pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); + } + if ( pMediaImpl.notNull()) { LLPluginClassMedia *media = pMediaImpl->getMediaPlugin(); @@ -1183,6 +1215,17 @@ void LLPanelFace::onClickAutoFix(void* userdata) LLPanelFaceSendFunctor sendfunc; LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc); } + + + +// TODO: I don't know who put these in or what these are for??? +void LLPanelFace::setMediaURL(const std::string& url) +{ +} +void LLPanelFace::setMediaType(const std::string& mime_type) +{ +} + // static void LLPanelFace::onCommitPlanarAlign(LLUICtrl* ctrl, void* userdata) { diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 9363f076a..2f9a7887a 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -56,6 +56,8 @@ public: virtual ~LLPanelFace(); void refresh(); + void setMediaURL(const std::string& url); + void setMediaType(const std::string& mime_type); protected: void getState(); @@ -69,6 +71,7 @@ protected: void sendShiny(); // applies and sends shininess void sendFullbright(); // applies and sends full bright void sendGlow(); + void sendMedia(); // this function is to return TRUE if the dra should succeed. static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item, void* ud); diff --git a/indra/newview/llpanelgeneral.cpp b/indra/newview/llpanelgeneral.cpp index bd67c5284..a2b998ef4 100644 --- a/indra/newview/llpanelgeneral.cpp +++ b/indra/newview/llpanelgeneral.cpp @@ -39,7 +39,6 @@ #include "llcolorswatch.h" #include "llcombobox.h" #include "lluictrlfactory.h" -#include "llurlsimstring.h" #include "llviewercontrol.h" #include "llagent.h" @@ -165,8 +164,6 @@ void LLPanelGeneral::apply() gSavedSettings.setBOOL("UIAutoScale", childGetValue("ui_auto_scale")); gSavedSettings.setString("Language", childGetValue("language_combobox")); - LLURLSimString::setString(childGetValue("location_combobox")); - LLComboBox* crash_behavior_combobox = getChild("crash_behavior_combobox"); gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior_combobox->getCurrentIndex()); } diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp index e49e849a0..07fc30553 100644 --- a/indra/newview/llpanellandmedia.cpp +++ b/indra/newview/llpanellandmedia.cpp @@ -92,6 +92,7 @@ BOOL LLPanelLandMedia::postBuild() mMediaTextureCtrl->setCallbackUserData( this ); mMediaTextureCtrl->setAllowNoTexture ( TRUE ); mMediaTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + mMediaTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER); mMediaTextureCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); mMediaAutoScaleCheck = getChild("media_auto_scale"); @@ -157,7 +158,7 @@ void LLPanelLandMedia::refresh() std::string mime_type = parcel->getMediaType(); if (mime_type.empty()) { - mime_type = "none/none"; + mime_type = LLMIMETypes::getDefaultMimeType(); } setMediaType(mime_type); mMediaTypeCombo->setEnabled( can_change_media ); @@ -203,17 +204,17 @@ void LLPanelLandMedia::refresh() mSetURLButton->setEnabled( can_change_media ); mResetURLButton->setEnabled( can_change_media ); - LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mURLEntryFloater.get(); + /*LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mURLEntryFloater.get(); if (floater_url_entry) { floater_url_entry->updateFromLandMediaPanel(); - } + }*/ } } void LLPanelLandMedia::populateMIMECombo() { - std::string default_mime_type = "none/none"; + std::string default_mime_type = LLMIMETypes::getDefaultMimeType(); std::string default_label; LLMIMETypes::mime_widget_set_map_t::const_iterator it; for (it = LLMIMETypes::sWidgetMap.begin(); it != LLMIMETypes::sWidgetMap.end(); ++it) @@ -327,7 +328,7 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) void LLPanelLandMedia::onSetBtn(void *userdata) { LLPanelLandMedia *self = (LLPanelLandMedia *)userdata; - self->mURLEntryFloater = LLFloaterURLEntry::show( self->getHandle() ); + self->mURLEntryFloater = LLFloaterURLEntry::show( self->getHandle(), self->getMediaURL() ); LLFloater* parent_floater = gFloaterView->getParentFloater(self); if (parent_floater) { diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index f2523b505..497d04af7 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -45,6 +45,7 @@ #include "sgversion.h" #include "v4color.h" +#include "llappviewer.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ @@ -61,20 +62,17 @@ #include "llui.h" #include "lluiconstants.h" #include "llurlhistory.h" // OGPX : regionuri text box has a history of region uris (if FN/LN are loaded at startup) -#include "llurlsimstring.h" #include "llviewerbuild.h" #include "llviewertexturelist.h" #include "llviewermenu.h" // for handle_preferences() #include "llviewernetwork.h" #include "llviewerwindow.h" // to link into child list #include "llnotify.h" -#include "llurlsimstring.h" #include "lluictrlfactory.h" #include "llhttpclient.h" #include "llweb.h" #include "llmediactrl.h" -#include "llfloatermediabrowser.h" #include "llfloatertos.h" #include "llglheaders.h" @@ -84,7 +82,6 @@ // [/RLVa:KB] // -#include "llappviewer.h" #include "llspinctrl.h" #include "llviewermessage.h" #include @@ -93,8 +90,6 @@ #include "llstring.h" #include -#define USE_VIEWER_AUTH 0 - class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy iamHereLogin_timeout; @@ -149,7 +144,7 @@ class LLLoginRefreshHandler : public LLCommandHandler { public: // don't allow from external browsers - LLLoginRefreshHandler() : LLCommandHandler("login_refresh", true) { } + LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) @@ -202,11 +197,6 @@ namespace { boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; }; -void set_start_location(LLUICtrl* ctrl, void* data) -{ - LLURLSimString::setString(ctrl->getValue().asString()); -} - //--------------------------------------------------------------------------- // Public methods //--------------------------------------------------------------------------- @@ -216,8 +206,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, : LLPanel(std::string("panel_login"), LLRect(0,600,800,0), FALSE), // not bordered mLogoImage(), mCallback(callback), - mCallbackData(cb_data), - mHtmlAvailable( TRUE ) + mCallbackData(cb_data) { setFocusRoot(TRUE); @@ -244,13 +233,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLUICtrlFactory::getInstance()->buildPanel(this, "panel_login.xml"); -#if USE_VIEWER_AUTH - //leave room for the login menu bar - setRect(LLRect(0, rect.getHeight()-18, rect.getWidth(), 0)); -#endif reshape(rect.getWidth(), rect.getHeight()); -#if !USE_VIEWER_AUTH LLComboBox* name_combo = sInstance->getChild("name_combo"); name_combo->setCommitCallback(onSelectLoginEntry); name_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLoginComboLostFocus, this, name_combo)); @@ -270,38 +254,41 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, sendChildToBack(getChildView("channel_text")); sendChildToBack(getChildView("forgot_password_text")); - //OGPX : This keeps the uris in a history file - //OGPX TODO: should this be inside an OGP only check? - LLComboBox* regioncombo = getChild("regionuri_edit"); - regioncombo->setAllowTextEntry(TRUE, 256, FALSE); - std::string current_regionuri = gSavedSettings.getString("CmdLineRegionURI"); - - // iterate on uri list adding to combobox (couldn't figure out how to add them all in one call) - // ... and also append the command line value we might have gotten to the URLHistory - LLSD regionuri_history = LLURLHistory::getURLHistory("regionuri"); - LLSD::array_iterator iter_history = regionuri_history.beginArray(); - LLSD::array_iterator iter_end = regionuri_history.endArray(); - for (; iter_history != iter_end; ++iter_history) - { - regioncombo->addSimpleElement((*iter_history).asString()); - } - - if ( LLURLHistory::appendToURLCollection("regionuri",current_regionuri)) - { - // since we are in login, another read of urlhistory file is going to happen - // so we need to persist the new value we just added (or maybe we should do it in startup.cpp?) - - // since URL history only populated on create of sInstance, add to combo list directly - regioncombo->addSimpleElement(current_regionuri); - } - - // select which is displayed if we have a current URL. - regioncombo->setSelectedByValue(LLSD(current_regionuri),TRUE); - //llinfos << " url history: " << LLSDOStreamer(LLURLHistory::getURLHistory("regionuri")) << llendl; - LLComboBox* combo = getChild("start_location_combo"); - combo->setAllowTextEntry(TRUE, 128, FALSE); + LLComboBox* location_combo = getChild("start_location_combo"); + updateLocationSelectorsVisibility(); // separate so that it can be called from preferences + location_combo->setAllowTextEntry(TRUE, 128, FALSE); + location_combo->setFocusLostCallback( boost::bind(&LLPanelLogin::onLocationSLURL, this) ); + + LLComboBox *server_choice_combo = getChild("grids_combo"); + server_choice_combo->setCommitCallback(boost::bind(&LLPanelLogin::onSelectGrid, _1)); + + // Load all of the grids, sorted, and then add a bar and the current grid at the top + updateGridCombo(); + + LLSLURL start_slurl(LLStartUp::getStartSLURL()); + if ( !start_slurl.isSpatial() ) // has a start been established by the command line or NextLoginLocation ? + { + // no, so get the preference setting + std::string defaultStartLocation = gSavedSettings.getString("LoginLocation"); + LL_INFOS("AppInit")<<"default LoginLocation '"<" BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation"); - std::string sim_string = LLURLSimString::sInstance.mSimString; + std::string sim_string = start_slurl.getRegion(); if (!sim_string.empty()) { // Replace "" with this region name - combo->remove(2); - combo->add( sim_string ); - combo->setTextEntry(sim_string); - combo->setCurrentByIndex( 2 ); + location_combo->remove(2); + location_combo->add( sim_string ); + location_combo->setTextEntry(sim_string); + location_combo->setCurrentByIndex( 2 ); } else if (login_last) { - combo->setCurrentByIndex( 1 ); + location_combo->setCurrentByIndex( 1 ); } else { - combo->setCurrentByIndex( 0 ); + location_combo->setCurrentByIndex( 0 ); } - combo->setCommitCallback( &set_start_location ); - childSetAction("connect_btn", onClickConnect, this); setDefaultBtn("connect_btn"); - // childSetAction("quit_btn", onClickQuit, this); childSetAction("grids_btn", onClickGrids, this); - childSetCommitCallback("grids_combo", onSelectGrid, this); std::string channel = gVersionChannel; @@ -355,26 +338,13 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLTextBox* create_new_account_text = getChild("create_new_account_text"); create_new_account_text->setClickedCallback(boost::bind(&onClickNewAccount)); -#endif - + // get the web browser control LLMediaCtrl* web_browser = getChild("login_html"); web_browser->addObserver(this); - // Need to handle login secondlife:///app/ URLs - web_browser->setTrusted( true ); - - // don't make it a tab stop until SL-27594 is fixed - web_browser->setTabStop(FALSE); - // web_browser->navigateToLocalPage( "loading", "loading.html" ); - - // make links open in external browser - web_browser->setOpenInExternalBrowser( true ); - reshapeBrowser(); - updateGridCombo(); - childSetVisible("create_new_account_text", !gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()); childSetVisible("forgot_password_text", @@ -382,11 +352,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, loadLoginPage(); -#if !USE_VIEWER_AUTH - // Initialize visibility (and don't force visibility - use prefs) - refreshLocation( false ); -#endif - } void LLPanelLogin::setSiteIsAlive( bool alive ) @@ -400,33 +365,16 @@ void LLPanelLogin::setSiteIsAlive( bool alive ) loadLoginPage(); web_browser->setVisible(true); - - // mark as available - mHtmlAvailable = TRUE; } } else // the site is not available (missing page, server down, other badness) { -#if !USE_VIEWER_AUTH if ( web_browser ) { // hide browser control (revealing default one) web_browser->setVisible( FALSE ); - - // mark as unavailable - mHtmlAvailable = FALSE; } -#else - - if ( web_browser ) - { - web_browser->navigateToLocalPage( "loading-error" , "index.html" ); - - // mark as available - mHtmlAvailable = TRUE; - } -#endif } } @@ -454,13 +402,8 @@ void LLPanelLogin::reshapeBrowser() LLRect rect = gViewerWindow->getWindowRectScaled(); LLRect html_rect; html_rect.setCenterAndSize( -#if USE_VIEWER_AUTH - rect.getCenterX() - 2, rect.getCenterY(), - rect.getWidth() + 6, rect.getHeight()); -#else - rect.getCenterX() - 2, rect.getCenterY() + 40, - rect.getWidth() + 6, rect.getHeight() - 78 ); -#endif + rect.getCenterX() - 2, rect.getCenterY() + 40, + rect.getWidth() + 6, rect.getHeight() - 78 ); web_browser->setRect( html_rect ); web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE ); reshape( rect.getWidth(), rect.getHeight(), 1 ); @@ -474,9 +417,6 @@ LLPanelLogin::~LLPanelLogin() if ( gResponsePtr ) gResponsePtr->setParent( 0 ); - //// We know we're done with the image, so be rid of it. - //gTextureList.deleteImage( mLogoImage ); - if ( gFocusMgr.getDefaultKeyboardFocus() == this ) { gFocusMgr.setDefaultKeyboardFocus(NULL); @@ -518,14 +458,13 @@ void LLPanelLogin::draw() S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); - if ( mHtmlAvailable ) + if ( getChild("login_html")->getVisible()) { -#if !USE_VIEWER_AUTH // draw a background box in black - gl_rect_2d( 0, height - 264, width, 264, LLColor4( 0.0f, 0.0f, 0.0f, 1.f ) ); - // draw the bottom part of the background image - just the blue background to the native client UI + gl_rect_2d( 0, height - 264, width, 264, LLColor4::black ); + // draw the bottom part of the background image + // just the blue background to the native client UI mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight()); -#endif } else { @@ -560,12 +499,13 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) return TRUE; } - if ( KEY_F1 == key ) + //Singu TODO: Re-implement f1 help. + /*if ( KEY_F1 == key ) { llinfos << "Spawning HTML help window" << llendl; gViewerHtmlHelp.show(); return TRUE; - } + }*/ # if !LL_RELEASE_FOR_DOWNLOAD if ( KEY_F2 == key ) @@ -605,12 +545,6 @@ void LLPanelLogin::setFocus(BOOL b) // static void LLPanelLogin::giveFocus() { -#if USE_VIEWER_AUTH - if (sInstance) - { - sInstance->setFocus(TRUE); - } -#else if( sInstance ) { // Grab focus and move cursor to first blank input field @@ -621,12 +555,9 @@ void LLPanelLogin::giveFocus() BOOL have_pass = !pass.empty(); LLLineEditor* edit = NULL; - LLUICtrl* combo = NULL; - if (have_username) + LLComboBox* combo = NULL; + if (have_username && !have_pass) { - if(have_pass) - combo = sInstance->getChild("connect_btn"); - else // User saved his name but not his password. Move // focus to password field. edit = sInstance->getChild("password_edit"); @@ -647,7 +578,6 @@ void LLPanelLogin::giveFocus() combo->setFocus(TRUE); } } -#endif } @@ -770,7 +700,7 @@ void LLPanelLogin::getFields(std::string *firstname, } // static -void LLPanelLogin::getLocation(std::string &location) +/*void LLPanelLogin::getLocation(std::string &location) { if (!sInstance) { @@ -780,33 +710,14 @@ void LLPanelLogin::getLocation(std::string &location) LLComboBox* combo = sInstance->getChild("start_location_combo"); location = combo->getValue().asString(); -} +}*/ // static -void LLPanelLogin::refreshLocation( bool force_visible ) +void LLPanelLogin::updateLocationSelectorsVisibility() { - if (!sInstance) return; - -#if USE_VIEWER_AUTH - loadLoginPage(); -#else - LLComboBox* combo = sInstance->getChild("start_location_combo"); - - if (LLURLSimString::parse()) + if (sInstance) { - combo->setCurrentByIndex( 3 ); // BUG? Maybe 2? - combo->setTextEntry(LLURLSimString::sInstance.mSimString); - } - else - { - BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation"); - combo->setCurrentByIndex( login_last ? 1 : 0 ); - } - - BOOL show_start = TRUE; - - if ( ! force_visible ) - show_start = gSavedSettings.getBOOL("ShowStartLocation"); + BOOL show_start = gSavedSettings.getBOOL("ShowStartLocation"); // [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2009-07-08 (RLVa-1.0.0e) // TODO-RLVa: figure out some way to make this work with RLV_EXTENSION_STARTLOCATION @@ -818,11 +729,61 @@ void LLPanelLogin::refreshLocation( bool force_visible ) #endif // RLV_EXTENSION_STARTLOCATION // [/RLVa:KB] - sInstance->childSetVisible("start_location_combo", show_start); // maintain ShowStartLocation if legacy - sInstance->childSetVisible("start_location_text", show_start); - sInstance->childSetVisible("regionuri_edit",FALSE); // Do Not show regionuri box if legacy + sInstance->getChild("start_location_combo")->setVisible(show_start); // maintain ShowStartLocation if legacy + sInstance->getChild("start_location_text")->setVisible(show_start); + + bool show_server = true; + sInstance->getChild("grids_combo")->setVisible(show_server); + sInstance->getChild("grids_text")->setVisible(show_server); + sInstance->getChild("grids_btn")->setVisible(show_server); + } + +} -#endif +// static +void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) +{ + if (!sInstance) return; + + LL_DEBUGS("AppInit")<getChild("start_location_combo"); + /* + * Determine whether or not the new_start_slurl modifies the grid. + * + * Note that some forms that could be in the slurl are grid-agnostic., + * such as "home". Other forms, such as + * https://grid.example.com/region/Party%20Town/20/30/5 + * specify a particular grid; in those cases we want to change the grid + * and the grid selector to match the new value. + */ + enum LLSLURL::SLURL_TYPE new_slurl_type = new_start_slurl.getType(); + switch ( new_slurl_type ) + { + case LLSLURL::LOCATION: + { + location_combo->setCurrentByIndex( 2 ); + location_combo->setTextEntry(new_start_slurl.getLocationString()); + } + case LLSLURL::HOME_LOCATION: + location_combo->setCurrentByIndex( 0 ); // home location + break; + case LLSLURL::LAST_LOCATION: + location_combo->setCurrentByIndex( 1 ); // last location + break; + default: + LL_WARNS("AppInit")<<"invalid login slurl, using home"<setCurrentByIndex(1); // home location + break; + } + + updateLocationSelectorsVisibility(); +} + +void LLPanelLogin::setLocation(const LLSLURL& slurl) +{ + LL_DEBUGS("AppInit")<<"setting Location "<getRootView()->removeChild( LLPanelLogin::sInstance ); - - gFocusMgr.setDefaultKeyboardFocus(NULL); + LLPanelLogin::sInstance->getParent()->removeChild( LLPanelLogin::sInstance ); delete sInstance; sInstance = NULL; @@ -880,70 +839,39 @@ void LLPanelLogin::updateGridCombo() } } -// static -void LLPanelLogin::refreshLoginPage() -{ - if (!sInstance || (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP)) - return; - - sInstance->updateGridCombo(); - - sInstance->childSetVisible("create_new_account_text", - !gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()); - sInstance->childSetVisible("forgot_password_text", - !gHippoGridManager->getConnectedGrid()->getPasswordUrl().empty()); - - // kick off a request to grab the url manually - gResponsePtr = LLIamHereLogin::build(sInstance); - - std::string login_page = gHippoGridManager->getConnectedGrid()->getLoginPage(); - if (!login_page.empty()) { - LLHTTPClient::head(login_page, gResponsePtr.get()); - } else { - sInstance->setSiteIsAlive(false); - } -} - void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - sInstance->updateGridCombo(); - std::ostringstream login_uri; + sInstance->updateGridCombo(); - std::string login_page = gHippoGridManager->getConnectedGrid()->getLoginPage(); - if (login_page.empty()) + std::string login_page_str = gHippoGridManager->getConnectedGrid()->getLoginPage(); + if (login_page_str.empty()) { sInstance->setSiteIsAlive(false); return; } - - login_uri << login_page; - + // Use the right delimeter depending on how LLURI parses the URL - LLURI login_page_uri = LLURI(login_page); - std::string first_query_delimiter = "&"; - if (login_page_uri.queryMap().size() == 0) + LLURI login_page = LLURI(login_page_str); + LLSD params(login_page.queryMap()); + + LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; + + // Language + params["lang"] = LLUI::getLanguage(); + + // First Login? + if (gSavedSettings.getBOOL("FirstLoginThisInstall")) { - first_query_delimiter = "?"; - } - - // Language - std::string language = LLUI::getLanguage(); - login_uri << first_query_delimiter<<"lang=" << language; - - // First Login? - if (gSavedSettings.getBOOL("FirstLoginThisInstall")) + params["firstlogin"] = "TRUE"; // not bool: server expects string TRUE + } + + if(login_page_str.find("secondlife.com") == -1) { - login_uri << "&firstlogin=TRUE"; - } - - std::string version = llformat("%d.%d.%d (%d)", - gVersionMajor, gVersionMinor, gVersionPatch, gVersionBuild); - - if(login_page.find("secondlife.com") == -1) { - login_uri << "&channel=" << LLWeb::curlEscape(gVersionChannel); - login_uri << "&version=" << LLWeb::curlEscape(version); + params["version"]= llformat("%d.%d.%d (%d)", + gVersionMajor, gVersionMinor, gVersionPatch, gVersionBuild); + params["channel"] = gVersionChannel; } // Grid @@ -960,109 +888,35 @@ void LLPanelLogin::loadLoginPage() i = tmp.rfind('/'); if (i != std::string::npos) { tmp = tmp.substr(i+1); - login_uri << "&grid=" << LLWeb::curlEscape(tmp); + params["grid"] = tmp; } } } - else if (gHippoGridManager->getConnectedGrid()->isOpenSimulator()){ - login_uri << "&grid=" << gHippoGridManager->getConnectedGrid()->getGridNick(); + else if (gHippoGridManager->getConnectedGrid()->isOpenSimulator()) + { + params["grid"] = gHippoGridManager->getConnectedGrid()->getGridNick(); } else if (gHippoGridManager->getConnectedGrid()->getPlatform() == HippoGridInfo::PLATFORM_AURORA) { - login_uri << "&grid=" << LLWeb::curlEscape(LLViewerLogin::getInstance()->getGridLabel()); + params["grid"] = LLViewerLogin::getInstance()->getGridLabel(); } + + // add OS info + params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); + // Make an LLURI with this augmented info + LLURI login_uri(LLURI::buildHTTP(login_page.authority(), + login_page.path(), + params)); gViewerWindow->setMenuBackgroundColor(false, !LLViewerLogin::getInstance()->isInProductionGrid()); gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); - -#if USE_VIEWER_AUTH - LLURLSimString::sInstance.parse(); - - std::string location; - std::string region; - std::string password; - - if (LLURLSimString::parse()) - { - std::ostringstream oRegionStr; - location = "specify"; - oRegionStr << LLURLSimString::sInstance.mSimName << "/" << LLURLSimString::sInstance.mX << "/" - << LLURLSimString::sInstance.mY << "/" - << LLURLSimString::sInstance.mZ; - region = oRegionStr.str(); - } - else - { - if (gSavedSettings.getBOOL("LoginLastLocation")) - { - location = "last"; - } - else - { - location = "home"; - } - } - - std::string firstname, lastname; - - if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) - { - LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - firstname = cmd_line_login[0].asString(); - lastname = cmd_line_login[1].asString(); - password = cmd_line_login[2].asString(); - } - - if (firstname.empty()) - { - firstname = gSavedSettings.getString("FirstName"); - } - - if (lastname.empty()) - { - lastname = gSavedSettings.getString("LastName"); - } - - std::string curl_region = LLWeb::curlEscape(region); - - login_uri <<"firstname=" << firstname << - "&lastname=" << lastname << "&location=" << location << "®ion=" << curl_region; - - if (!password.empty()) - { - login_uri << "&password=" << password; - } - else if (!(password = load_password_from_disk()).empty()) - { - login_uri << "&password=$1$" << password; - } - if (gAutoLogin) - { - login_uri << "&auto_login=TRUE"; - } - if (gSavedSettings.getBOOL("ShowStartLocation")) - { - login_uri << "&show_start_location=TRUE"; - } - if (gSavedSettings.getBOOL("RememberPassword")) - { - login_uri << "&remember_password=TRUE"; - } - BOOL show_server = sInstance ? sInstance->mShowServerCombo : FALSE; - if (show_server || gSavedSettings.getBOOL("ForceShowGrid")) - { - login_uri << "&show_grid=TRUE"; - } -#endif - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - - if (web_browser->getCurrentNavUrl() != login_uri.str()) + if (web_browser->getCurrentNavUrl() != login_uri.asString()) { LL_DEBUGS("AppInit") << "loading: " << login_uri << LL_ENDL; - web_browser->navigateTo( login_uri.str(), "text/html" ); + web_browser->navigateTo( login_uri.asString(), "text/html" ); } } @@ -1165,29 +1019,6 @@ void LLPanelLogin::onClickGrids(void*) LLFloaterPreference::switchTab(LLPreferenceCore::TAB_GRIDS); } -// static -void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl, void*) -{ - gHippoGridManager->setCurrentGrid(ctrl->getValue()); - LLPanelLogin::refreshLoginPage(); -} - -// *NOTE: This function is dead as of 2008 August. I left it here in case -// we suddenly decide to put the Quit button back. JC -// static -void LLPanelLogin::onClickQuit(void*) -{ - if (sInstance && sInstance->mCallback) - { - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); - - sInstance->mCallback(1, sInstance->mCallbackData); - } -} - - // static void LLPanelLogin::onClickVersion(void*) { @@ -1218,6 +1049,49 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller) } } +// static +//void LLPanelLogin::updateServer() +void LLPanelLogin::refreshLoginPage() +{ + if (!sInstance || (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP)) + return; + + sInstance->updateGridCombo(); + + sInstance->childSetVisible("create_new_account_text", + !gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()); + sInstance->childSetVisible("forgot_password_text", + !gHippoGridManager->getConnectedGrid()->getPasswordUrl().empty()); + + // kick off a request to grab the url manually + gResponsePtr = LLIamHereLogin::build(sInstance); + + std::string login_page = gHippoGridManager->getConnectedGrid()->getLoginPage(); + if (!login_page.empty()) { + LLHTTPClient::head(login_page, gResponsePtr.get()); + } else { + sInstance->setSiteIsAlive(false); + } +} + +// static +//void LLPanelLogin::onSelectServer() +void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl) +{ + gHippoGridManager->setCurrentGrid(ctrl->getValue()); + LLPanelLogin::refreshLoginPage(); +} + +void LLPanelLogin::onLocationSLURL() +{ + LLComboBox* location_combo = getChild("start_location_combo"); + std::string location = location_combo->getValue().asString(); + LL_DEBUGS("AppInit")< #include "llmediactrl.h" // LLMediaCtrlObserver #include "llsavedlogins.h" +#include "llslurl.h" class LLUIImage; class LLComboBox; @@ -78,14 +79,12 @@ public: */ static void setFields(const LLSavedLoginEntry& entry, bool takeFocus = false); - //static void addServer(const std::string& server, S32 domain_name); - static void refreshLocation( bool force_visible ); + static void getFields(std::string *firstname, std::string *lastname, std::string *password); - static void getFields(std::string *firstname, std::string *lastname, - std::string *password); - - //static BOOL isGridComboDirty(); - static void getLocation(std::string &location); + static void setLocation(const LLSLURL& slurl); + + /// Call when preferences that control visibility may have changed + static void updateLocationSelectorsVisibility(); static void close(); @@ -102,19 +101,21 @@ public: // inherited from LLViewerMediaObserver /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + /// to be called from LLStartUp::setStartSLURL + static void onUpdateStartSLURL(const LLSLURL& new_start_slurl); + private: void reshapeBrowser(); + void onLocationSLURL(); + static void onClickConnect(void*); static void onClickNewAccount(); static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); static void onClickGrids(void*); - static void onSelectGrid(LLUICtrl *ctrl, void*); - static void onClickQuit(void*); + static void onSelectGrid(LLUICtrl *ctrl); static void onClickVersion(void*); static void onClickForgotPassword(); static void onPassKey(LLLineEditor* caller); - //static void onSelectServer(LLUICtrl*, void*); - //static void onServerComboLostFocus(LLFocusableElement*, void*); static void onSelectLoginEntry(LLUICtrl*, void*); void onLoginComboLostFocus(LLComboBox* combo_box); static void onNameCheckChanged(LLUICtrl* ctrl, void* data); @@ -154,7 +155,6 @@ private: static LLPanelLogin* sInstance; static BOOL sCapslockDidNotification; - BOOL mHtmlAvailable; LLSavedLogins mLoginHistoryData; }; diff --git a/indra/newview/llpanelmediahud.cpp b/indra/newview/llpanelmediahud.cpp deleted file mode 100644 index ad47eb732..000000000 --- a/indra/newview/llpanelmediahud.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/** - * @file llpanelmsgs.cpp - * @brief Message popup preferences panel - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-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" - -//LLPanelMediaHUD -#include "llagent.h" -#include "llagentcamera.h" -#include "llparcel.h" -#include "llpanel.h" -#include "llselectmgr.h" -#include "llrender.h" -#include "lldrawable.h" -#include "llviewerwindow.h" -#include "llwindow.h" -#include "lluictrlfactory.h" -#include "llbutton.h" -#include "llface.h" -#include "llhudview.h" -#include "lliconctrl.h" -#include "lltoolpie.h" -#include "llviewercamera.h" -#include "llpanelmediahud.h" -#include "llpluginclassmedia.h" -#include "llviewercontrol.h" -#include "llviewerparcelmgr.h" -#include "llviewermedia.h" -#include "llviewermediafocus.h" -#include "llvovolume.h" -#include "llweb.h" - -glh::matrix4f glh_get_current_modelview(); -glh::matrix4f glh_get_current_projection(); - -const F32 ZOOM_NEAR_PADDING = 1.0f; -const F32 ZOOM_MEDIUM_PADDING = 1.2f; -const F32 ZOOM_FAR_PADDING = 1.5f; - -// -// LLPanelMediaHUD -// - -LLPanelMediaHUD::LLPanelMediaHUD(viewer_media_t media_impl) - : mMediaImpl(media_impl) -{ - mMediaFocus = false; - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_hud.xml"); - mMouseMoveTimer.reset(); - mFadeTimer.stop(); - mCurrentZoom = ZOOM_NONE; - mScrollState = SCROLL_NONE; -} -LLPanelMediaHUD::~LLPanelMediaHUD() -{ - mMediaImpl = NULL; -} - -BOOL LLPanelMediaHUD::postBuild() -{ - LLButton* close_btn = getChild("close"); - close_btn->setClickedCallback(onClickClose, this); - - LLButton* back_btn = getChild("back"); - back_btn->setClickedCallback(onClickBack, this); - - LLButton* fwd_btn = getChild("fwd"); - fwd_btn->setClickedCallback(onClickForward, this); - - LLButton* home_btn = getChild("home"); - home_btn->setClickedCallback(onClickHome, this); - - LLButton* stop_btn = getChild("stop"); - stop_btn->setClickedCallback(onClickStop, this); - - LLButton* media_stop_btn = getChild("media_stop"); - media_stop_btn->setClickedCallback(onClickStop, this); - - LLButton* reload_btn = getChild("reload"); - reload_btn->setClickedCallback(onClickReload, this); - - LLButton* play_btn = getChild("play"); - play_btn->setClickedCallback(onClickPlay, this); - - LLButton* pause_btn = getChild("pause"); - pause_btn->setClickedCallback(onClickPause, this); - - LLButton* open_btn = getChild("new_window"); - open_btn->setClickedCallback(onClickOpen, this); - - LLButton* zoom_btn = getChild("zoom_frame"); - zoom_btn->setClickedCallback(onClickZoom, this); - - LLButton* open_btn_h = getChild("new_window_hover"); - open_btn_h->setClickedCallback(onClickOpen, this); - - LLButton* zoom_btn_h = getChild("zoom_frame_hover"); - zoom_btn_h->setClickedCallback(onClickZoom, this); - - LLButton* scroll_up_btn = getChild("scrollup"); - scroll_up_btn->setClickedCallback(onScrollUp, this); - scroll_up_btn->setHeldDownCallback(onScrollUpHeld, this); - scroll_up_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_left_btn = getChild("scrollleft"); - scroll_left_btn->setClickedCallback(onScrollLeft, this); - scroll_left_btn->setHeldDownCallback(onScrollLeftHeld, this); - scroll_left_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_right_btn = getChild("scrollright"); - scroll_right_btn->setClickedCallback(onScrollRight, this); - scroll_right_btn->setHeldDownCallback(onScrollLeftHeld, this); - scroll_right_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_down_btn = getChild("scrolldown"); - scroll_down_btn->setClickedCallback(onScrollDown, this); - scroll_down_btn->setHeldDownCallback(onScrollDownHeld, this); - scroll_down_btn->setMouseUpCallback(onScrollStop, this); - - mMouseInactiveTime = gSavedSettings.getF32("MediaControlTimeout"); - mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); - - mCurrentZoom = ZOOM_NONE; - // clicks on HUD buttons do not remove keyboard focus from media - setIsChrome(TRUE); - return TRUE; -} - -void LLPanelMediaHUD::updateShape() -{ - const S32 MIN_HUD_WIDTH=200; - const S32 MIN_HUD_HEIGHT=120; - - LLPluginClassMedia* media_plugin = NULL; - if(mMediaImpl.notNull() && mMediaImpl->hasMedia()) - { - media_plugin = mMediaImpl->getMediaPlugin(); - } - - // Early out for no media plugin - if(media_plugin == NULL) - { - setVisible(FALSE); - return; - } - - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - bool can_navigate = parcel->getMediaAllowNavigate(); - - // LLObjectSelectionHandle selection = LLViewerMediaFocus::getInstance()->getSelection(); - - LLSelectNode* nodep = mMediaFocus ? LLSelectMgr::getInstance()->getSelection()->getFirstNode() : LLSelectMgr::getInstance()->getHoverNode(); - if(! nodep) - { - return; - } - setVisible(FALSE); - LLViewerObject* objectp = nodep->getObject(); - - if (objectp) - { - - // Set the state of the buttons - LLButton* back_btn = getChild("back"); - LLButton* fwd_btn = getChild("fwd"); - LLButton* reload_btn = getChild("reload"); - LLButton* play_btn = getChild("play"); - LLButton* pause_btn = getChild("pause"); - LLButton* stop_btn = getChild("stop"); - LLButton* media_stop_btn = getChild("media_stop"); - LLButton* home_btn = getChild("home"); - LLButton* close_btn = getChild("close"); - LLButton* open_btn = getChild("new_window"); - LLPanel* media_focused_panel = getChild("media_focused_controls"); - LLPanel* media_hover_panel = getChild("media_hover_controls"); - back_btn->setVisible(true); - fwd_btn->setVisible(true); - reload_btn->setVisible(true); - stop_btn->setVisible(false); - home_btn->setVisible(true); - close_btn->setVisible(true); - open_btn->setVisible(true); - - - if(mMediaFocus) - { - back_btn->setEnabled(mMediaImpl->canNavigateBack() && can_navigate); - fwd_btn->setEnabled(mMediaImpl->canNavigateForward() && can_navigate); - stop_btn->setEnabled(can_navigate); - home_btn->setEnabled(can_navigate); - LLPluginClassMediaOwner::EMediaStatus result = media_plugin->getStatus(); - - if(media_plugin->pluginSupportsMediaTime()) - { - reload_btn->setEnabled(FALSE); - reload_btn->setVisible(FALSE); - media_stop_btn->setVisible(TRUE); - home_btn->setVisible(FALSE); - back_btn->setEnabled(TRUE); - fwd_btn->setEnabled(TRUE); - switch(result) - { - case LLPluginClassMediaOwner::MEDIA_PLAYING: - play_btn->setEnabled(FALSE); - play_btn->setVisible(FALSE); - pause_btn->setEnabled(TRUE); - pause_btn->setVisible(TRUE); - media_stop_btn->setEnabled(TRUE); - break; - case LLPluginClassMediaOwner::MEDIA_PAUSED: - default: - pause_btn->setEnabled(FALSE); - pause_btn->setVisible(FALSE); - play_btn->setEnabled(TRUE); - play_btn->setVisible(TRUE); - media_stop_btn->setEnabled(FALSE); - break; - } - } - else - { - play_btn->setVisible(FALSE); - pause_btn->setVisible(FALSE); - media_stop_btn->setVisible(FALSE); - if(result == LLPluginClassMediaOwner::MEDIA_LOADING) - { - reload_btn->setEnabled(FALSE); - reload_btn->setVisible(FALSE); - stop_btn->setEnabled(TRUE); - stop_btn->setVisible(TRUE); - } - else - { - reload_btn->setEnabled(TRUE); - reload_btn->setVisible(TRUE); - stop_btn->setEnabled(FALSE); - stop_btn->setVisible(FALSE); - } - } - } - media_focused_panel->setVisible(mMediaFocus); - media_hover_panel->setVisible(!mMediaFocus); - - if(media_plugin == NULL) - // Handle Scrolling - switch (mScrollState) - { - case SCROLL_UP: - media_plugin->scrollEvent(0, -1, MASK_NONE); - break; - case SCROLL_DOWN: - media_plugin->scrollEvent(0, 1, MASK_NONE); - break; - case SCROLL_LEFT: - mMediaImpl->handleKeyHere(KEY_LEFT, MASK_NONE); - break; - case SCROLL_RIGHT: - mMediaImpl->handleKeyHere(KEY_RIGHT, MASK_NONE); - break; - case SCROLL_NONE: - default: - break; - } - LLBBox screen_bbox; - setVisible(TRUE); - glh::matrix4f mat = glh_get_current_projection()*glh_get_current_modelview(); - std::vector::iterator vert_it; - std::vector::iterator vert_end; - std::vector vect_face; - - LLVolume* volume = objectp->getVolume(); - - if (volume) - { - const LLVolumeFace& vf = volume->getVolumeFace(nodep->getLastSelectedTE()); - - const LLVector3* ext = (LLVector3*)vf.mExtents->getF32ptr(); - - LLVector3 center = (ext[0]+ext[1])*0.5f; - LLVector3 size = (ext[1]-ext[0])*0.5f; - LLVector3 vert[] = - { - center + size.scaledVec(LLVector3(1,1,1)), - center + size.scaledVec(LLVector3(-1,1,1)), - center + size.scaledVec(LLVector3(1,-1,1)), - center + size.scaledVec(LLVector3(-1,-1,1)), - center + size.scaledVec(LLVector3(1,1,-1)), - center + size.scaledVec(LLVector3(-1,1,-1)), - center + size.scaledVec(LLVector3(1,-1,-1)), - center + size.scaledVec(LLVector3(-1,-1,-1)), - }; - - LLVOVolume* vo = (LLVOVolume*) objectp; - - for (U32 i = 0; i < 8; i++) - { - vect_face.push_back(vo->volumePositionToAgent(vert[i])); - } - } - vert_it = vect_face.begin(); - vert_end = vect_face.end(); - - LLVector3 min = LLVector3(1,1,1); - LLVector3 max = LLVector3(-1,-1,-1); - for(; vert_it != vert_end; ++vert_it) - { - // project silhouette vertices into screen space - glh::vec3f screen_vert = glh::vec3f(vert_it->mV); - mat.mult_matrix_vec(screen_vert); - - // add to screenspace bounding box - update_min_max(min, max, LLVector3(screen_vert.v)); - } - - LLCoordGL screen_min; - screen_min.mX = llround((F32)gViewerWindow->getWindowWidth() * (min.mV[VX] + 1.f) * 0.5f); - screen_min.mY = llround((F32)gViewerWindow->getWindowHeight() * (min.mV[VY] + 1.f) * 0.5f); - - LLCoordGL screen_max; - screen_max.mX = llround((F32)gViewerWindow->getWindowWidth() * (max.mV[VX] + 1.f) * 0.5f); - screen_max.mY = llround((F32)gViewerWindow->getWindowHeight() * (max.mV[VY] + 1.f) * 0.5f); - - // grow panel so that screenspace bounding box fits inside "media_region" element of HUD - LLRect media_hud_rect; - getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_hud_rect); - LLView* media_region = getChild("media_region"); - media_hud_rect.mLeft -= media_region->getRect().mLeft; - media_hud_rect.mBottom -= media_region->getRect().mBottom; - media_hud_rect.mTop += getRect().getHeight() - media_region->getRect().mTop; - media_hud_rect.mRight += getRect().getWidth() - media_region->getRect().mRight; - - // keep all parts of HUD on-screen - media_hud_rect.intersectWith(getParent()->getLocalRect()); - - // If we had to clip the rect, don't display the border - childSetVisible("bg_image", false); - - // clamp to minimum size, keeping centered - media_hud_rect.setCenterAndSize(media_hud_rect.getCenterX(), media_hud_rect.getCenterY(), - llmax(MIN_HUD_WIDTH, media_hud_rect.getWidth()), llmax(MIN_HUD_HEIGHT, media_hud_rect.getHeight())); - - setShape(media_hud_rect); - - // Test mouse position to see if the cursor is stationary - LLCoordWindow cursor_pos_window; - getWindow()->getCursorPosition(&cursor_pos_window); - - // If last pos is not equal to current pos, the mouse has moved - // We need to reset the timer, and make sure the panel is visible - if(cursor_pos_window.mX != mLastCursorPos.mX || - cursor_pos_window.mY != mLastCursorPos.mY || - mScrollState != SCROLL_NONE) - { - mMouseMoveTimer.start(); - mLastCursorPos = cursor_pos_window; - } - - // Mouse has been stationary, but not for long enough to fade the UI - if(mMouseMoveTimer.getElapsedTimeF32() < mMouseInactiveTime) - { - // If we have started fading, reset the alpha values - if(mFadeTimer.getStarted()) - { - F32 alpha = 1.0f; - setAlpha(alpha); - mFadeTimer.stop(); - } - } - // If we need to start fading the UI (and we have not already started) - else if (!mFadeTimer.getStarted()) - { - mFadeTimer.start(); - } - } -} -/*virtual*/ -void LLPanelMediaHUD::draw() -{ - if(mFadeTimer.getStarted()) - { - if(mFadeTimer.getElapsedTimeF32() >= mControlFadeTime) - { - setVisible(FALSE); - } - else - { - F32 time = mFadeTimer.getElapsedTimeF32(); - F32 alpha = llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); - setAlpha(alpha); - } - } - LLPanel::draw(); -} -void LLPanelMediaHUD::setAlpha(F32 alpha) -{ - LLViewQuery query; - - LLView* query_view = mMediaFocus ? getChildView("media_focused_controls") : getChildView("media_hover_controls"); - viewList_t children = query(query_view); - for (viewList_t::iterator child_iter = children.begin(); - child_iter != children.end(); ++child_iter) - { - LLUICtrl* ctrl = dynamic_cast(*child_iter); - if (ctrl) - ctrl->setAlpha(alpha); - } - - LLPanel::setAlpha(alpha); -} -BOOL LLPanelMediaHUD::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); -} -bool LLPanelMediaHUD::isMouseOver() -{ - if( ! getVisible() ) - { - return false; - } - LLRect screen_rect; - LLCoordWindow cursor_pos_window; - getWindow()->getCursorPosition(&cursor_pos_window); - - localRectToScreen(getLocalRect(), &screen_rect); - // screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); - - if(screen_rect.pointInRect(cursor_pos_window.mX, cursor_pos_window.mY)) - { - return true; - } - return false; -} - -//static -void LLPanelMediaHUD::onClickClose(void* user_data) -{ - LLViewerMediaFocus::getInstance()->setFocusFace(FALSE, NULL, 0, NULL); - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mCurrentZoom != ZOOM_NONE) - { - // gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - this_panel->mCurrentZoom = ZOOM_NONE; - } - this_panel->setVisible(FALSE); - -} - -//static -void LLPanelMediaHUD::onClickBack(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(-2.0); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_back(); - } - - } -} -//static -void LLPanelMediaHUD::onClickForward(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(2.0); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_forward(); - } - } -} -//static -void LLPanelMediaHUD::onClickHome(void* user_data) -{ - //LLViewerMedia::navigateHome(); - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->navigateHome(); - } -} -//static -void LLPanelMediaHUD::onClickOpen(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - LLWeb::loadURL(this_panel->mMediaImpl->getMediaURL()); - } -} -//static -void LLPanelMediaHUD::onClickReload(void* user_data) -{ - //LLViewerMedia::navigateHome(); - LLPanelMediaHUD* this_panel = static_cast (user_data); - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); - if(objectp && this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->navigateTo(objectp->getMediaURL()); - } -} -//static -void LLPanelMediaHUD::onClickPlay(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(); - } -} -//static -void LLPanelMediaHUD::onClickPause(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->pause(); - } -} -//static -void LLPanelMediaHUD::onClickStop(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->stop(); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_stop(); - } - } -} -//static -void LLPanelMediaHUD::onClickZoom(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->nextZoomLevel(); -} -void LLPanelMediaHUD::nextZoomLevel() -{ - F32 zoom_padding = 0.0f; - S32 last_zoom_level = (S32)mCurrentZoom; - mCurrentZoom = (EZoomLevel)((last_zoom_level + 1) % (S32)ZOOM_END); - - switch (mCurrentZoom) - { - case ZOOM_NONE: - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - break; - } - case ZOOM_MEDIUM: - { - zoom_padding = ZOOM_MEDIUM_PADDING; - break; - } - default: - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - break; - } - } - - if (zoom_padding > 0.0f) - LLViewerMediaFocus::getInstance()->setCameraZoom(zoom_padding); -} -void LLPanelMediaHUD::onScrollUp(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->scrollEvent(0, -1, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollUpHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_UP; -} -void LLPanelMediaHUD::onScrollRight(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->handleKeyHere(KEY_RIGHT, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollRightHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_RIGHT; -} - -void LLPanelMediaHUD::onScrollLeft(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->handleKeyHere(KEY_LEFT, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollLeftHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_LEFT; -} - -void LLPanelMediaHUD::onScrollDown(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->scrollEvent(0, 1, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollDownHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_DOWN; -} - -void LLPanelMediaHUD::onScrollStop(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_NONE; -} diff --git a/indra/newview/llpanelmediahud.h b/indra/newview/llpanelmediahud.h deleted file mode 100644 index 1d1f5e1be..000000000 --- a/indra/newview/llpanelmediahud.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @file llpanelmediahud.h - * @brief Media hud panel - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-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_PANELMEDIAHUD_H -#define LL_PANELMEDIAHUD_H - -#include "llpanel.h" -#include "llviewermedia.h" - -#include "llcoord.h" - -class LLViewerMediaImpl; - -class LLPanelMediaHUD : public LLPanel -{ -public: - LLPanelMediaHUD(viewer_media_t media_impl); - virtual ~LLPanelMediaHUD(); - /*virtual*/ BOOL postBuild(); - virtual void draw(); - virtual void setAlpha(F32 alpha); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - void updateShape(); - bool isMouseOver(); - void setMediaFocus(bool b) { mMediaFocus = b; } - void nextZoomLevel(); - void resetZoomLevel() { mCurrentZoom = ZOOM_NONE; } - - LLHandle getHandle() const { return getDerivedHandle(); } - - void setMediaImpl(viewer_media_t media_impl) { mMediaImpl = media_impl; } - - - enum EZoomLevel - { - ZOOM_NONE = 0, - ZOOM_MEDIUM = 1, - ZOOM_END - }; - enum EScrollDir - { - SCROLL_UP = 0, - SCROLL_DOWN, - SCROLL_LEFT, - SCROLL_RIGHT, - SCROLL_NONE - }; - -private: - static void onClickClose(void* user_data); - static void onClickBack(void* user_data); - static void onClickForward(void* user_data); - static void onClickHome(void* user_data); - static void onClickOpen(void* user_data); - static void onClickReload(void* user_data); - static void onClickPlay(void* user_data); - static void onClickPause(void* user_data); - static void onClickStop(void* user_data); - static void onClickZoom(void* user_data); - static void onScrollUp(void* user_data); - static void onScrollUpHeld(void* user_data); - static void onScrollLeft(void* user_data); - static void onScrollLeftHeld(void* user_data); - static void onScrollRight(void* user_data); - static void onScrollRightHeld(void* user_data); - static void onScrollDown(void* user_data); - static void onScrollDownHeld(void* user_data); - static void onScrollStop(void* user_data); - - bool mMediaFocus; - LLMatrix4 mLastCameraMat; - EZoomLevel mCurrentZoom; - EScrollDir mScrollState; - LLCoordWindow mLastCursorPos; - LLFrameTimer mMouseMoveTimer; - LLFrameTimer mFadeTimer; - F32 mMouseInactiveTime; - F32 mControlFadeTime; - viewer_media_t mMediaImpl; -}; - -#endif // LL_PANELMEDIAHUD_H diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp new file mode 100644 index 000000000..0b2f52ed7 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -0,0 +1,1335 @@ +/** + * @file llpanelmsgs.cpp + * @brief Message popup preferences panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-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 "llagent.h" +#include "llagentcamera.h" +#include "llparcel.h" +#include "llpanel.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llrender.h" +#include "lldrawable.h" +#include "llviewerwindow.h" +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llface.h" +#include "llcombobox.h" +#include "llslider.h" +#include "llhudview.h" +#include "lliconctrl.h" +#include "lltoolpie.h" +#include "llviewercamera.h" +#include "llviewerobjectlist.h" +#include "llpanelprimmediacontrols.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "llsliderctrl.h" +#include "llstring.h" +#include "llviewercontrol.h" +#include "llviewerdisplay.h" +#include "llviewerparcelmgr.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" +#include "llvovolume.h" +#include "llweb.h" +#include "llwindow.h" +#include "llfloatertools.h" // to enable hide if build tools are up +#include "llvector4a.h" +#include "lllayoutstack.h" + +// Functions pulled from pipeline.cpp +glh::matrix4f glh_get_current_modelview(); +glh::matrix4f glh_get_current_projection(); +// Functions pulled from llviewerdisplay.cpp +bool get_hud_matrices(glh::matrix4f &proj, glh::matrix4f &model); + +// Warning: make sure these two match! +const LLPanelPrimMediaControls::EZoomLevel LLPanelPrimMediaControls::kZoomLevels[] = { ZOOM_NONE, ZOOM_MEDIUM }; +const int LLPanelPrimMediaControls::kNumZoomLevels = 2; + +// +// LLPanelPrimMediaControls +// + +LLPanelPrimMediaControls::LLPanelPrimMediaControls() : + mAlpha(1.f), + mCurrentURL(""), + mPreviousURL(""), + mPauseFadeout(false), + mUpdateSlider(true), + mClearFaceOnFade(false), + mCurrentRate(0.0), + mMovieDuration(0.0), + mTargetObjectID(LLUUID::null), + mTargetObjectFace(0), + mTargetImplID(LLUUID::null), + mTargetObjectNormal(LLVector3::zero), + mZoomObjectID(LLUUID::null), + mZoomObjectFace(0), + mVolumeSliderVisible(0), + mHideImmediately(false) +{ + //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_hud.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_prim_media_controls.xml"); + mInactivityTimer.reset(); + mFadeTimer.stop(); + mCurrentZoom = ZOOM_NONE; + mScrollState = SCROLL_NONE; + + mPanelHandle.bind(this); + + mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); + mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); +} + +LLPanelPrimMediaControls::~LLPanelPrimMediaControls() +{ +} + +BOOL LLPanelPrimMediaControls::postBuild() +{ + mMediaRegion = getChild("media_region"); + mBackCtrl = getChild("back"); + mFwdCtrl = getChild("fwd"); + mReloadCtrl = getChild("reload"); + mPlayCtrl = getChild("play"); + mPauseCtrl = getChild("pause"); + mStopCtrl = getChild("stop"); + mMediaStopCtrl = getChild("media_stop"); + mHomeCtrl = getChild("home"); + mUnzoomCtrl = getChild("close"); // This is actually "unzoom" + mOpenCtrl = getChild("new_window"); + mZoomCtrl = getChild("zoom_frame"); + mMediaProgressPanel = getChild("media_progress_indicator"); + mMediaProgressBar = getChild("media_progress_bar"); + mMediaAddressCtrl = getChild("media_address"); + mMediaAddress = getChild("media_address_url"); + mMediaPlaySliderPanel = getChild("media_play_position"); + mMediaPlaySliderCtrl = getChild("media_play_slider"); + mSkipFwdCtrl = getChild("skip_forward"); + mSkipBackCtrl = getChild("skip_back"); + mVolumeCtrl = getChild("media_volume"); + mMuteBtn = getChild("media_mute_button"); + mVolumeSliderCtrl = getChild("volume_slider"); + mWhitelistIcon = getChild("media_whitelist_flag"); + mSecureLockIcon = getChild("media_secure_lock_flag"); + mMediaControlsStack = getChild("media_controls"); + mLeftBookend = getChild("left_bookend"); + mRightBookend = getChild("right_bookend"); + mBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); + mVolumeSliderBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); + LLStringUtil::convertToF32(getString("skip_step"), mSkipStep); + LLStringUtil::convertToS32(getString("min_width"), mMinWidth); + LLStringUtil::convertToS32(getString("min_height"), mMinHeight); + LLStringUtil::convertToF32(getString("zoom_near_padding"), mZoomNearPadding); + LLStringUtil::convertToF32(getString("zoom_medium_padding"), mZoomMediumPadding); + LLStringUtil::convertToF32(getString("zoom_far_padding"), mZoomFarPadding); + LLStringUtil::convertToS32(getString("top_world_view_avoid_zone"), mTopWorldViewAvoidZone); + + // These are currently removed...but getChild creates a "dummy" widget. + // This class handles them missing. + mMediaPanelScroll = findChild("media_panel_scroll"); + mScrollUpCtrl = findChild("scrollup"); + mScrollLeftCtrl = findChild("scrollleft"); + mScrollRightCtrl = findChild("scrollright"); + mScrollDownCtrl = findChild("scrolldown"); + + if (mScrollUpCtrl) + { + mScrollUpCtrl->setClickedCallback(onScrollUp, this); + mScrollUpCtrl->setHeldDownCallback(onScrollUpHeld, this); + mScrollUpCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollLeftCtrl) + { + mScrollLeftCtrl->setClickedCallback(onScrollLeft, this); + mScrollLeftCtrl->setHeldDownCallback(onScrollLeftHeld, this); + mScrollLeftCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollRightCtrl) + { + mScrollRightCtrl->setClickedCallback(onScrollRight, this); + mScrollRightCtrl->setHeldDownCallback(onScrollRightHeld, this); + mScrollRightCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollDownCtrl) + { + mScrollDownCtrl->setClickedCallback(onScrollDown, this); + mScrollDownCtrl->setHeldDownCallback(onScrollDownHeld, this); + mScrollDownCtrl->setMouseUpCallback(onScrollStop, this); + } + + mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); + + gAgent.setMouselookModeInCallback(boost::bind(&LLPanelPrimMediaControls::onMouselookModeIn, this)); + + mCurrentZoom = ZOOM_NONE; + // clicks on buttons do not remove keyboard focus from media + setIsChrome(TRUE); + return TRUE; +} + +void LLPanelPrimMediaControls::setMediaFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull() && objectp.notNull()) + { + LLUUID prev_id = mTargetImplID; + mTargetImplID = media_impl->getMediaTextureID(); + mTargetObjectID = objectp->getID(); + mTargetObjectFace = face; + mTargetObjectNormal = pick_normal; + mClearFaceOnFade = false; + + if (prev_id != mTargetImplID) + mVolumeSliderCtrl->setValue(media_impl->getVolume()); + } + else + { + // This happens on a timer now. +// mTargetImplID = LLUUID::null; +// mTargetObjectID = LLUUID::null; +// mTargetObjectFace = 0; + mClearFaceOnFade = true; + } + + updateShape(); +} + +void LLPanelPrimMediaControls::focusOnTarget() +{ + // Sets the media focus to the current target of the LLPanelPrimMediaControls. + // This is how we transition from hover to focus when the user clicks on a control. + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if(media_impl) + { + if(!media_impl->hasFocus()) + { + // The current target doesn't have media focus -- focus on it. + LLViewerObject* objectp = getTargetObject(); + LLViewerMediaFocus::getInstance()->setFocusFace(objectp, mTargetObjectFace, media_impl, mTargetObjectNormal); + } + } +} + +LLViewerMediaImpl* LLPanelPrimMediaControls::getTargetMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mTargetImplID); +} + +LLViewerObject* LLPanelPrimMediaControls::getTargetObject() +{ + return gObjectList.findObject(mTargetObjectID); +} + +LLPluginClassMedia* LLPanelPrimMediaControls::getTargetMediaPlugin() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if(impl && impl->hasMedia()) + { + return impl->getMediaPlugin(); + } + + return NULL; +} + +void LLPanelPrimMediaControls::updateShape() +{ + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + LLViewerObject* objectp = getTargetObject(); + + if(!media_impl || gFloaterTools->getVisible()) + { + setVisible(FALSE); + return; + } + + LLPluginClassMedia* media_plugin = NULL; + if(media_impl->hasMedia()) + { + media_plugin = media_impl->getMediaPlugin(); + } + + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + bool can_navigate = parcel->getMediaAllowNavigate(); + bool enabled = false; + bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace); + // There is no such thing as "has_focus" being different from normal controls set + // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' + // to 'true' (or, actually, we use a setting) + bool has_focus = (gSavedSettings.getBOOL("PrimMediaControlsUseHoverControlSet")) ? media_impl->hasFocus() : true; + setVisible(enabled); + + if (objectp) + { + bool mini_controls = false; + LLMediaEntry *media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); + if (media_data && NULL != dynamic_cast(objectp)) + { + // Don't show the media controls if we do not have permissions + enabled = dynamic_cast(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); + mini_controls = (LLMediaEntry::MINI == media_data->getControls()); + } + const bool is_hud = objectp->isHUDAttachment(); + + // + // Set the state of the buttons + // + + // XXX RSP: TODO: FIXME: clean this up so that it is clearer what mode we are in, + // and that only the proper controls get made visible/enabled according to that mode. + mBackCtrl->setVisible(has_focus); + mFwdCtrl->setVisible(has_focus); + mReloadCtrl->setVisible(has_focus); + mStopCtrl->setVisible(false); + mHomeCtrl->setVisible(has_focus); + mZoomCtrl->setVisible(!is_zoomed); + mUnzoomCtrl->setVisible(is_zoomed); + mOpenCtrl->setVisible(true); + mMediaAddressCtrl->setVisible(has_focus && !mini_controls); + mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); + mVolumeCtrl->setVisible(false); + + mWhitelistIcon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); + // Disable zoom if HUD + mZoomCtrl->setEnabled(!is_hud); + mUnzoomCtrl->setEnabled(!is_hud); + mSecureLockIcon->setVisible(false); + mCurrentURL = media_impl->getCurrentMediaURL(); + + mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); + mFwdCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateForward() && can_navigate); + mStopCtrl->setEnabled(has_focus && can_navigate); + mHomeCtrl->setEnabled(has_focus && can_navigate); + LLPluginClassMediaOwner::EMediaStatus result = ((media_impl != NULL) && media_impl->hasMedia()) ? media_plugin->getStatus() : LLPluginClassMediaOwner::MEDIA_NONE; + + mVolumeCtrl->setVisible(has_focus); + mVolumeCtrl->setEnabled(has_focus); + mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); + mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); + + if(media_plugin && media_plugin->pluginSupportsMediaTime()) + { + mReloadCtrl->setEnabled(false); + mReloadCtrl->setVisible(false); + mMediaStopCtrl->setVisible(has_focus); + mHomeCtrl->setVisible(has_focus); + mBackCtrl->setVisible(false); + mFwdCtrl->setVisible(false); + mMediaAddressCtrl->setVisible(false); + mMediaAddressCtrl->setEnabled(false); + mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); + mMediaPlaySliderPanel->setEnabled(has_focus && !mini_controls); + mSkipFwdCtrl->setVisible(has_focus && !mini_controls); + mSkipFwdCtrl->setEnabled(has_focus && !mini_controls); + mSkipBackCtrl->setVisible(has_focus && !mini_controls); + mSkipBackCtrl->setEnabled(has_focus && !mini_controls); + + mVolumeCtrl->setVisible(has_focus); + mVolumeCtrl->setEnabled(has_focus); + mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); + mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); + + mWhitelistIcon->setVisible(false); + mSecureLockIcon->setVisible(false); + if (mMediaPanelScroll) + { + mMediaPanelScroll->setVisible(false); + mScrollUpCtrl->setVisible(false); + mScrollDownCtrl->setVisible(false); + mScrollRightCtrl->setVisible(false); + mScrollDownCtrl->setVisible(false); + } + + F32 volume = media_impl->getVolume(); + // movie's url changed + if(mCurrentURL!=mPreviousURL) + { + mMovieDuration = media_plugin->getDuration(); + mPreviousURL = mCurrentURL; + } + + if(mMovieDuration == 0) + { + mMovieDuration = media_plugin->getDuration(); + mMediaPlaySliderCtrl->setValue(0); + mMediaPlaySliderCtrl->setEnabled(false); + } + // TODO: What if it's not fully loaded + + if(mUpdateSlider && mMovieDuration!= 0) + { + F64 current_time = media_plugin->getCurrentTime(); + F32 percent = current_time / mMovieDuration; + mMediaPlaySliderCtrl->setValue(percent); + mMediaPlaySliderCtrl->setEnabled(true); + } + + // video vloume + if(volume <= 0.0) + { + mMuteBtn->setToggleState(true); + } + else if (volume >= 1.0) + { + mMuteBtn->setToggleState(false); + } + else + { + mMuteBtn->setToggleState(false); + } + + switch(result) + { + case LLPluginClassMediaOwner::MEDIA_PLAYING: + mPlayCtrl->setEnabled(FALSE); + mPlayCtrl->setVisible(FALSE); + mPauseCtrl->setEnabled(TRUE); + mPauseCtrl->setVisible(has_focus); + + break; + case LLPluginClassMediaOwner::MEDIA_PAUSED: + default: + mPauseCtrl->setEnabled(FALSE); + mPauseCtrl->setVisible(FALSE); + mPlayCtrl->setEnabled(TRUE); + mPlayCtrl->setVisible(has_focus); + break; + } + } + else // web based + { + if(media_plugin) + { + mCurrentURL = media_plugin->getLocation(); + } + else + { + mCurrentURL.clear(); + } + + mPlayCtrl->setVisible(FALSE); + mPauseCtrl->setVisible(FALSE); + mMediaStopCtrl->setVisible(FALSE); + mMediaAddressCtrl->setVisible(has_focus && !mini_controls); + mMediaAddressCtrl->setEnabled(has_focus && !mini_controls); + mMediaPlaySliderPanel->setVisible(FALSE); + mMediaPlaySliderPanel->setEnabled(FALSE); + mSkipFwdCtrl->setVisible(FALSE); + mSkipFwdCtrl->setEnabled(FALSE); + mSkipBackCtrl->setVisible(FALSE); + mSkipBackCtrl->setEnabled(FALSE); + + if(media_impl->getVolume() <= 0.0) + { + mMuteBtn->setToggleState(true); + } + else + { + mMuteBtn->setToggleState(false); + } + + if (mMediaPanelScroll) + { + mMediaPanelScroll->setVisible(has_focus); + mScrollUpCtrl->setVisible(has_focus); + mScrollDownCtrl->setVisible(has_focus); + mScrollRightCtrl->setVisible(has_focus); + mScrollDownCtrl->setVisible(has_focus); + } + // TODO: get the secure lock bool from media plug in + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + mSecureLockIcon->setVisible(has_focus); + } + + if(mCurrentURL!=mPreviousURL) + { + setCurrentURL(); + mPreviousURL = mCurrentURL; + } + + if(result == LLPluginClassMediaOwner::MEDIA_LOADING) + { + mReloadCtrl->setEnabled(FALSE); + mReloadCtrl->setVisible(FALSE); + mStopCtrl->setEnabled(TRUE); + mStopCtrl->setVisible(has_focus); + } + else + { + mReloadCtrl->setEnabled(TRUE); + mReloadCtrl->setVisible(has_focus); + mStopCtrl->setEnabled(FALSE); + mStopCtrl->setVisible(FALSE); + } + } + + + if(media_plugin) + { + // + // Handle progress bar + // + if(LLPluginClassMediaOwner::MEDIA_LOADING == media_plugin->getStatus()) + { + mMediaProgressPanel->setVisible(true); + mMediaProgressBar->setValue(media_plugin->getProgressPercent()); + } + else + { + mMediaProgressPanel->setVisible(false); + } + } + + if(media_impl) + { + // + // Handle Scrolling + // + switch (mScrollState) + { + case SCROLL_UP: + media_impl->scrollWheel(0, -1, MASK_NONE); + break; + case SCROLL_DOWN: + media_impl->scrollWheel(0, 1, MASK_NONE); + break; + case SCROLL_LEFT: + media_impl->scrollWheel(1, 0, MASK_NONE); + // media_impl->handleKeyHere(KEY_LEFT, MASK_NONE); + break; + case SCROLL_RIGHT: + media_impl->scrollWheel(-1, 0, MASK_NONE); + // media_impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + break; + case SCROLL_NONE: + default: + break; + } + } + + setVisible(enabled); + + // + // Calculate position and shape of the controls + // + std::vector::iterator vert_it; + std::vector::iterator vert_end; + std::vector vect_face; + + LLVolume* volume = objectp->getVolume(); + + if (volume) + { + const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace); + + LLVector3 ext[2]; + ext[0].set(vf.mExtents[0].getF32ptr()); + ext[1].set(vf.mExtents[1].getF32ptr()); + + LLVector3 center = (ext[0]+ext[1])*0.5f; + LLVector3 size = (ext[1]-ext[0])*0.5f; + LLVector3 vert[] = + { + center + size.scaledVec(LLVector3(1,1,1)), + center + size.scaledVec(LLVector3(-1,1,1)), + center + size.scaledVec(LLVector3(1,-1,1)), + center + size.scaledVec(LLVector3(-1,-1,1)), + center + size.scaledVec(LLVector3(1,1,-1)), + center + size.scaledVec(LLVector3(-1,1,-1)), + center + size.scaledVec(LLVector3(1,-1,-1)), + center + size.scaledVec(LLVector3(-1,-1,-1)), + }; + + LLVOVolume* vo = (LLVOVolume*) objectp; + + for (U32 i = 0; i < 8; i++) + { + vect_face.push_back(vo->volumePositionToAgent(vert[i])); + } + } + vert_it = vect_face.begin(); + vert_end = vect_face.end(); + + glh::matrix4f mat; + if (!is_hud) + { + mat = glh_get_current_projection() * glh_get_current_modelview(); + } + else { + glh::matrix4f proj, modelview; + if (get_hud_matrices(proj, modelview)) + mat = proj * modelview; + } + LLVector3 min = LLVector3(1,1,1); + LLVector3 max = LLVector3(-1,-1,-1); + for(; vert_it != vert_end; ++vert_it) + { + // project silhouette vertices into screen space + glh::vec3f screen_vert = glh::vec3f(vert_it->mV); + mat.mult_matrix_vec(screen_vert); + + // add to screenspace bounding box + update_min_max(min, max, LLVector3(screen_vert.v)); + } + + // convert screenspace bbox to pixels (in screen coords) + LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); + LLCoordGL screen_min; + screen_min.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); + screen_min.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); + + LLCoordGL screen_max; + screen_max.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); + screen_max.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); + + // grow panel so that screenspace bounding box fits inside "media_region" element of panel + LLRect media_panel_rect; + // Get the height of the controls (less the volume slider) + S32 controls_height = mMediaControlsStack->getRect().getHeight() - mVolumeSliderCtrl->getRect().getHeight(); + getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_panel_rect); + media_panel_rect.mTop += controls_height; + + // keep all parts of panel on-screen + // Area of the top of the world view to avoid putting the controls + window_rect.mTop -= mTopWorldViewAvoidZone; + // Don't include "spacing" bookends on left & right of the media controls + window_rect.mLeft -= mLeftBookend->getRect().getWidth(); + window_rect.mRight += mRightBookend->getRect().getWidth(); + // Don't include the volume slider + window_rect.mBottom -= mVolumeSliderCtrl->getRect().getHeight(); + media_panel_rect.intersectWith(window_rect); + + // clamp to minimum size, keeping rect inside window + S32 centerX = media_panel_rect.getCenterX(); + S32 centerY = media_panel_rect.getCenterY(); + // Shrink screen rect by min width and height, to ensure containment + window_rect.stretch(-mMinWidth/2, -mMinHeight/2); + window_rect.clampPointToRect(centerX, centerY); + media_panel_rect.setCenterAndSize(centerX, centerY, + llmax(mMinWidth, media_panel_rect.getWidth()), + llmax(mMinHeight, media_panel_rect.getHeight())); + + // Finally set the size of the panel + setShape(media_panel_rect, true); + + // Test mouse position to see if the cursor is stationary + LLCoordWindow cursor_pos_window; + getWindow()->getCursorPosition(&cursor_pos_window); + + // If last pos is not equal to current pos, the mouse has moved + // We need to reset the timer, and make sure the panel is visible + if(cursor_pos_window.mX != mLastCursorPos.mX || + cursor_pos_window.mY != mLastCursorPos.mY || + mScrollState != SCROLL_NONE) + { + mInactivityTimer.start(); + mLastCursorPos = cursor_pos_window; + } + + if(isMouseOver() || hasFocus()) + { + // Never fade the controls if the mouse is over them or they have keyboard focus. + mFadeTimer.stop(); + } + else if(!mClearFaceOnFade && (mInactivityTimer.getElapsedTimeF32() < mInactiveTimeout)) + { + // Mouse is over the object, but has not been stationary for long enough to fade the UI + mFadeTimer.stop(); + } + else if(! mFadeTimer.getStarted() ) + { + // we need to start fading the UI (and we have not already started) + mFadeTimer.reset(); + mFadeTimer.start(); + } + else + { + // I don't think this is correct anymore. This is done in draw() after the fade has completed. + // setVisible(FALSE); + } + } +} + +/*virtual*/ +void LLPanelPrimMediaControls::draw() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if (impl) + { + LLNotificationPtr notification = impl->getCurrentNotification(); + if (notification != mActiveNotification) + { + mActiveNotification = notification; + if (notification) + { + showNotification(notification); + } + else + { + hideNotification(); + } + } + } + + F32 alpha = 1.f; + if(mHideImmediately) + { + //hide this panel + clearFaceOnFade(); + + mHideImmediately = false; + } + else if(mFadeTimer.getStarted()) + { + F32 time = mFadeTimer.getElapsedTimeF32(); + alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + + if(time >= mControlFadeTime) + { + //hide this panel + clearFaceOnFade(); + } + } + + // Build rect for icon area in coord system of this panel + // Assumes layout_stack is a direct child of this panel + mMediaControlsStack->updateLayout(); + + // adjust for layout stack spacing + S32 space = mMediaControlsStack->getPanelSpacing() + 2; + LLRect controls_bg_area = mMediaControlsStack->getRect(); + + controls_bg_area.mTop += space + 2; + + // adjust to ignore space from volume slider + controls_bg_area.mBottom += mVolumeSliderCtrl->getRect().getHeight(); + + // adjust to ignore space from left bookend padding + controls_bg_area.mLeft += mLeftBookend->getRect().getWidth() - space; + + // ignore space from right bookend padding + controls_bg_area.mRight -= mRightBookend->getRect().getWidth() - space - 2; + + // draw control background UI image + mBackgroundImage->draw( controls_bg_area, UI_VERTEX_COLOR % alpha); + + // draw volume slider background UI image + if (mVolumeSliderCtrl->getVisible()) + { + LLRect volume_slider_rect; + screenRectToLocal(mVolumeSliderCtrl->calcScreenRect(), &volume_slider_rect); + mVolumeSliderBackgroundImage->draw(volume_slider_rect, UI_VERTEX_COLOR % alpha); + } + + { + //LLViewDrawContext context(alpha); + LLPanel::draw(); + } +} + +BOOL LLPanelPrimMediaControls::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + mInactivityTimer.start(); + return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); +} + +BOOL LLPanelPrimMediaControls::handleMouseDown(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseDown(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleMouseUp(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseUp(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleKeyHere( KEY key, MASK mask ) +{ + mInactivityTimer.start(); + return LLPanel::handleKeyHere(key, mask); +} + +bool LLPanelPrimMediaControls::isMouseOver() +{ + bool result = false; + + if( getVisible() ) + { + LLCoordWindow cursor_pos_window; + LLCoordScreen cursor_pos_screen; + LLCoordGL cursor_pos_gl; + S32 x, y; + getWindow()->getCursorPosition(&cursor_pos_window); + cursor_pos_gl = cursor_pos_window.convert(); + + if(mMediaControlsStack->getVisible()) + { + mMediaControlsStack->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); + + LLView *hit_child = mMediaControlsStack->childFromPoint(x, y); + if(hit_child && hit_child->getVisible()) + { + // This was useful for debugging both coordinate translation and view hieararchy problems... + // llinfos << "mouse coords: " << x << ", " << y << " hit child " << hit_child->getName() << llendl; + + // This will be a direct child of the LLLayoutStack, which should be a layout_panel. + // These may not shown/hidden by the logic in updateShape(), so we need to do another hit test on the children of the layout panel, + // which are the actual controls. + hit_child->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); + + LLView *hit_child_2 = hit_child->childFromPoint(x, y); + if(hit_child_2 && hit_child_2->getVisible()) + { + // This was useful for debugging both coordinate translation and view hieararchy problems... + // llinfos << " mouse coords: " << x << ", " << y << " hit child 2 " << hit_child_2->getName() << llendl; + result = true; + } + } + } + } + + return result; +} + + +void LLPanelPrimMediaControls::onClickClose() +{ + close(); +} + +void LLPanelPrimMediaControls::close() +{ + resetZoomLevel(true); + LLViewerMediaFocus::getInstance()->clearFocus(); + setVisible(FALSE); +} + + +void LLPanelPrimMediaControls::onClickBack() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl =getTargetMediaImpl(); + + if (impl) + { + impl->navigateBack(); + } +} + +void LLPanelPrimMediaControls::onClickForward() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if (impl) + { + impl->navigateForward(); + } +} + +void LLPanelPrimMediaControls::onClickHome() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateHome(); + } +} + +void LLPanelPrimMediaControls::onClickOpen() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if(impl) + { + LLWeb::loadURL(impl->getCurrentMediaURL()); + } +} + +void LLPanelPrimMediaControls::onClickReload() +{ + focusOnTarget(); + + //LLViewerMedia::navigateHome(); + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateReload(); + } +} + +void LLPanelPrimMediaControls::onClickPlay() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->play(); + } +} + +void LLPanelPrimMediaControls::onClickPause() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->pause(); + } +} + +void LLPanelPrimMediaControls::onClickStop() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateStop(); + } +} + +void LLPanelPrimMediaControls::onClickMediaStop() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->stop(); + } +} + +void LLPanelPrimMediaControls::onClickSkipBack() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl =getTargetMediaImpl(); + + if (impl) + { + impl->skipBack(mSkipStep); + } +} + +void LLPanelPrimMediaControls::onClickSkipForward() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if (impl) + { + impl->skipForward(mSkipStep); + } +} + +void LLPanelPrimMediaControls::onClickZoom() +{ + focusOnTarget(); + + if(mCurrentZoom == ZOOM_NONE) + { + nextZoomLevel(); + } +} + +void LLPanelPrimMediaControls::nextZoomLevel() +{ + LLViewerObject* objectp = getTargetObject(); + if(objectp && objectp->isHUDAttachment()) + { + // Never allow zooming on HUD attachments. + return; + } + + int index = 0; + while (index < kNumZoomLevels) + { + if (kZoomLevels[index] == mCurrentZoom) + { + index++; + break; + } + index++; + } + mCurrentZoom = kZoomLevels[index % kNumZoomLevels]; + updateZoom(); +} + +void LLPanelPrimMediaControls::resetZoomLevel(bool reset_camera) +{ + if(mCurrentZoom != ZOOM_NONE) + { + mCurrentZoom = ZOOM_NONE; + if(reset_camera) + { + updateZoom(); + } + } +} + +void LLPanelPrimMediaControls::updateZoom() +{ + F32 zoom_padding = 0.0f; + switch (mCurrentZoom) + { + case ZOOM_NONE: + { + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + case ZOOM_FAR: + { + zoom_padding = mZoomFarPadding; + break; + } + case ZOOM_MEDIUM: + { + zoom_padding = mZoomMediumPadding; + break; + } + case ZOOM_NEAR: + { + zoom_padding = mZoomNearPadding; + break; + } + default: + { + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + } + + if (zoom_padding > 0.0f) + { + // since we only zoom into medium for now, always set zoom_in constraint to true + LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding, true); + } + + // Remember the object ID/face we zoomed into, so we can update the zoom icon appropriately + mZoomObjectID = mTargetObjectID; + mZoomObjectFace = mTargetObjectFace; +} + +void LLPanelPrimMediaControls::onScrollUp(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(0, -1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollUpHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_UP; +} +void LLPanelPrimMediaControls::onScrollRight(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(-1, 0, MASK_NONE); +// impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollRightHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_RIGHT; +} + +void LLPanelPrimMediaControls::onScrollLeft(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(1, 0, MASK_NONE); +// impl->handleKeyHere(KEY_LEFT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollLeftHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_LEFT; +} + +void LLPanelPrimMediaControls::onScrollDown(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(0, 1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollDownHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_DOWN; +} + +void LLPanelPrimMediaControls::onScrollStop(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_NONE; +} + +void LLPanelPrimMediaControls::onCommitURL() +{ + focusOnTarget(); + + std::string url = mMediaAddress->getValue().asString(); + if(getTargetMediaImpl() && !url.empty()) + { + getTargetMediaImpl()->navigateTo( url, "", true); + + // Make sure keyboard focus is set to the media focus object. + gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + + } + mPauseFadeout = false; + mFadeTimer.start(); +} + + +void LLPanelPrimMediaControls::onInputURL(LLFocusableElement* caller, void *userdata) +{ + + LLPanelPrimMediaControls* this_panel = static_cast (userdata); + this_panel->focusOnTarget(); + + this_panel->mPauseFadeout = true; + this_panel->mFadeTimer.stop(); + this_panel->mFadeTimer.reset(); + +} + +void LLPanelPrimMediaControls::setCurrentURL() +{ +#ifdef USE_COMBO_BOX_FOR_MEDIA_URL +// LLComboBox* media_address_combo = getChild("media_address_combo"); +// // redirects will navigate momentarily to about:blank, don't add to history +// if (media_address_combo && mCurrentURL != "about:blank") +// { +// media_address_combo->remove(mCurrentURL); +// media_address_combo->add(mCurrentURL); +// media_address_combo->selectByValue(mCurrentURL); +// } +#else // USE_COMBO_BOX_FOR_MEDIA_URL + if (mMediaAddress && mCurrentURL != "about:blank") + { + mMediaAddress->setValue(mCurrentURL); + } +#endif // USE_COMBO_BOX_FOR_MEDIA_URL +} + +void LLPanelPrimMediaControls::onCommitSlider() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + // get slider value + F64 slider_value = mMediaPlaySliderCtrl->getValue().asReal(); + if(slider_value <= 0.0) + { + media_impl->stop(); + } + else + { + media_impl->seek(slider_value*mMovieDuration); + //mUpdateSlider= false; + } + } +} + +void LLPanelPrimMediaControls::onCommitVolumeUp() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume += 0.1f; + if(volume >= 1.0f) + { + volume = 1.0f; + } + + media_impl->setVolume(volume); + mMuteBtn->setToggleState(false); + } +} + +void LLPanelPrimMediaControls::onCommitVolumeDown() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume -= 0.1f; + if(volume <= 0.0f) + { + volume = 0.0f; + } + + media_impl->setVolume(volume); + mMuteBtn->setToggleState(false); + } +} + +void LLPanelPrimMediaControls::onCommitVolumeSlider() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); + } +} + +void LLPanelPrimMediaControls::onToggleMute() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + if(volume > 0.0) + { + media_impl->setVolume(0.0); + } + else if (mVolumeSliderCtrl->getValueF32() == 0.0) + { + media_impl->setVolume(1.0); + mVolumeSliderCtrl->setValue(1.0); + } + else + { + media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); + } + } +} + +void LLPanelPrimMediaControls::showVolumeSlider() +{ + mVolumeSliderVisible++; +} + +void LLPanelPrimMediaControls::hideVolumeSlider() +{ + mVolumeSliderVisible--; +} + +bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() +{ + return mVolumeSliderVisible > 0; +} + + +void LLPanelPrimMediaControls::clearFaceOnFade() +{ + if(mClearFaceOnFade) + { + // Hiding this object makes scroll events go missing after it fades out + // (see DEV-41755 for a full description of the train wreck). + // Only hide the controls when we're untargeting. + setVisible(FALSE); + + mClearFaceOnFade = false; + mVolumeSliderVisible = 0; + mTargetImplID = LLUUID::null; + mTargetObjectID = LLUUID::null; + mTargetObjectFace = 0; + } +} + +void LLPanelPrimMediaControls::onMouselookModeIn() +{ + LLViewerMediaFocus::getInstance()->clearHover(); + mHideImmediately = true; +} + +void LLPanelPrimMediaControls::showNotification(LLNotificationPtr notify) +{ + LLNotifications::instance().add(notify); +} + +void LLPanelPrimMediaControls::hideNotification() +{ +} diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h new file mode 100644 index 000000000..7db687d23 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.h @@ -0,0 +1,227 @@ +/** + * @file llpanelmediahud.h + * @brief Media hud panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-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_PANELPRIMMEDIACONTROLS_H +#define LL_PANELPRIMMEDIACONTROLS_H + +#include "llpanel.h" +#include "llviewermedia.h" +#include "llnotificationptr.h" +#include "llcoord.h" + +class LLButton; +class LLIconCtrl; +class LLLayoutStack; +class LLProgressBar; +class LLSliderCtrl; +class LLViewerMediaImpl; + +class LLPanelPrimMediaControls : public LLPanel +{ +public: + LLPanelPrimMediaControls(); + virtual ~LLPanelPrimMediaControls(); + /*virtual*/ BOOL postBuild(); + virtual void draw(); + virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + void updateShape(); + bool isMouseOver(); + + void showNotification(LLNotificationPtr notify); + void hideNotification(); + + + enum EZoomLevel + { + ZOOM_NONE = 0, + ZOOM_FAR, + ZOOM_MEDIUM, + ZOOM_NEAR + }; + + EZoomLevel getZoomLevel() const { return mCurrentZoom; } + void nextZoomLevel(); + void resetZoomLevel(bool reset_camera = true); + void close(); + + LLHandle getHandle() const { return mPanelHandle; } + void setMediaFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + + + static const EZoomLevel kZoomLevels[]; + static const int kNumZoomLevels; + + enum EScrollDir + { + SCROLL_UP = 0, + SCROLL_DOWN, + SCROLL_LEFT, + SCROLL_RIGHT, + SCROLL_NONE + }; + +private: + void onClickClose(); + void onClickBack(); + void onClickForward(); + void onClickHome(); + void onClickOpen(); + void onClickReload(); + void onClickPlay(); + void onClickPause(); + void onClickStop(); + void onClickZoom(); + void onClickSkipBack(); + void onClickSkipForward(); + void onClickMediaStop(); + void onCommitURL(); + + void updateZoom(); + void setCurrentURL(); + void onCommitSlider(); + + void onCommitVolumeUp(); + void onCommitVolumeDown(); + void onCommitVolumeSlider(); + void onToggleMute(); + void showVolumeSlider(); + void hideVolumeSlider(); + bool shouldVolumeSliderBeVisible(); + + static void onScrollUp(void* user_data); + static void onScrollUpHeld(void* user_data); + static void onScrollLeft(void* user_data); + static void onScrollLeftHeld(void* user_data); + static void onScrollRight(void* user_data); + static void onScrollRightHeld(void* user_data); + static void onScrollDown(void* user_data); + static void onScrollDownHeld(void* user_data); + static void onScrollStop(void* user_data); + + static void onInputURL(LLFocusableElement* caller, void *userdata); + static bool hasControlsPermission(LLViewerObject *obj, const LLMediaEntry *media_entry); + + void focusOnTarget(); + + LLViewerMediaImpl* getTargetMediaImpl(); + LLViewerObject* getTargetObject(); + LLPluginClassMedia* getTargetMediaPlugin(); + +private: + + void clearFaceOnFade(); + + void onMouselookModeIn(); + + LLView *mMediaRegion; + LLUICtrl *mBackCtrl; + LLUICtrl *mFwdCtrl; + LLUICtrl *mReloadCtrl; + LLUICtrl *mPlayCtrl; + LLUICtrl *mPauseCtrl; + LLUICtrl *mStopCtrl; + LLUICtrl *mMediaStopCtrl; + LLUICtrl *mHomeCtrl; + LLUICtrl *mUnzoomCtrl; + LLUICtrl *mOpenCtrl; + LLUICtrl *mSkipBackCtrl; + LLUICtrl *mSkipFwdCtrl; + LLUICtrl *mZoomCtrl; + LLPanel *mMediaProgressPanel; + LLProgressBar *mMediaProgressBar; + LLUICtrl *mMediaAddressCtrl; + LLUICtrl *mMediaAddress; + LLUICtrl *mMediaPlaySliderPanel; + LLUICtrl *mMediaPlaySliderCtrl; + LLUICtrl *mVolumeCtrl; + LLButton *mMuteBtn; + LLSliderCtrl *mVolumeSliderCtrl; + LLIconCtrl *mWhitelistIcon; + LLIconCtrl *mSecureLockIcon; + LLLayoutStack *mMediaControlsStack; + LLUICtrl *mLeftBookend; + LLUICtrl *mRightBookend; + LLUIImage* mBackgroundImage; + LLUIImage* mVolumeSliderBackgroundImage; + F32 mSkipStep; + S32 mMinWidth; + S32 mMinHeight; + F32 mZoomNearPadding; + F32 mZoomMediumPadding; + F32 mZoomFarPadding; + S32 mTopWorldViewAvoidZone; + + LLUICtrl *mMediaPanelScroll; + LLButton *mScrollUpCtrl; + LLButton *mScrollLeftCtrl; + LLButton *mScrollRightCtrl; + LLButton *mScrollDownCtrl; + + bool mPauseFadeout; + bool mUpdateSlider; + bool mClearFaceOnFade; + bool mHideImmediately; + + LLMatrix4 mLastCameraMat; + EZoomLevel mCurrentZoom; + EScrollDir mScrollState; + LLCoordWindow mLastCursorPos; + LLFrameTimer mInactivityTimer; + LLFrameTimer mFadeTimer; + F32 mInactiveTimeout; + F32 mControlFadeTime; + LLRootHandle mPanelHandle; + F32 mAlpha; + std::string mCurrentURL; + std::string mPreviousURL; + F64 mCurrentRate; + F64 mMovieDuration; + + LLUUID mTargetObjectID; + S32 mTargetObjectFace; + LLUUID mTargetImplID; + LLVector3 mTargetObjectNormal; + + LLUUID mZoomObjectID; + S32 mZoomObjectFace; + + S32 mVolumeSliderVisible; + + LLNotificationPtr mActiveNotification; +}; + +#endif // LL_PANELMEDIAHUD_H diff --git a/indra/newview/llpanelweb.cpp b/indra/newview/llpanelweb.cpp index 9f60ecda4..986b5580a 100644 --- a/indra/newview/llpanelweb.cpp +++ b/indra/newview/llpanelweb.cpp @@ -44,17 +44,6 @@ #include "llviewerwindow.h" #include "llpluginclassmedia.h" -// helper functions for getting/freeing the web browser media -// if creating/destroying these is too slow, we'll need to create -// a static member and update all our static callbacks -viewer_media_t get_web_media() -{ - - viewer_media_t media_source = LLViewerMedia::newMediaImpl("", LLUUID::null, 0, 0, 0, 0, "text/html"); - - return media_source; -} - LLPanelWeb::LLPanelWeb() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_web.xml"); @@ -98,15 +87,14 @@ void LLPanelWeb::apply() bool value = childGetValue("use_external_browser").asString() == "external" ? true : false; gSavedSettings.setBOOL("UseExternalBrowser", value); - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - { - media_source->getMediaPlugin()->enable_cookies(childGetValue("cookies_enabled")); + LLViewerMedia::setCookiesEnabled(getChild("cookies_enabled")->getValue()); - bool proxy_enable = childGetValue("web_proxy_enabled"); - std::string proxy_address = childGetValue("web_proxy_editor"); - int proxy_port = childGetValue("web_proxy_port"); - media_source->getMediaPlugin()->proxy_setup(proxy_enable, proxy_address, proxy_port); + if (hasChild("web_proxy_enabled") && hasChild("web_proxy_editor") && hasChild("web_proxy_port")) + { + bool proxy_enable = getChild("web_proxy_enabled")->getValue(); + std::string proxy_address = getChild("web_proxy_editor")->getValue(); + int proxy_port = getChild("web_proxy_port")->getValue(); + LLViewerMedia::setProxyConfig(proxy_enable, proxy_address, proxy_port); } } @@ -126,9 +114,7 @@ bool LLPanelWeb::callback_clear_browser_cache(const LLSD& notification, const LL S32 option = LLNotification::getSelectedOption(notification, response); if ( option == 0 ) // YES { - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->clear_cache(); + LLViewerMedia::clearAllCaches(); } return false; } @@ -145,25 +131,11 @@ bool LLPanelWeb::callback_clear_cookies(const LLSD& notification, const LLSD& re S32 option = LLNotification::getSelectedOption(notification, response); if ( option == 0 ) // YES { - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->clear_cookies(); + LLViewerMedia::clearAllCookies(); } return false; } -// static -void LLPanelWeb::onCommitCookies(LLUICtrl* ctrl, void* data) -{ - LLPanelWeb* self = (LLPanelWeb*)data; - LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl; - - if (!self || !check) return; - - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->enable_cookies(check->get()); -} // static void LLPanelWeb::onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data) { @@ -174,6 +146,4 @@ void LLPanelWeb::onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data) self->childSetEnabled("web_proxy_editor", check->get()); self->childSetEnabled("web_proxy_port", check->get()); self->childSetEnabled("proxy_text_label", check->get()); - - } diff --git a/indra/newview/llpanelweb.h b/indra/newview/llpanelweb.h index 449b9cb29..8e64a0ec6 100644 --- a/indra/newview/llpanelweb.h +++ b/indra/newview/llpanelweb.h @@ -51,7 +51,6 @@ private: static void onClickClearCookies(void*); static bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); static bool callback_clear_cookies(const LLSD& notification, const LLSD& response); - static void onCommitCookies(LLUICtrl* ctrl, void* data); static void onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data); }; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 300330cb6..646f83f2e 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -35,6 +35,7 @@ #include "lldbstrings.h" #include "lleconomy.h" #include "llgl.h" +#include "llmediaentry.h" #include "llrender.h" #include "llnotifications.h" #include "llpermissions.h" @@ -1895,47 +1896,79 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) getSelection()->applyToObjects(&sendfunc); } -void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url) -{ - U8 media_flags = LLTextureEntry::MF_NONE; - if (media_type == LLViewerObject::MEDIA_SET) - { - media_flags = LLTextureEntry::MF_HAS_MEDIA; - } - +// This function expects media_data to be a map containing relevant +// media data name/value pairs (e.g. home_url, etc.) +void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) +{ struct f : public LLSelectedTEFunctor { U8 mMediaFlags; - f(const U8& t) : mMediaFlags(t) {} + const LLSD &mMediaData; + f(const U8& t, const LLSD& d) : mMediaFlags(t), mMediaData(d) {} bool apply(LLViewerObject* object, S32 te) { if (object->permModify()) { - // update viewer side color in anticipation of update from simulator - object->setTEMediaFlags(te, mMediaFlags); + // If we are adding media, then check the current state of the + // media data on this face. + // - If it does not have media, AND we are NOT setting the HOME URL, then do NOT add media to this + // face. + // - If it does not have media, and we ARE setting the HOME URL, add media to this face. + // - If it does already have media, add/update media to/on this face + // If we are removing media, just do it (ignore the passed-in LLSD). + if (mMediaFlags & LLTextureEntry::MF_HAS_MEDIA) + { + llassert(mMediaData.isMap()); + const LLTextureEntry *texture_entry = object->getTE(te); + if (!mMediaData.isMap() || + (NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY)) + { + // skip adding/updating media + } + else { + // Add/update media + object->setTEMediaFlags(te, mMediaFlags); + LLVOVolume *vo = dynamic_cast(object); + llassert(NULL != vo); + if (NULL != vo) + { + vo->syncMediaData(te, mMediaData, true/*merge*/, true/*ignore_agent*/); + } + } + } + else + { + // delete media (or just set the flags) + object->setTEMediaFlags(te, mMediaFlags); + } } return true; } - } setfunc(media_flags); + } setfunc(media_type, media_data); getSelection()->applyToTEs(&setfunc); - - struct g : public LLSelectedObjectFunctor + + struct f2 : public LLSelectedObjectFunctor { - U8 media_type; - const std::string& media_url ; - g(U8 a, const std::string& b) : media_type(a), media_url(b) {} virtual bool apply(LLViewerObject* object) { if (object->permModify()) { object->sendTEUpdate(); - object->setMediaType(media_type); - object->setMediaURL(media_url); + LLVOVolume *vo = dynamic_cast(object); + llassert(NULL != vo); + // It's okay to skip this object if hasMedia() is false... + // the sendTEUpdate() above would remove all media data if it were + // there. + if (NULL != vo && vo->hasMedia()) + { + // Send updated media data FOR THE ENTIRE OBJECT + vo->sendMediaDataUpdate(); + } } return true; } - } sendfunc(media_type, media_url); - getSelection()->applyToObjects(&sendfunc); + } func2; + mSelectedObjects->applyToObjects( &func2 ); } void LLSelectMgr::selectionSetGlow(F32 glow) @@ -4097,6 +4130,7 @@ void LLSelectMgr::deselectAllIfTooFar() if ( (gSavedSettings.getBOOL("LimitSelectDistance") || (fRlvFartouch) ) // [/RLVa:KB] && (!mSelectedObjects->getPrimaryObject() || !mSelectedObjects->getPrimaryObject()->isAvatar()) + && (mSelectedObjects->getPrimaryObject() != LLViewerMediaFocus::getInstance()->getFocusedObject()) && !mSelectedObjects->isAttachment() && !selectionCenter.isExactlyZero()) { @@ -5538,7 +5572,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID(); - LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getSelectedUUID(); + LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID(); // //for (S32 pass = 0; pass < 2; pass++) //{ diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 7aa6eea13..3526d93d6 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -528,7 +528,7 @@ public: void selectionSetTexGen( U8 texgen ); void selectionSetShiny( U8 shiny ); void selectionSetFullbright( U8 fullbright ); - void selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url); + void selectionSetMedia( U8 media_type, const LLSD &media_data ); void selectionSetClickAction(U8 action); void selectionSetIncludeInSearch(bool include_in_search); void selectionSetGlow(const F32 glow); @@ -663,6 +663,8 @@ public: void sendAttach(U8 attachment_point, bool replace=true); void sendDetach(); void sendDropAttachment(); + void sendLink(); + void sendDelink(); //void sendHinge(U8 type); //void sendDehinge(); void sendSelect(); @@ -703,8 +705,7 @@ private: void (*pack_body)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type); - void sendLink(); - void sendDelink(); + static void packAgentID( void *); static void packAgentAndSessionID(void* user_data); diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp new file mode 100644 index 000000000..69f731467 --- /dev/null +++ b/indra/newview/llslurl.cpp @@ -0,0 +1,526 @@ +/** + * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llslurl.h" + +#include "llpanellogin.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llfiltersd2xmlrpc.h" +#include "curl/curl.h" +#include "hippogridmanager.h" + +const char* LLSLURL::SLURL_HTTP_SCHEME = "http"; +const char* LLSLURL::SLURL_HTTPS_SCHEME = "https"; +const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife"; +const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife"; +const char* LLSLURL::SLURL_COM = "slurl.com"; +// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag +// text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this +// version is required also. + +const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com"; +const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_APP_PATH = "app"; +const char* LLSLURL::SLURL_REGION_PATH = "region"; +const char* LLSLURL::SIM_LOCATION_HOME = "home"; +const char* LLSLURL::SIM_LOCATION_LAST = "last"; + + +const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; +const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; +#define MAINGRID "util.agni.lindenlab.com" + +// resolve a simstring from a slurl +LLSLURL::LLSLURL(const std::string& slurl) +{ + // by default we go to agni. + mType = INVALID; + + if(slurl == SIM_LOCATION_HOME) + { + mType = HOME_LOCATION; + } + else if(slurl.empty() || (slurl == SIM_LOCATION_LAST)) + { + mType = LAST_LOCATION; + } + else + { + LLURI slurl_uri; + // parse the slurl as a uri + if(slurl.find(':') == std::string::npos) + { + // There may be no scheme ('secondlife:' etc.) passed in. In that case + // we want to normalize the slurl by putting the appropriate scheme + // in front of the slurl. So, we grab the appropriate slurl base + // from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or + // https:///region/ for Standalone grid (the word region, not the region name) + // these slurls are typically passed in from the 'starting location' box on the login panel, + // where the user can type in /// + //std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. + std::string fixed_slurl = MAIN_GRID_SLURL_BASE; + + // the slurl that was passed in might have a prepended /, or not. So, + // we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife//// + // or some such. + + if(slurl[0] == '/') + { + fixed_slurl += slurl.substr(1); + } + else + { + fixed_slurl += slurl; + } + // We then load the slurl into a LLURI form + slurl_uri = LLURI(fixed_slurl); + } + else + { + // as we did have a scheme, implying a URI style slurl, we + // simply parse it as a URI + slurl_uri = LLURI(slurl); + } + + LLSD path_array = slurl_uri.pathArray(); + + // determine whether it's a maingrid URI or an Standalone/open style URI + // by looking at the scheme. If it's a 'secondlife:' slurl scheme or + // 'sl:' scheme, we know it's maingrid + + // At the end of this if/else block, we'll have determined the grid, + // and the slurl type (APP or LOCATION) + if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) + { + // parse a maingrid style slurl. We know the grid is maingrid + // so grab it. + // A location slurl for maingrid (with the special schemes) can be in the form + // secondlife:///// + // or + // secondlife:///secondlife//// + // where if grid is empty, it specifies Agni + + // An app style slurl for maingrid can be + // secondlife:///app/ + // where an empty grid implies Agni + + // we'll start by checking the top of the 'path' which will be + // either 'app', 'secondlife', or . + + // default to maingrid + + mGrid = MAINGRID; + + if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)) + { + // it's in the form secondlife:///(app|secondlife) + // so parse the grid name to derive the grid ID + if (!slurl_uri.hostName().empty()) + { + mGrid = gHippoGridManager->getGrid(slurl_uri.hostName())->getGridNick(); + } + else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + // If the slurl is in the form secondlife:///secondlife/ form, + // then we are in fact on maingrid. + mGrid = MAINGRID; + } + else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + // for app style slurls, where no grid name is specified, assume the currently + // selected or logged in grid. + mGrid = gHippoGridManager->getCurrentGridNick(); + } + + if(mGrid.empty()) + { + // we couldn't find the grid in the grid manager, so bail + LL_WARNS("AppInit")<<"unable to find grid"< or /app/, so it must be secondlife:// + // therefore the hostname will be the region name, and it's a location type + mType = LOCATION; + // 'normalize' it so the region name is in fact the head of the path_array + path_array.insert(0, slurl_uri.hostName()); + } + } + else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + { + // We're dealing with either a Standalone style slurl or slurl.com slurl + if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::WWW_SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::MAPS_SECONDLIFE_COM)) + { + // slurl.com implies maingrid + mGrid = MAINGRID; + } + else + { + // Don't try to match any old http:/// URL as a SLurl. + // SLE SLurls will have the grid hostname in the URL, so only + // match http URLs if the hostname matches the grid hostname + // (or its a slurl.com or maps.secondlife.com URL). + if ((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME || + slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) && + slurl_uri.hostName() != gHippoGridManager->getCurrentGridNick()) + { + return; + } + + // As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style + // urls are properly formed, unlike the stinky maingrid style + mGrid = slurl_uri.hostName(); + } + if (path_array.size() == 0) + { + // um, we need a path... + return; + } + + // we need to normalize the urls so + // the path portion starts with the 'command' that we want to do + // it can either be region or app. + if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)) + { + // strip off 'region' or 'secondlife' + path_array.erase(0); + // it's a location + mType = LOCATION; + } + else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + mType = APP; + path_array.erase(0); + // leave app appended. + } + else + { + // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + return; + } + } + else + { + // invalid scheme, so bail + return; + } + + + if(path_array.size() == 0) + { + // we gotta have some stuff after the specifier as to whether it's a region or command + return; + } + + // now that we know whether it's an app slurl or a location slurl, + // parse the slurl into the proper data structures. + if(mType == APP) + { + // grab the app command type and strip it (could be a command to jump somewhere, + // or whatever ) + mAppCmd = path_array[0].asString(); + path_array.erase(0); + + // Grab the parameters + mAppPath = path_array; + // and the query + mAppQuery = slurl_uri.query(); + mAppQueryMap = slurl_uri.queryMap(); + return; + } + else if(mType == LOCATION) + { + // at this point, head of the path array should be [ , , , ] where x, y and z + // are collectively optional + // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + path_array.erase(0); + + // parse the x, y, and optionally z + if(path_array.size() >= 2) + { + + mPosition = LLVector3(path_array); // this construction handles LLSD without all components (values default to 0.f) + if((F32(mPosition[VX]) < 0.f) || + (mPosition[VX] > REGION_WIDTH_METERS) || + (F32(mPosition[VY]) < 0.f) || + (mPosition[VY] > REGION_WIDTH_METERS) || + (F32(mPosition[VZ]) < 0.f) || + (mPosition[VZ] > REGION_HEIGHT_METERS)) + { + mType = INVALID; + return; + } + + } + else + { + // if x, y and z were not fully passed in, go to the middle of the region. + // teleport will adjust the actual location to make sure you're on the ground + // and such + mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0); + } + } + } +} + + +// Create a slurl for the middle of the region +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region) +{ + mGrid = grid; + mRegion = region; + mType = LOCATION; + mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0); +} + + + +// create a slurl given the position. The position will be modded with the region +// width handling global positions as well +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3& position) +{ + mGrid = grid; + mRegion = region; + S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) ); + S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) ); + S32 z = llround( (F32)position[VZ] ); + mType = LOCATION; + mPosition = LLVector3(x, y, z); +} + + +// create a simstring +LLSLURL::LLSLURL(const std::string& region, + const LLVector3& position) +{ + *this = LLSLURL(gHippoGridManager->getCurrentGridNick(), + region, position); +} + +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3d& global_position) +{ + HippoGridInfo* target_grid = gHippoGridManager->getGrid(grid); + *this = LLSLURL((target_grid ? target_grid->getGridNick() : ""), + region, LLVector3(global_position.mdV[VX], + global_position.mdV[VY], + global_position.mdV[VZ])); +} + +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& region, + const LLVector3d& global_position) +{ + *this = LLSLURL(gHippoGridManager->getCurrentGridNick(), + region, global_position); +} + +LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb) +{ + mType = APP; + mAppCmd = command; + mAppPath = LLSD::emptyArray(); + mAppPath.append(LLSD(id)); + mAppPath.append(LLSD(verb)); +} + + +std::string LLSLURL::getSLURLString() const +{ + switch(mType) + { + case HOME_LOCATION: + return SIM_LOCATION_HOME; + case LAST_LOCATION: + return SIM_LOCATION_LAST; + case LOCATION: + { + // lookup the grid + S32 x = llround( (F32)mPosition[VX] ); + S32 y = llround( (F32)mPosition[VY] ); + S32 z = llround( (F32)mPosition[VZ] ); + //return LLGridManager::getInstance()->getSLURLBase(mGrid) + + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. + return MAIN_GRID_SLURL_BASE + + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); + } + case APP: + { + std::ostringstream app_url; + //app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; + //Singu TODO: Implement LLHippoGridMgr::getAppSLURLBase some day. For now it's hardcoded. + app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; + for(LLSD::array_const_iterator i = mAppPath.beginArray(); + i != mAppPath.endArray(); + i++) + { + app_url << "/" << i->asString(); + } + if(mAppQuery.length() > 0) + { + app_url << "?" << mAppQuery; + } + return app_url.str(); + } + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL; + return std::string(); + } +} + +std::string LLSLURL::getLoginString() const +{ + + std::stringstream unescaped_start; + switch(mType) + { + case LOCATION: + unescaped_start << "uri:" + << mRegion << "&" + << llround(mPosition[0]) << "&" + << llround(mPosition[1]) << "&" + << llround(mPosition[2]); + break; + case HOME_LOCATION: + unescaped_start << "home"; + break; + case LAST_LOCATION: + unescaped_start << "last"; + break; + default: + LL_WARNS("AppInit") << "Unexpected SLURL type ("<<(int)mType <<")for login string"<< LL_ENDL; + break; + } + return xml_escape_string(unescaped_start.str()); +} + +bool LLSLURL::operator==(const LLSLURL& rhs) +{ + if(rhs.mType != mType) return false; + switch(mType) + { + case LOCATION: + return ((mGrid == rhs.mGrid) && + (mRegion == rhs.mRegion) && + (mPosition == rhs.mPosition)); + case APP: + return getSLURLString() == rhs.getSLURLString(); + + case HOME_LOCATION: + case LAST_LOCATION: + return true; + default: + return false; + } +} + +bool LLSLURL::operator !=(const LLSLURL& rhs) +{ + return !(*this == rhs); +} + +std::string LLSLURL::getLocationString() const +{ + return llformat("%s/%d/%d/%d", + mRegion.c_str(), + (int)llround(mPosition[0]), + (int)llround(mPosition[1]), + (int)llround(mPosition[2])); +} + +// static +const std::string LLSLURL::typeName[NUM_SLURL_TYPES] = +{ + "INVALID", + "LOCATION", + "HOME_LOCATION", + "LAST_LOCATION", + "APP", + "HELP" +}; + +std::string LLSLURL::getTypeString(SLURL_TYPE type) +{ + std::string name; + if ( type >= INVALID && type < NUM_SLURL_TYPES ) + { + name = LLSLURL::typeName[type]; + } + else + { + name = llformat("Out of Range (%d)",type); + } + return name; +} + + +std::string LLSLURL::asString() const +{ + std::ostringstream result; + result + << " mType: " << LLSLURL::getTypeString(mType) + << " mGrid: " + getGrid() + << " mRegion: " + getRegion() + << " mPosition: " << mPosition + << " mAppCmd:" << getAppCmd() + << " mAppPath:" + getAppPath().asString() + << " mAppQueryMap:" + getAppQueryMap().asString() + << " mAppQuery: " + getAppQuery() + ; + + return result.str(); +} + diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h new file mode 100644 index 000000000..b86cf7949 --- /dev/null +++ b/indra/newview/llslurl.h @@ -0,0 +1,114 @@ +/** + * @file llslurl.h + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLSLURL_H +#define LLSLURL_H + +#include "llstring.h" + + +// represents a location in a grid + +class LLSLURL +{ +public: + static const char* SLURL_HTTPS_SCHEME; + static const char* SLURL_HTTP_SCHEME; + static const char* SLURL_SL_SCHEME; + static const char* SLURL_SECONDLIFE_SCHEME; + static const char* SLURL_SECONDLIFE_PATH; + static const char* SLURL_COM; + static const char* WWW_SLURL_COM; + static const char* SECONDLIFE_COM; + static const char* MAPS_SECONDLIFE_COM; + static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; + static LLSLURL START_LOCATION; + static const char* SIM_LOCATION_HOME; + static const char* SIM_LOCATION_LAST; + static const char* SLURL_APP_PATH; + static const char* SLURL_REGION_PATH; + + // if you modify this enumeration, update typeName as well + enum SLURL_TYPE { + INVALID, + LOCATION, + HOME_LOCATION, + LAST_LOCATION, + APP, + HELP, + NUM_SLURL_TYPES // must be last + }; + + + LLSLURL(): mType(INVALID) { } + LLSLURL(const std::string& slurl); + LLSLURL(const std::string& grid, const std::string& region); + LLSLURL(const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb); + + SLURL_TYPE getType() const { return mType; } + + std::string getSLURLString() const; + std::string getLoginString() const; + std::string getLocationString() const; + std::string getGrid() const { return mGrid; } + std::string getRegion() const { return mRegion; } + LLVector3 getPosition() const { return mPosition; } + std::string getAppCmd() const { return mAppCmd; } + std::string getAppQuery() const { return mAppQuery; } + LLSD getAppQueryMap() const { return mAppQueryMap; } + LLSD getAppPath() const { return mAppPath; } + + bool isValid() const { return mType != INVALID; } + bool isSpatial() const { return (mType == LAST_LOCATION) || (mType == HOME_LOCATION) || (mType == LOCATION); } + + bool operator==(const LLSLURL& rhs); + bool operator!=(const LLSLURL&rhs); + + std::string asString() const ; + +protected: + static const std::string typeName[NUM_SLURL_TYPES]; + /// Get a human-readable version of the type for logging + static std::string getTypeString(SLURL_TYPE type); + + SLURL_TYPE mType; + + // used for Apps and Help + std::string mAppCmd; + LLSD mAppPath; + LLSD mAppQueryMap; + std::string mAppQuery; + + std::string mGrid; // reference to grid manager grid + std::string mRegion; + LLVector3 mPosition; +}; + +#endif // LLSLURL_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 9f44db11b..cba7a27ef 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -86,6 +86,7 @@ #include "llsecondlifeurls.h" #include "llstring.h" #include "lltexteditor.h" +#include "llurlentry.h" #include "lluserrelations.h" #include "sgversion.h" #include "llviewercontrol.h" @@ -262,7 +263,7 @@ extern S32 gStartImageHeight; // static LLHost gAgentSimHost; -static BOOL gSkipOptionalUpdate = FALSE; +//static BOOL gSkipOptionalUpdate = FALSE; static bool gGotUseCircuitCodeAck = false; static std::string sInitialOutfit; @@ -272,6 +273,7 @@ static boost::signals2::connection sWearablesLoadedCon; static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; +LLSLURL LLStartUp::sStartSLURL; static U64 gFirstSimHandle = 0; @@ -294,7 +296,7 @@ void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); bool login_alert_status(const LLSD& notification, const LLSD& response); -void update_app(BOOL mandatory, const std::string& message); +//void update_app(BOOL mandatory, const std::string& message); bool update_dialog_callback(const LLSD& notification, const LLSD& response); void login_packet_failed(void**, S32 result); void use_circuit_callback(void**, S32 result); @@ -307,6 +309,7 @@ void release_start_screen(); void reset_login(); void apply_udp_blacklist(const std::string& csv); bool process_login_success_response(std::string &password); +void transition_back_to_login_panel(const std::string& emsg); void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is_group) { @@ -412,7 +415,7 @@ bool idle_startup() if (gNoRender) { // HACK, skip optional updates if you're running drones - gSkipOptionalUpdate = TRUE; + //gSkipOptionalUpdate = TRUE; } else { @@ -753,11 +756,11 @@ bool idle_startup() // // Log on to system // - if (!LLStartUp::sSLURLCommand.empty()) + /*if (!LLStartUp::sSLURLCommand.empty()) { // this might be a secondlife:///app/login URL gLoginHandler.parseDirectLogin(LLStartUp::sSLURLCommand); - } + }*/ if (!gLoginHandler.getFirstName().empty() || !gLoginHandler.getLastName().empty() || !gLoginHandler.getWebLoginKey().isNull() ) @@ -782,11 +785,8 @@ bool idle_startup() pass.hex_digest(md5pass); password = md5pass; -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else show_connect_box = false; -#endif + gSavedSettings.setBOOL("AutoLogin", TRUE); } else if (gSavedSettings.getBOOL("AutoLogin")) @@ -796,11 +796,7 @@ bool idle_startup() password = LLStartUp::loadPasswordFromDisk(); gSavedSettings.setBOOL("RememberPassword", TRUE); -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else show_connect_box = false; -#endif } else { @@ -861,6 +857,7 @@ bool idle_startup() if (show_connect_box) { + LL_DEBUGS("AppInit") << "show_connect_box on" << LL_ENDL; // Load all the name information out of the login view // NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't // show the login view until login_show() is called below. @@ -886,13 +883,15 @@ bool idle_startup() if (login_history.size() > 0) { LLPanelLogin::setFields(*login_history.getEntries().rbegin()); + display_startup(); } else { LLPanelLogin::setFields(firstname, lastname, password); + display_startup(); LLPanelLogin::giveFocus(); } - display_startup(); + gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); @@ -900,6 +899,7 @@ bool idle_startup() } else { + LL_DEBUGS("AppInit") << "show_connect_box off, skipping to STATE_LOGIN_CLEANUP" << LL_ENDL; // skip directly to message template verification LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); } @@ -1025,10 +1025,10 @@ bool idle_startup() // Set PerAccountSettingsFile to the default value. gSavedSettings.setString("PerAccountSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, - LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount") - ) - ); + LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"))); + // Note: can't store warnings files per account because some come up before login + // Overwrite default user settings with user settings LLAppViewer::instance()->loadSettingsFromDirectory(AIReadAccess(gSettings), "Account"); @@ -1093,12 +1093,12 @@ bool idle_startup() if (show_connect_box) { - std::string location; + /*std::string location; LLPanelLogin::getLocation( location ); LLURLSimString::setString( location ); // END TODO - LLPanelLogin::close(); + LLPanelLogin::close();*/ } //For HTML parsing in text boxes. @@ -1125,27 +1125,21 @@ bool idle_startup() #endif // RLV_EXTENSION_STARTLOCATION { // Force login at the last location - agent_location_id = START_LOCATION_ID_LAST; - gSavedSettings.setBOOL("LoginLastLocation", FALSE); - - // Clear some things that would cause us to divert to a user-specified location - LLURLSimString::setString(LLURLSimString::sLocationStringLast); - LLStartUp::sSLURLCommand.clear(); - } else + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + } // [/RLVa:KB] - if (LLURLSimString::parse()) - { - // a startup URL was specified - agent_location_id = START_LOCATION_ID_URL; - } - else if (gSavedSettings.getBOOL("LoginLastLocation")) - { - agent_location_id = START_LOCATION_ID_LAST; // last location - } - else - { - agent_location_id = START_LOCATION_ID_HOME; // home - } + switch (LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + agent_location_id = START_LOCATION_ID_URL; + break; + case LLSLURL::LAST_LOCATION: + agent_location_id = START_LOCATION_ID_LAST; + break; + default: + agent_location_id = START_LOCATION_ID_HOME; + break; + } gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -1357,7 +1351,8 @@ bool idle_startup() lastname, password, // web_login_key, start.str(), - gSkipOptionalUpdate, + //gSkipOptionalUpdate, + true, gAcceptTOS, gAcceptCriticalMessage, gLastExecEvent, @@ -1433,7 +1428,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "STATE_LOGIN_PROCESS_RESPONSE" << LL_ENDL; std::ostringstream emsg; bool quit = false; - bool update = false; + //bool update = false; bool successful_login = false; LLUserAuth::UserAuthcode error = LLUserAuth::getInstance()->authResponse(); // reset globals @@ -1519,7 +1514,7 @@ bool idle_startup() // Clear the password password = ""; } - if(reason_response == "update") + /*if(reason_response == "update") { auth_message = message_response; update = true; @@ -1535,7 +1530,7 @@ bool idle_startup() gSkipOptionalUpdate = TRUE; return false; } - } + }*/ } break; default: @@ -1562,14 +1557,6 @@ bool idle_startup() break; } - if (update || gSavedSettings.getBOOL("ForceMandatoryUpdate")) - { - gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); - update_app(TRUE, auth_message); - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - return false; - } - // Version update and we're not showing the dialog if(quit) { @@ -1607,8 +1594,7 @@ bool idle_startup() LLSD args; args["ERROR_MESSAGE"] = emsg.str(); LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); + transition_back_to_login_panel(emsg.str()); show_connect_box = true; } } @@ -1623,10 +1609,11 @@ bool idle_startup() // Bounce back to the login screen. LLSD args; args["ERROR_MESSAGE"] = emsg.str(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); + transition_back_to_login_panel(emsg.str()); show_connect_box = true; + return FALSE; } return FALSE; } @@ -2490,38 +2477,24 @@ bool idle_startup() // JC - 7/20/2002 gViewerWindow->sendShapeToSim(); + // The reason we show the alert is because we want to + // reduce confusion for when you log in and your provided + // location is not your expected location. So, if this is + // your first login, then you do not have an expectation, + // thus, do not show this alert. if (!gAgent.isFirstLogin()) { - bool url_ok = LLURLSimString::sInstance.parse(); - if (!((gAgentStartLocation == "url" && url_ok) || - (!url_ok && ((gAgentStartLocation == "last" && gSavedSettings.getBOOL("LoginLastLocation")) || - (gAgentStartLocation == "home" && !gSavedSettings.getBOOL("LoginLastLocation")))))) - { - // The reason we show the alert is because we want to - // reduce confusion for when you log in and your provided - // location is not your expected location. So, if this is - // your first login, then you do not have an expectation, - // thus, do not show this alert. - LLSD args; - if (url_ok) - { - args["TYPE"] = "desired"; - args["HELP"] = ""; - } - else if (gSavedSettings.getBOOL("LoginLastLocation")) - { - args["TYPE"] = "last"; - args["HELP"] = ""; - } - else - { - args["TYPE"] = "home"; - args["HELP"] = "You may want to set a new home location."; - } - LLNotificationsUtil::add("AvatarMoved", args); - } - else + llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl; + LLSLURL start_slurl = LLStartUp::getStartSLURL(); + LL_DEBUGS("AppInit") << "start slurl "<forceQuit(); - // Bump them back to the login screen. - //reset_login(); - } - else - { - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - } - return false; - } - - LLSD query_map = LLSD::emptyMap(); - // *TODO place os string in a global constant -#if LL_WINDOWS - query_map["os"] = "win"; -#elif LL_DARWIN - query_map["os"] = "mac"; -#elif LL_LINUX - query_map["os"] = "lnx"; -#elif LL_SOLARIS - query_map["os"] = "sol"; -#endif - // *TODO change userserver to be grid on both viewer and sim, since - // userserver no longer exists. - query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); - // - query_map["channel"] = gVersionChannel; - - // *TODO constantize this guy - // *NOTE: This URL is also used in win_setup/lldownloader.cpp - LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); - - if(LLAppViewer::sUpdaterInfo) - { - delete LLAppViewer::sUpdaterInfo ; - } - LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; - -#if LL_WINDOWS - LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); - if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - // We're hosed, bail - LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; - - std::string updater_source = gDirUtilp->getAppRODataDir(); - updater_source += gDirUtilp->getDirDelimiter(); - updater_source += "updater.exe"; - - LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source - << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath - << LL_ENDL; - - - if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); - }; - - LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; - - //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. - LLAppViewer::instance()->removeMarkerFile(); // In case updater fails - -#elif LL_DARWIN - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); - }; - - LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gVersionBundleID; - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // Run the auto-updater. - system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ - -#elif LL_LINUX || LL_SOLARIS - OSMessageBox("Automatic updating is not yet implemented for Linux.\n" - "Please download the latest version from www.secondlife.com.", - LLStringUtil::null, OSMB_OK); -#endif - LLAppViewer::instance()->forceQuit(); - return false; -} void use_circuit_callback(void**, S32 result) { @@ -3694,8 +3496,6 @@ void reset_login() //--------------------------------------------------------------------------- -std::string LLStartUp::sSLURLCommand; - bool LLStartUp::canGoFullscreen() { return gStartupState >= STATE_WORLD_INIT; @@ -3752,67 +3552,64 @@ void LLStartUp::cleanupNameCache() delete gCacheName; gCacheName = NULL; } + bool LLStartUp::dispatchURL() { // ok, if we've gotten this far and have a startup URL - if (!sSLURLCommand.empty()) + if (!getStartSLURL().isValid()) { - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); + return false; } - else if (LLURLSimString::parse()) + if(getStartSLURL().getType() != LLSLURL::APP) { + // If we started with a location, but we're already // at that location, don't pop dialogs open. LLVector3 pos = gAgent.getPositionAgent(); - F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX; - F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY; + LLVector3 slurlpos = getStartSLURL().getPosition(); + F32 dx = pos.mV[VX] - slurlpos.mV[VX]; + F32 dy = pos.mV[VY] - slurlpos.mV[VY]; const F32 SLOP = 2.f; // meters - if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName() + if( getStartSLURL().getRegion() != gAgent.getRegion()->getName() || (dx*dx > SLOP*SLOP) || (dy*dy > SLOP*SLOP) ) { - std::string url = LLURLSimString::getURL(); - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), "clicked", + NULL, false); } return true; } return false; } -bool login_alert_done(const LLSD& notification, const LLSD& response) +void LLStartUp::setStartSLURL(const LLSLURL& slurl) { - LLPanelLogin::giveFocus(); - return false; + LL_DEBUGS("AppInit")<banUdpMessage(item); - - start = comma + 1; - - } - while(comma < csv.length()); - + return sStartSLURL; } /* @@ -4067,6 +3864,36 @@ bool LLStartUp::startLLProxy() return proxy_ok; } + +bool login_alert_done(const LLSD& notification, const LLSD& response) +{ + LLPanelLogin::giveFocus(); + return false; +} +void apply_udp_blacklist(const std::string& csv) +{ + + std::string::size_type start = 0; + std::string::size_type comma = 0; + do + { + comma = csv.find(",", start); + if (comma == std::string::npos) + { + comma = csv.length(); + } + std::string item(csv, start, comma-start); + + lldebugs << "udp_blacklist " << item << llendl; + gMessageSystem->banUdpMessage(item); + + start = comma + 1; + + } + while(comma < csv.length()); + +} + bool process_login_success_response(std::string& password) { LLSD response = LLUserAuth::getInstance()->getResponse(); @@ -4082,9 +3909,17 @@ bool process_login_success_response(std::string& password) if(!text.empty()) gAgentID.set(text); gDebugInfo["AgentID"] = text; + // Agent id needed for parcel info request in LLUrlEntryParcel + // to resolve parcel name. + LLUrlEntryParcel::setAgentID(gAgentID); + text = response["session_id"].asString(); if(!text.empty()) gAgentSessionID.set(text); gDebugInfo["SessionID"] = text; + + // Session id needed for parcel info request in LLUrlEntryParcel + // to resolve parcel name. + LLUrlEntryParcel::setSessionID(gAgentSessionID); text = response["secure_session_id"].asString(); if(!text.empty()) gAgent.mSecureSessionID.set(text); @@ -4429,3 +4264,9 @@ bool process_login_success_response(std::string& password) return success; } +void transition_back_to_login_panel(const std::string& emsg) +{ + // Bounce back to the login screen. + reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + gSavedSettings.setBOOL("AutoLogin", FALSE); +} \ No newline at end of file diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 021cba775..0e546a9ee 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -37,6 +37,7 @@ class LLViewerTexture ; class LLEventPump; +class LLSLURL; #include "llviewerstats.h" @@ -133,14 +134,14 @@ public: // the viewer, dispatch it static void postStartupState(); - static std::string sSLURLCommand; - // *HACK: On startup, if we were passed a secondlife://app/do/foo - // command URL, store it for later processing. + static void setStartSLURL(const LLSLURL& slurl); + static LLSLURL& getStartSLURL(); static bool startLLProxy(); // Initialize the SOCKS 5 proxy static LLViewerStats::PhaseMap& getPhases() { return *sPhases; } private: + static LLSLURL sStartSLURL; static std::string startupStateToString(EStartupState state); static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index a9da2c1a9..73aaa6194 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -1017,7 +1017,7 @@ class LLBalanceHandler : public LLCommandHandler { public: // Requires "trusted" browser/URL source - LLBalanceHandler() : LLCommandHandler("balance", true) { } + LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (tokens.size() == 1 diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index d1fc130e9..ea0ac8b94 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -122,6 +122,7 @@ public: const LLRect& rect, const std::string& label, PermissionMask immediate_filter_perm_mask, + PermissionMask dnd_filter_perm_mask, PermissionMask non_immediate_filter_perm_mask, BOOL can_apply_immediately, const std::string& fallback_image_name); @@ -213,6 +214,7 @@ protected: LLFilterEditor* mFilterEdit; LLInventoryPanel* mInventoryPanel; PermissionMask mImmediateFilterPermMask; + PermissionMask mDnDFilterPermMask; PermissionMask mNonImmediateFilterPermMask; BOOL mCanApplyImmediately; BOOL mNoCopyTextureSelected; @@ -227,6 +229,7 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( const LLRect& rect, const std::string& label, PermissionMask immediate_filter_perm_mask, + PermissionMask dnd_filter_perm_mask, PermissionMask non_immediate_filter_perm_mask, BOOL can_apply_immediately, const std::string& fallback_image_name) @@ -250,6 +253,7 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mActive( TRUE ), mFilterEdit(NULL), mImmediateFilterPermMask(immediate_filter_perm_mask), + mDnDFilterPermMask(dnd_filter_perm_mask), mNonImmediateFilterPermMask(non_immediate_filter_perm_mask), mContextConeOpacity(0.f), mSelectedItemPinned(FALSE) @@ -366,10 +370,9 @@ BOOL LLFloaterTexturePicker::handleDragAndDrop( if (mod) item_perm_mask |= PERM_MODIFY; if (xfer) item_perm_mask |= PERM_TRANSFER; - - PermissionMask filter_perm_mask = mImmediateFilterPermMask; + //PermissionMask filter_perm_mask = getFilterPermMask(); Commented out due to no-copy texture loss. + PermissionMask filter_perm_mask = mDnDFilterPermMask; if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask ) - { if (drop) { @@ -1339,6 +1342,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus) rect, mLabel, mImmediateFilterPermMask, + mDnDFilterPermMask, mNonImmediateFilterPermMask, mCanApplyImmediately, mFallbackImageName); diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index f5a45807a..35a5e9c8f 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -132,6 +132,8 @@ public: void setImmediateFilterPermMask(PermissionMask mask) { mImmediateFilterPermMask = mask; } + void setDnDFilterPermMask(PermissionMask mask) + { mDnDFilterPermMask = mask; } void setNonImmediateFilterPermMask(PermissionMask mask) { mNonImmediateFilterPermMask = mask; } PermissionMask getImmediateFilterPermMask() { return mImmediateFilterPermMask; } @@ -185,6 +187,7 @@ private: BOOL mAllowInvisibleTexture; // If true, the user can select "Invisible" as an option LLCoordGL mLastFloaterLeftTop; PermissionMask mImmediateFilterPermMask; + PermissionMask mDnDFilterPermMask; PermissionMask mNonImmediateFilterPermMask; BOOL mCanApplyImmediately; BOOL mNeedsRawImageData; diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index f3a1831db..ac69f13b8 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -46,6 +46,7 @@ #include "llhoverview.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" +#include "llmediaentry.h" #include "llmenugl.h" #include "llmutelist.h" #include "llselectmgr.h" @@ -650,12 +651,9 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) if(!object) { - gViewerWindow->setCursor(UI_CURSOR_ARROW); + //gViewerWindow->setCursor(UI_CURSOR_ARROW); // We need to clear media hover flag - if (LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); - } + LLViewerMediaFocus::getInstance()->clearHover(); } return TRUE; @@ -905,24 +903,28 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) if(!tep) return false; - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); - if (media_impl.isNull() || !media_impl->hasMedia()) + LLMediaEntry* mep = (tep->hasMedia()) ? tep->getMediaData() : NULL; + if(!mep) return false; + + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); if (gSavedSettings.getBOOL("MediaOnAPrimUI")) { - LLObjectSelectionHandle selection = LLViewerMediaFocus::getInstance()->getSelection(); - if (!selection->contains(pick.getObject(), pick.mObjectFace)) + if (!LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) || media_impl.isNull()) { - LLViewerMediaFocus::getInstance()->setFocusFace(TRUE, pick.getObject(), pick.mObjectFace, media_impl); + // It's okay to give this a null impl + LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal); } else { - media_impl->mouseDown(pick.mXYCoords.mX, pick.mXYCoords.mY, gKeyboard->currentMask(TRUE)); - media_impl->mouseCapture(); // the mouse-up will happen when capture is lost + // Make sure keyboard focus is set to the media focus object. + gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + LLEditMenuHandler::gEditMenuHandler = LLViewerMediaFocus::instance().getFocusedMediaImpl(); + + media_impl->mouseDown(pick.mUVCoords, gKeyboard->currentMask(TRUE)); + mMediaMouseCaptureID = mep->getMediaID(); + setMouseCapture(TRUE); // This object will send a mouse-up to the media when it loses capture. } return true; @@ -948,38 +950,47 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick) pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs() ) { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + LLViewerMediaFocus::getInstance()->clearHover(); return false; } - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); - viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); - if (tep - && media_impl.notNull() - && media_impl->hasMedia() + if(!tep) + return false; + + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if (mep && gSavedSettings.getBOOL("MediaOnAPrimUI")) - { - if(LLViewerMediaFocus::getInstance()->getFocus()) + { + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + + if(media_impl.notNull()) { - media_impl->mouseMove(pick.mXYCoords.mX, pick.mXYCoords.mY, gKeyboard->currentMask(TRUE)); - } + // Update media hover object + if (!LLViewerMediaFocus::getInstance()->isHoveringOverFace(objectp, pick.mObjectFace)) + { + LLViewerMediaFocus::getInstance()->setHoverFace(objectp, pick.mObjectFace, media_impl, pick.mNormal); + } + + // If this is the focused media face, send mouse move events. + if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace)) + { + media_impl->mouseMove(pick.mUVCoords, gKeyboard->currentMask(TRUE)); + gViewerWindow->setCursor(media_impl->getLastSetCursor()); + } + else + { + // This is not the focused face -- set the default cursor. + gViewerWindow->setCursor(UI_CURSOR_ARROW); + } - // Set mouse over flag if unset - if (! LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLSelectMgr::getInstance()->setHoverObject(objectp, pick.mObjectFace); - LLViewerMediaFocus::getInstance()->setMouseOverFlag(true, media_impl); - LLViewerMediaFocus::getInstance()->setPickInfo(pick); + return true; } - - return true; } - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + + // In all other cases, clear media hover. + LLViewerMediaFocus::getInstance()->clearHover(); return false; } diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index a9971abbd..f7cce735c 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -39,51 +39,47 @@ #include "llfloaterurldisplay.h" #include "llfloaterdirectory.h" #include "llfloaterworldmap.h" -#include "llfloatermediabrowser.h" #include "llpanellogin.h" +#include "llregionhandle.h" +#include "llslurl.h" #include "llstartup.h" // gStartupState #include "llurlsimstring.h" #include "llweb.h" #include "llworldmap.h" #include "llworldmapmessage.h" +#include "llviewernetwork.h" + +#include "hippogridmanager.h" // library includes +#include "llnotificationsutil.h" #include "llsd.h" -const std::string SLURL_SL_HELP_PREFIX = "secondlife://app."; -const std::string SLURL_SL_PREFIX = "sl://"; -const std::string SLURL_SECONDLIFE_PREFIX = "secondlife://"; -const std::string SLURL_SLURL_PREFIX = "http://slurl.com/secondlife/"; -const std::string SLURL_SLURL_ALT_PREFIX = "http://maps.secondlife.com/secondlife/"; - -const std::string SLURL_APP_TOKEN = "app/"; - class LLURLDispatcherImpl { public: - static bool isSLURL(const std::string& url); - - static bool isSLURLCommand(const std::string& url); - - static bool dispatch(const std::string& url, + static bool dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const LLSLURL& slurl); private: - static bool dispatchCore(const std::string& url, + static bool dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); // handles both left and right click - static bool dispatchHelp(const std::string& url, bool right_mouse); + static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse); // Handles sl://app.floater.html.help by showing Help floater. // Returns true if handled. - static bool dispatchApp(const std::string& url, + static bool dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -91,233 +87,158 @@ private: // by showing panel in Search floater. // Returns true if handled or explicitly blocked. - static bool dispatchRegion(const std::string& url, bool right_mouse); + static bool dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse); // handles secondlife://Ahern/123/45/67/ // Returns true if handled. - static void regionHandleCallback(U64 handle, const std::string& url, + static void regionHandleCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a location has been resolved to a // region name - static void regionNameCallback(U64 handle, const std::string& url, + static void regionNameCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a region name has been resolved to a // location in-world, used by places-panel display. - static bool matchPrefix(const std::string& url, const std::string& prefix); - - static std::string stripProtocol(const std::string& url); - friend class LLTeleportHandler; }; // static -bool LLURLDispatcherImpl::isSLURL(const std::string& url) -{ - if (matchPrefix(url, SLURL_SL_HELP_PREFIX)) return true; - if (matchPrefix(url, SLURL_SL_PREFIX)) return true; - if (matchPrefix(url, SLURL_SECONDLIFE_PREFIX)) return true; - if (matchPrefix(url, SLURL_SLURL_PREFIX)) return true; - if (matchPrefix(url, SLURL_SLURL_ALT_PREFIX)) return true; - return false; -} - -// static -bool LLURLDispatcherImpl::isSLURLCommand(const std::string& url) -{ - if (matchPrefix(url, SLURL_SL_PREFIX + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SECONDLIFE_PREFIX + "/" + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SLURL_PREFIX + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SLURL_ALT_PREFIX + SLURL_APP_TOKEN)) - { - return true; - } - return false; -} - -// static -bool LLURLDispatcherImpl::dispatchCore(const std::string& url, +bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (url.empty()) return false; - if (dispatchHelp(url, right_mouse)) return true; - if (dispatchApp(url, right_mouse, web, trusted_browser)) return true; - if (dispatchRegion(url, right_mouse)) return true; + //if (dispatchHelp(slurl, right_mouse)) return true; + switch(slurl.getType()) + { + case LLSLURL::APP: + return dispatchApp(slurl, nav_type, right_mouse, web, trusted_browser); + case LLSLURL::LOCATION: + return dispatchRegion(slurl, nav_type, right_mouse); + default: + return false; + } /* // Inform the user we can't handle this std::map args; - args["SLURL"] = url; + args["SLURL"] = slurl; r; */ - - return false; } // static -bool LLURLDispatcherImpl::dispatch(const std::string& url, +bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { - llinfos << "url: " << url << llendl; const bool right_click = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, nav_type, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) +bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { - llinfos << "url: " << url << llendl; const bool right_click = true; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, "clicked", right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchHelp(const std::string& url, bool right_mouse) -{ -#if LL_LIBXUL_ENABLED - if (matchPrefix(url, SLURL_SL_HELP_PREFIX)) - { - gViewerHtmlHelp.show(); - return true; - } -#endif - return false; -} - -// static -bool LLURLDispatcherImpl::dispatchApp(const std::string& url, +bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (!isSLURL(url)) - { - return false; - } - - LLURI uri(url); - LLSD pathArray = uri.pathArray(); - pathArray.erase(0); // erase "app" - std::string cmd = pathArray.get(0); - pathArray.erase(0); // erase "cmd" + llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; + const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery()); bool handled = LLCommandDispatcher::dispatch( - cmd, pathArray, uri.queryMap(), web, trusted_browser); - return handled; + slurl.getAppCmd(), slurl.getAppPath(), query_map, web, nav_type, trusted_browser); + + // alert if we didn't handle this secondlife:///app/ SLURL + // (but still return true because it is a valid app SLURL) + if (! handled) + { + LLNotificationsUtil::add("UnsupportedCommandSLURL"); + } + return true; } // static -bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse) { - if (!isSLURL(url)) - { + if(slurl.getType() != LLSLURL::LOCATION) + { return false; - } - + } // Before we're logged in, need to update the startup screen // to tell the user where they are going. if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { - // Parse it and stash in globals, it will be dispatched in - // STATE_CLEANUP. - LLURLSimString::setString(url); // We're at the login screen, so make sure user can see // the login location box to know where they are going. - LLPanelLogin::refreshLocation( true ); + LLPanelLogin::setLocation(slurl); return true; } - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLFloaterURLDisplay* url_displayp = LLFloaterURLDisplay::getInstance(LLSD()); - url_displayp->setName(region_name); - // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, - url, - false); // don't teleport + slurl.getSLURLString(), + LLUI::sConfigGroup->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } /*static*/ -void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)x; - local_pos.mV[VY] = (F32)y; - local_pos.mV[VZ] = (F32)z; - - - // determine whether the point is in this region - if ((x >= 0) && (x < REGION_WIDTH_UNITS) && - (y >= 0) && (y < REGION_WIDTH_UNITS)) - { - // if so, we're done - regionHandleCallback(region_handle, url, snapshot_id, teleport); - } - - else - { - // otherwise find the new region from the location - - // add the position to get the new region - LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos); - - U64 new_region_handle = to_region_handle(global_pos); - LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, - LLURLDispatcherImpl::regionHandleCallback, - url, teleport); - } + + if(slurl.getType() == LLSLURL::LOCATION) + { + regionHandleCallback(region_handle, slurl, snapshot_id, teleport); + } } /* static */ -void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - // remap x and y to local coordinates - S32 local_x = x % REGION_WIDTH_UNITS; - S32 local_y = y % REGION_WIDTH_UNITS; - if (local_x < 0) - local_x += REGION_WIDTH_UNITS; - if (local_y < 0) - local_y += REGION_WIDTH_UNITS; - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)local_x; - local_pos.mV[VY] = (F32)local_y; - local_pos.mV[VZ] = (F32)z; - + // we can't teleport cross grid at this point + HippoGridInfo* new_grid = gHippoGridManager->getGrid(slurl.getGrid()); + if( new_grid + != gHippoGridManager->getCurrentGrid()) + { + LLSD args; + args["SLURL"] = slurl.getLocationString(); + args["CURRENT_GRID"] = gHippoGridManager->getCurrentGridNick(); + + std::string grid_label = new_grid ? new_grid->getGridNick() : ""; + + if(!grid_label.empty()) + { + args["GRID"] = grid_label; + } + else + { + args["GRID"] = slurl.getGrid(); + } + LLNotificationsUtil::add("CantTeleportToGrid", args); + return; + } + LLVector3d global_pos = from_region_handle(region_handle); + LLVector3 local_pos = slurl.getPosition(); + global_pos += LLVector3d(local_pos); if (teleport) { - LLVector3d global_pos = from_region_handle(region_handle); - global_pos += LLVector3d(local_pos); + gAgent.teleportViaLocation(global_pos); if(gFloaterWorldMap) { @@ -335,46 +256,12 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str { url_displayp->setSnapshotDisplay(snapshot_id); } - std::string locationString = llformat("%s %d, %d, %d", region_name.c_str(), x, y, z); + LLVector3 pos = slurl.getPosition(); + std::string locationString = llformat("%s %d, %d, %d", slurl.getRegion().c_str(), local_pos.mV[VX],local_pos.mV[VY],local_pos.mV[VZ]); url_displayp->setLocationString(locationString); } } -// static -bool LLURLDispatcherImpl::matchPrefix(const std::string& url, const std::string& prefix) -{ - std::string test_prefix = url.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - return test_prefix == prefix; -} - -// static -std::string LLURLDispatcherImpl::stripProtocol(const std::string& url) -{ - std::string stripped = url; - if (matchPrefix(stripped, SLURL_SL_HELP_PREFIX)) - { - stripped.erase(0, SLURL_SL_HELP_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SL_PREFIX)) - { - stripped.erase(0, SLURL_SL_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SECONDLIFE_PREFIX)) - { - stripped.erase(0, SLURL_SECONDLIFE_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SLURL_PREFIX)) - { - stripped.erase(0, SLURL_SLURL_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SLURL_ALT_PREFIX)) - { - stripped.erase(0, SLURL_SLURL_ALT_PREFIX.length()); - } - return stripped; -} - //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled // to URL parsing and sim-fragment parsing @@ -384,7 +271,7 @@ public: // Teleport requests *must* come from a trusted browser // inside the app, otherwise a malicious web page could // cause a constant teleport loop. JC - LLTeleportHandler() : LLCommandHandler("teleport", true) { } + LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) @@ -393,18 +280,21 @@ public: // a global position, and teleport to it if (tokens.size() < 1) return false; - // Region names may be %20 escaped. - std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]); - - // build secondlife://De%20Haro/123/45/67 for use in callback - std::string url = SLURL_SECONDLIFE_PREFIX; - for (int i = 0; i < tokens.size(); ++i) + LLVector3 coords(128, 128, 0); + if (tokens.size() <= 4) { - url += tokens[i].asString() + "/"; + coords = LLVector3(tokens[1].asReal(), + tokens[2].asReal(), + tokens[3].asReal()); } + + // Region names may be %20 escaped. + + std::string region_name = LLURI::unescape(tokens[0]); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionHandleCallback, - url, + LLSLURL(region_name, coords).getSLURLString(), true); // teleport return true; } @@ -414,50 +304,32 @@ LLTeleportHandler gTeleportHandler; //--------------------------------------------------------------------------- // static -bool LLURLDispatcher::isSLURL(const std::string& url) -{ - return LLURLDispatcherImpl::isSLURL(url); -} - -// static -bool LLURLDispatcher::isSLURLCommand(const std::string& url) -{ - return LLURLDispatcherImpl::isSLURLCommand(url); -} - -// static -bool LLURLDispatcher::dispatch(const std::string& url, +bool LLURLDispatcher::dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), nav_type, web, trusted_browser); } // static -bool LLURLDispatcher::dispatchRightClick(const std::string& url) +bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) { - return LLURLDispatcherImpl::dispatchRightClick(url); + return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl)); } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) { // *NOTE: Text editors are considered sources of trusted URLs - // in order to make objectim and avatar profile links in chat - // history work. While a malicious resident could chat an app - // SLURL, the receiving resident will see it and must affirmatively + // in order to make avatar profile links in chat history work. + // While a malicious resident could chat an app SLURL, the + // receiving resident will see it and must affirmatively // click on it. // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_browser); } -// static -std::string LLURLDispatcher::buildSLURL(const std::string& regionname, - S32 x, S32 y, S32 z) -{ - std::string slurl = SLURL_SLURL_PREFIX + regionname + llformat("/%d/%d/%d",x,y,z); - slurl = LLWeb::escapeURL( slurl ); - return slurl; -} + diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index c947e5e37..82e1d3152 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -38,15 +38,11 @@ class LLMediaCtrl; class LLURLDispatcher { public: - static bool isSLURL(const std::string& url); - // Is this any sort of secondlife:// or sl:// URL? - - static bool isSLURLCommand(const std::string& url); - // Is this a special secondlife://app/ URL? - - static bool dispatch(const std::string& url, + + static bool dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, - bool trusted_browser); + bool trusted_browser); // At startup time and on clicks in internal web browsers, // teleport, open map, or run requested command. // @param url @@ -60,12 +56,9 @@ public: // that navigates to trusted (Linden Lab) pages. // Returns true if someone handled the URL. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& url); - - static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - // builds: http://slurl.com/secondlife/RegionName/x/y/z/ + static bool dispatchFromTextEditor(const std::string& slurl); }; #endif diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index e6abe3676..6965181db 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -34,43 +34,139 @@ #include "llviewermedia.h" + +#include "llviewermedia.h" + +#include "llagent.h" +#include "llagentcamera.h" #include "llappviewer.h" +#include "llaudioengine.h" // for gAudiop +#include "llcallbacklist.h" #include "lldir.h" #include "lldiriterator.h" #include "llevent.h" // LLSimpleListener -#include "llhoverview.h" +#include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows. +#include "llfocusmgr.h" #include "llkeyboard.h" +#include "llmarketplacefunctions.h" +#include "llmediaentry.h" #include "llmimetypes.h" +#include "llmutelist.h" #include "llnotifications.h" #include "llnotificationsutil.h" +#include "llpanelprofile.h" +#include "llparcel.h" #include "llpluginclassmedia.h" #include "llplugincookiestore.h" #include "llurldispatcher.h" #include "lluuid.h" +#include "llvieweraudio.h" #include "llviewermediafocus.h" #include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llviewerparcelmedia.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" #include "llviewertexture.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llvovolume.h" +#include "llwebprofile.h" #include "llwindow.h" #include "llvieweraudio.h" -#include "llweb.h" -#include "llwebprofile.h" -#include "llfloateravatarinfo.h" // for getProfileURL() function -//#include "viewerversion.h" +#include "aifilepicker.h" +#include "llstartup.h" + +#include // for SkinFolder listener +#include + +/*static*/ const char* LLViewerMedia::AUTO_PLAY_MEDIA_SETTING = "ParcelMediaAutoPlayEnable"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING = "MediaShowOnOthers"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING = "MediaShowWithinParcel"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING = "MediaShowOutsideParcel"; + class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy mimeDiscoveryResponder_timeout; extern AIHTTPTimeoutPolicy viewerMediaOpenIDResponder_timeout; extern AIHTTPTimeoutPolicy viewerMediaWebProfileResponder_timeout; -// Merov: Temporary definitions while porting the new viewer media code to Snowglobe -const int LEFT_BUTTON = 0; -const int RIGHT_BUTTON = 1; +// Move this to its own file. + +LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() +{ + observerListType::iterator iter = mObservers.begin(); + + while( iter != mObservers.end() ) + { + LLViewerMediaObserver *self = *iter; + iter++; + remObserver(self); + } +} /////////////////////////////////////////////////////////////////////////////// -// Helper class that tries to download a URL from a web site and calls a method +// +bool LLViewerMediaEventEmitter::addObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; + + if ( std::find( mObservers.begin(), mObservers.end(), observer ) != mObservers.end() ) + return false; + + mObservers.push_back( observer ); + observer->mEmitters.push_back( this ); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaEventEmitter::remObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; + + mObservers.remove( observer ); + observer->mEmitters.remove(this); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaEventEmitter::emitEvent( LLPluginClassMedia* media, LLViewerMediaObserver::EMediaEvent event ) +{ + // Broadcast the event to any observers. + observerListType::iterator iter = mObservers.begin(); + while( iter != mObservers.end() ) + { + LLViewerMediaObserver *self = *iter; + ++iter; + self->handleMediaEvent( media, event ); + } +} + +// Move this to its own file. +LLViewerMediaObserver::~LLViewerMediaObserver() +{ + std::list::iterator iter = mEmitters.begin(); + + while( iter != mEmitters.end() ) + { + LLViewerMediaEventEmitter *self = *iter; + iter++; + self->remObserver( this ); + } +} + + +// Move this to its own file. +// helper class that tries to download a URL from a web site and calls a method // on the Panel Land Media and to discover the MIME type class LLMimeDiscoveryResponder : public LLHTTPClient::ResponderHeadersOnly { @@ -80,7 +176,19 @@ public: : mMediaImpl(media_impl), mDefaultMimeType(default_mime_type), mInitialized(false) - {} + { + if(mMediaImpl->mMimeTypeProbe != NULL) + { + llerrs << "impl already has an outstanding responder" << llendl; + } + + mMediaImpl->mMimeTypeProbe = this; + } + + ~LLMimeDiscoveryResponder() + { + disconnectOwner(); + } /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { @@ -105,12 +213,17 @@ public: void completeAny(U32 status, const std::string& mime_type) { - if(!mInitialized && ! mime_type.empty()) + // the call to initializeMedia may disconnect the responder, which will clear mMediaImpl. + // Make a local copy so we can call loadURI() afterwards. + LLViewerMediaImpl *impl = mMediaImpl; + + if(impl && !mInitialized && ! mime_type.empty()) { - if (mMediaImpl->initializeMedia(mime_type)) + if(impl->initializeMedia(mime_type)) { mInitialized = true; - mMediaImpl->play(); + impl->loadURI(); + disconnectOwner(); } } } @@ -118,6 +231,26 @@ public: /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mimeDiscoveryResponder_timeout; } /*virtual*/ char const* getName(void) const { return "LLMimeDiscoveryResponder"; } + void cancelRequest() + { + disconnectOwner(); + } + +private: + void disconnectOwner() + { + if(mMediaImpl) + { + if(mMediaImpl->mMimeTypeProbe != this) + { + llerrs << "internal error: mMediaImpl->mMimeTypeProbe != this" << llendl; + } + + mMediaImpl->mMimeTypeProbe = NULL; + } + mMediaImpl = NULL; + } + public: viewer_media_t mMediaImpl; @@ -218,55 +351,35 @@ private: std::string mHost; }; + LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL; LLURL LLViewerMedia::sOpenIDURL; std::string LLViewerMedia::sOpenIDCookie; -typedef std::list impl_list; -static impl_list sViewerMediaImplList; +LLPluginClassMedia* LLViewerMedia::sSpareBrowserMediaSource = NULL; +static LLViewerMedia::impl_list sViewerMediaImplList; +static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap; +static LLTimer sMediaCreateTimer; +static const F32 LLVIEWERMEDIA_CREATE_DELAY = 1.0f; +static F32 sGlobalVolume = 1.0f; +static bool sForceUpdate = false; +static LLUUID sOnlyAudibleTextureID = LLUUID::null; +static F64 sLowestLoadableImplInterest = 0.0f; +static bool sAnyMediaShowing = false; +static boost::signals2::connection sTeleportFinishConnection; static std::string sUpdatedCookies; static const char *PLUGIN_COOKIE_FILE_NAME = "plugin_cookies.txt"; ////////////////////////////////////////////////////////////////////////////////////////// -// LLViewerMedia - -////////////////////////////////////////////////////////////////////////////////////////// -// static -viewer_media_t LLViewerMedia::newMediaImpl(const std::string& media_url, - const LLUUID& texture_id, - S32 media_width, - S32 media_height, - U8 media_auto_scale, - U8 media_loop, - std::string mime_type) +static void add_media_impl(LLViewerMediaImpl* media) { - LLViewerMediaImpl* media_impl = getMediaImplFromTextureID(texture_id); - if(media_impl == NULL || texture_id.isNull()) - { - // Create the media impl - media_impl = new LLViewerMediaImpl(media_url, texture_id, media_width, media_height, media_auto_scale, media_loop, mime_type); - sViewerMediaImplList.push_back(media_impl); - } - else - { - media_impl->stop(); - media_impl->mTextureId = texture_id; - media_impl->mMediaURL = media_url; - media_impl->mMediaWidth = media_width; - media_impl->mMediaHeight = media_height; - media_impl->mMediaAutoScale = media_auto_scale; - media_impl->mMediaLoop = media_loop; - if(! media_url.empty()) - media_impl->navigateTo(media_url, mime_type, true); - } - return media_impl; + sViewerMediaImplList.push_back(media); } ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::removeMedia(LLViewerMediaImpl* media) +static void remove_media_impl(LLViewerMediaImpl* media) { - impl_list::iterator iter = sViewerMediaImplList.begin(); - impl_list::iterator end = sViewerMediaImplList.end(); + LLViewerMedia::impl_list::iterator iter = sViewerMediaImplList.begin(); + LLViewerMedia::impl_list::iterator end = sViewerMediaImplList.end(); for(; iter != end; iter++) { @@ -278,22 +391,160 @@ void LLViewerMedia::removeMedia(LLViewerMediaImpl* media) } } +class LLViewerMediaMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLViewerMedia::muteListChanged();} +}; + +static LLViewerMediaMuteListObserver sViewerMediaMuteListObserver; +static bool sViewerMediaMuteListObserverInitialized = false; + + +////////////////////////////////////////////////////////////////////////////////////////// +// LLViewerMedia + +////////////////////////////////////////////////////////////////////////////////////////// +// static +viewer_media_t LLViewerMedia::newMediaImpl( + const LLUUID& texture_id, + S32 media_width, + S32 media_height, + U8 media_auto_scale, + U8 media_loop) +{ + LLViewerMediaImpl* media_impl = getMediaImplFromTextureID(texture_id); + if(media_impl == NULL || texture_id.isNull()) + { + // Create the media impl + media_impl = new LLViewerMediaImpl(texture_id, media_width, media_height, media_auto_scale, media_loop); + } + else + { + media_impl->unload(); + media_impl->setTextureID(texture_id); + media_impl->mMediaWidth = media_width; + media_impl->mMediaHeight = media_height; + media_impl->mMediaAutoScale = media_auto_scale; + media_impl->mMediaLoop = media_loop; + } + + return media_impl; +} + +viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const std::string& previous_url, bool update_from_self) +{ + // Try to find media with the same media ID + viewer_media_t media_impl = getMediaImplFromTextureID(media_entry->getMediaID()); + + lldebugs << "called, current URL is \"" << media_entry->getCurrentURL() + << "\", previous URL is \"" << previous_url + << "\", update_from_self is " << (update_from_self?"true":"false") + << llendl; + + bool was_loaded = false; + bool needs_navigate = false; + + if(media_impl) + { + was_loaded = media_impl->hasMedia(); + + media_impl->setHomeURL(media_entry->getHomeURL()); + + media_impl->mMediaAutoScale = media_entry->getAutoScale(); + media_impl->mMediaLoop = media_entry->getAutoLoop(); + media_impl->mMediaWidth = media_entry->getWidthPixels(); + media_impl->mMediaHeight = media_entry->getHeightPixels(); + media_impl->mMediaAutoPlay = media_entry->getAutoPlay(); + media_impl->mMediaEntryURL = media_entry->getCurrentURL(); + LLPluginClassMedia* plugin = media_impl->getMediaPlugin(); + if (plugin) + { + plugin->setAutoScale(media_impl->mMediaAutoScale); + plugin->setLoop(media_impl->mMediaLoop); + plugin->setSize(media_entry->getWidthPixels(), media_entry->getHeightPixels()); + } + + bool url_changed = (media_impl->mMediaEntryURL != previous_url); + if(media_impl->mMediaEntryURL.empty()) + { + if(url_changed) + { + // The current media URL is now empty. Unload the media source. + media_impl->unload(); + + lldebugs << "Unloading media instance (new current URL is empty)." << llendl; + } + } + else + { + // The current media URL is not empty. + // If (the media was already loaded OR the media was set to autoplay) AND this update didn't come from this agent, + // do a navigate. + bool auto_play = media_impl->isAutoPlayable(); + if((was_loaded || auto_play) && !update_from_self) + { + needs_navigate = url_changed; + } + + lldebugs << "was_loaded is " << (was_loaded?"true":"false") + << ", auto_play is " << (auto_play?"true":"false") + << ", needs_navigate is " << (needs_navigate?"true":"false") << llendl; + } + } + else + { + media_impl = newMediaImpl( + media_entry->getMediaID(), + media_entry->getWidthPixels(), + media_entry->getHeightPixels(), + media_entry->getAutoScale(), + media_entry->getAutoLoop()); + + media_impl->setHomeURL(media_entry->getHomeURL()); + media_impl->mMediaAutoPlay = media_entry->getAutoPlay(); + media_impl->mMediaEntryURL = media_entry->getCurrentURL(); + if(media_impl->isAutoPlayable()) + { + needs_navigate = true; + } + } + + if(media_impl) + { + if(needs_navigate) + { + media_impl->navigateTo(media_impl->mMediaEntryURL, "", true, true); + lldebugs << "navigating to URL " << media_impl->mMediaEntryURL << llendl; + } + else if(!media_impl->mMediaURL.empty() && (media_impl->mMediaURL != media_impl->mMediaEntryURL)) + { + // If we already have a non-empty media URL set and we aren't doing a navigate, update the media URL to match the media entry. + media_impl->mMediaURL = media_impl->mMediaEntryURL; + + // If this causes a navigate at some point (such as after a reload), it should be considered server-driven so it isn't broadcast. + media_impl->mNavigateServerRequest = true; + + lldebugs << "updating URL in the media impl to " << media_impl->mMediaEntryURL << llendl; + } + } + + return media_impl; +} + ////////////////////////////////////////////////////////////////////////////////////////// // static LLViewerMediaImpl* LLViewerMedia::getMediaImplFromTextureID(const LLUUID& texture_id) { - impl_list::iterator iter = sViewerMediaImplList.begin(); - impl_list::iterator end = sViewerMediaImplList.end(); - - for(; iter != end; iter++) + LLViewerMediaImpl* result = NULL; + + // Look up the texture ID in the texture id->impl map. + impl_id_map::iterator iter = sViewerMediaTextureIDMap.find(texture_id); + if(iter != sViewerMediaTextureIDMap.end()) { - LLViewerMediaImpl* media_impl = *iter; - if(media_impl->getMediaTextureID() == texture_id) - { - return media_impl; - } + result = iter->second; } - return NULL; + + return result; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -378,28 +629,186 @@ bool LLViewerMedia::textureHasMedia(const LLUUID& texture_id) // static void LLViewerMedia::setVolume(F32 volume) { + if(volume != sGlobalVolume || sForceUpdate) + { + sGlobalVolume = volume; + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + pimpl->updateVolume(); + } + + sForceUpdate = false; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +F32 LLViewerMedia::getVolume() +{ + return sGlobalVolume; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::muteListChanged() +{ + // When the mute list changes, we need to check mute status on all impls. impl_list::iterator iter = sViewerMediaImplList.begin(); impl_list::iterator end = sViewerMediaImplList.end(); for(; iter != end; iter++) { LLViewerMediaImpl* pimpl = *iter; - LLPluginClassMedia* plugin = pimpl->getMediaPlugin(); - if(plugin) - { - plugin->setVolume(volume); - } - else - { - llwarns << "Plug-in already destroyed" << llendl; - } + pimpl->mNeedsMuteCheck = true; } } ////////////////////////////////////////////////////////////////////////////////////////// // static -void LLViewerMedia::updateMedia() +bool LLViewerMedia::isInterestingEnough(const LLVOVolume *object, const F64 &object_interest) { + bool result = false; + + if (NULL == object) + { + result = false; + } + // Focused? Then it is interesting! + else if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == object->getID()) + { + result = true; + } + // Selected? Then it is interesting! + // XXX Sadly, 'contains()' doesn't take a const :( + else if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast(object))) + { + result = true; + } + else + { + lldebugs << "object interest = " << object_interest << ", lowest loadable = " << sLowestLoadableImplInterest << llendl; + if(object_interest >= sLowestLoadableImplInterest) + result = true; + } + + return result; +} + +LLViewerMedia::impl_list &LLViewerMedia::getPriorityList() +{ + return sViewerMediaImplList; +} + +// This is the predicate function used to sort sViewerMediaImplList by priority. +bool LLViewerMedia::priorityComparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2) +{ + if(i1->isForcedUnloaded() && !i2->isForcedUnloaded()) + { + // Muted or failed items always go to the end of the list, period. + return false; + } + else if(i2->isForcedUnloaded() && !i1->isForcedUnloaded()) + { + // Muted or failed items always go to the end of the list, period. + return true; + } + else if(i1->hasFocus()) + { + // The item with user focus always comes to the front of the list, period. + return true; + } + else if(i2->hasFocus()) + { + // The item with user focus always comes to the front of the list, period. + return false; + } + else if(i1->isParcelMedia()) + { + // The parcel media impl sorts above all other inworld media, unless one has focus. + return true; + } + else if(i2->isParcelMedia()) + { + // The parcel media impl sorts above all other inworld media, unless one has focus. + return false; + } + else if(i1->getUsedInUI() && !i2->getUsedInUI()) + { + // i1 is a UI element, i2 is not. This makes i1 "less than" i2, so it sorts earlier in our list. + return true; + } + else if(i2->getUsedInUI() && !i1->getUsedInUI()) + { + // i2 is a UI element, i1 is not. This makes i2 "less than" i1, so it sorts earlier in our list. + return false; + } + else if(i1->isPlayable() && !i2->isPlayable()) + { + // Playable items sort above ones that wouldn't play even if they got high enough priority + return true; + } + else if(!i1->isPlayable() && i2->isPlayable()) + { + // Playable items sort above ones that wouldn't play even if they got high enough priority + return false; + } + else if(i1->getInterest() == i2->getInterest()) + { + // Generally this will mean both objects have zero interest. In this case, sort on distance. + return (i1->getProximityDistance() < i2->getProximityDistance()); + } + else + { + // The object with the larger interest value should be earlier in the list, so we reverse the sense of the comparison here. + return (i1->getInterest() > i2->getInterest()); + } +} + +static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2) +{ + if(i1->getProximityDistance() < i2->getProximityDistance()) + { + return true; + } + else if(i1->getProximityDistance() > i2->getProximityDistance()) + { + return false; + } + else + { + // Both objects have the same distance. This most likely means they're two faces of the same object. + // They may also be faces on different objects with exactly the same distance (like HUD objects). + // We don't actually care what the sort order is for this case, as long as it's stable and doesn't change when you enable/disable media. + // Comparing the impl pointers gives a completely arbitrary ordering, but it will be stable. + return (i1 < i2); + } +} + +static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE("Update Media"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SPARE_IDLE("Spare Idle"); +static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE_INTEREST("Update/Interest"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SORT("Sort"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SORT2("Sort 2"); +static LLFastTimer::DeclareTimer FTM_MEDIA_MISC("Misc"); + + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::updateMedia(void *dummy_arg) +{ + LLFastTimer t1(FTM_MEDIA_UPDATE); + + // Enable/disable the plugin read thread + LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread")); + + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + createSpareBrowserMediaSource(); + + sAnyMediaShowing = false; sUpdatedCookies = getCookieStore()->getChangedCookies(); if(!sUpdatedCookies.empty()) { @@ -410,11 +819,334 @@ void LLViewerMedia::updateMedia() impl_list::iterator iter = sViewerMediaImplList.begin(); impl_list::iterator end = sViewerMediaImplList.end(); + { + LLFastTimer t(FTM_MEDIA_UPDATE_INTEREST); + for(; iter != end;) + { + LLViewerMediaImpl* pimpl = *iter++; + pimpl->update(); + pimpl->calculateInterest(); + } + } + + // Let the spare media source actually launch + if(sSpareBrowserMediaSource) + { + LLFastTimer t(FTM_MEDIA_SPARE_IDLE); + sSpareBrowserMediaSource->idle(); + } + + { + LLFastTimer t(FTM_MEDIA_SORT); + // Sort the static instance list using our interest criteria + sViewerMediaImplList.sort(priorityComparitor); + } + + // Go through the list again and adjust according to priority. + iter = sViewerMediaImplList.begin(); + end = sViewerMediaImplList.end(); + + F64 total_cpu = 0.0f; + int impl_count_total = 0; + int impl_count_interest_low = 0; + int impl_count_interest_normal = 0; + + std::vector proximity_order; + + bool inworld_media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); + bool inworld_audio_enabled = gSavedSettings.getBOOL("AudioStreamingMusic"); + U32 max_instances = gSavedSettings.getU32("PluginInstancesTotal"); + U32 max_normal = gSavedSettings.getU32("PluginInstancesNormal"); + U32 max_low = gSavedSettings.getU32("PluginInstancesLow"); + F32 max_cpu = gSavedSettings.getF32("PluginInstancesCPULimit"); + // Setting max_cpu to 0.0 disables CPU usage checking. + bool check_cpu_usage = (max_cpu != 0.0f); + + LLViewerMediaImpl* lowest_interest_loadable = NULL; + + // Notes on tweakable params: + // max_instances must be set high enough to allow the various instances used in the UI (for the help browser, search, etc.) to be loaded. + // If max_normal + max_low is less than max_instances, things will tend to get unloaded instead of being set to slideshow. + + { + LLFastTimer t(FTM_MEDIA_MISC); + for(; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + + LLViewerMediaImpl::EPriority new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + + if(pimpl->isForcedUnloaded() || (impl_count_total >= (int)max_instances)) + { + // Never load muted or failed impls. + // Hard limit on the number of instances that will be loaded at one time + new_priority = LLViewerMediaImpl::PRIORITY_UNLOADED; + } + else if(!pimpl->getVisible()) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if(pimpl->hasFocus()) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIGH; + impl_count_interest_normal++; // count this against the count of "normal" instances for priority purposes + } + else if(pimpl->getUsedInUI()) + { + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else if(pimpl->isParcelMedia()) + { + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else + { + // Look at interest and CPU usage for instances that aren't in any of the above states. + + // Heuristic -- if the media texture's approximate screen area is less than 1/4 of the native area of the texture, + // turn it down to low instead of normal. This may downsample for plugins that support it. + bool media_is_small = false; + F64 approximate_interest = pimpl->getApproximateTextureInterest(); + if(approximate_interest == 0.0f) + { + // this media has no current size, which probably means it's not loaded. + media_is_small = true; + } + else if(pimpl->getInterest() < (approximate_interest / 4)) + { + media_is_small = true; + } + + if(pimpl->getInterest() == 0.0f) + { + // This media is completely invisible, due to being outside the view frustrum or out of range. + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if(check_cpu_usage && (total_cpu > max_cpu)) + { + // Higher priority plugins have already used up the CPU budget. Set remaining ones to slideshow priority. + new_priority = LLViewerMediaImpl::PRIORITY_SLIDESHOW; + } + else if((impl_count_interest_normal < (int)max_normal) && !media_is_small) + { + // Up to max_normal inworld get normal priority + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else if (impl_count_interest_low + impl_count_interest_normal < (int)max_low + (int)max_normal) + { + // The next max_low inworld get turned down + new_priority = LLViewerMediaImpl::PRIORITY_LOW; + impl_count_interest_low++; + + // Set the low priority size for downsampling to approximately the size the texture is displayed at. + { + F32 approximate_interest_dimension = (F32) sqrt(pimpl->getInterest()); + + pimpl->setLowPrioritySizeLimit(llround(approximate_interest_dimension)); + } + } + else + { + // Any additional impls (up to max_instances) get very infrequent time + new_priority = LLViewerMediaImpl::PRIORITY_SLIDESHOW; + } + } + + if(!pimpl->getUsedInUI() && (new_priority != LLViewerMediaImpl::PRIORITY_UNLOADED)) + { + // This is a loadable inworld impl -- the last one in the list in this class defines the lowest loadable interest. + lowest_interest_loadable = pimpl; + + impl_count_total++; + } + + // Overrides if the window is minimized or we lost focus (taking care + // not to accidentally "raise" the priority either) + if (!gViewerWindow->getActive() /* viewer window minimized? */ + && new_priority > LLViewerMediaImpl::PRIORITY_HIDDEN) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if (!gFocusMgr.getAppHasFocus() /* viewer window lost focus? */ + && new_priority > LLViewerMediaImpl::PRIORITY_LOW) + { + new_priority = LLViewerMediaImpl::PRIORITY_LOW; + } + + if(!inworld_media_enabled) + { + // If inworld media is locked out, force all inworld media to stay unloaded. + if(!pimpl->getUsedInUI()) + { + new_priority = LLViewerMediaImpl::PRIORITY_UNLOADED; + } + } + // update the audio stream here as well + if( !inworld_audio_enabled) + { + if(LLViewerMedia::isParcelAudioPlaying() && gAudiop && LLViewerMedia::hasParcelAudio()) + { + gAudiop->stopInternetStream(); + } + } + pimpl->setPriority(new_priority); + + if(pimpl->getUsedInUI()) + { + // Any impls used in the UI should not be in the proximity list. + pimpl->mProximity = -1; + } + else + { + proximity_order.push_back(pimpl); + } + + total_cpu += pimpl->getCPUUsage(); + + if (!pimpl->getUsedInUI() && pimpl->hasMedia()) + { + sAnyMediaShowing = true; + } + + } + } + + // Re-calculate this every time. + sLowestLoadableImplInterest = 0.0f; + + // Only do this calculation if we've hit the impl count limit -- up until that point we always need to load media data. + if(lowest_interest_loadable && (impl_count_total >= (int)max_instances)) + { + // Get the interest value of this impl's object for use by isInterestingEnough + LLVOVolume *object = lowest_interest_loadable->getSomeObject(); + if(object) + { + // NOTE: Don't use getMediaInterest() here. We want the pixel area, not the total media interest, + // so that we match up with the calculation done in LLMediaDataClient. + sLowestLoadableImplInterest = object->getPixelArea(); + } + } + + if(gSavedSettings.getBOOL("MediaPerformanceManagerDebug")) + { + // Give impls the same ordering as the priority list + // they're already in the right order for this. + } + else + { + LLFastTimer t(FTM_MEDIA_SORT2); + // Use a distance-based sort for proximity values. + std::stable_sort(proximity_order.begin(), proximity_order.end(), proximity_comparitor); + } + + // Transfer the proximity order to the proximity fields in the objects. + for(int i = 0; i < (int)proximity_order.size(); i++) + { + proximity_order[i]->mProximity = i; + } + + LL_DEBUGS("PluginPriority") << "Total reported CPU usage is " << total_cpu << llendl; + +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isAnyMediaShowing() +{ + return sAnyMediaShowing; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setAllMediaEnabled(bool val) +{ + // Set "tentative" autoplay first. We need to do this here or else + // re-enabling won't start up the media below. + gSavedSettings.setBOOL("MediaTentativeAutoPlay", val); + + // Then + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + for(; iter != end; iter++) { LLViewerMediaImpl* pimpl = *iter; - pimpl->update(); + if (!pimpl->getUsedInUI()) + { + pimpl->setDisabled(!val); + } } + + // Also do Parcel Media and Parcel Audio + if (val) + { + if (!LLViewerMedia::isParcelMediaPlaying() && LLViewerMedia::hasParcelMedia()) + { + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + } + + if (gSavedSettings.getBOOL("AudioStreamingMusic") && + !LLViewerMedia::isParcelAudioPlaying() && + gAudiop && + LLViewerMedia::hasParcelAudio()) + { + if (LLAudioEngine::AUDIO_PAUSED == gAudiop->isInternetStreamPlaying()) + { + // 'false' means unpause + gAudiop->pauseInternetStream(false); + } + else + { + gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL()); + } + } + } + else { + // This actually unloads the impl, as opposed to "stop"ping the media + LLViewerParcelMedia::stop(); + if (gAudiop) + { + gAudiop->stopInternetStream(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isParcelMediaPlaying() +{ + return (LLViewerMedia::hasParcelMedia() && LLViewerParcelMedia::getParcelMedia() && LLViewerParcelMedia::getParcelMedia()->hasMedia()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isParcelAudioPlaying() +{ + return (LLViewerMedia::hasParcelAudio() && gAudiop && LLAudioEngine::AUDIO_PLAYING == gAudiop->isInternetStreamPlaying()); +} + +bool LLViewerMedia::onAuthSubmit(const LLSD& notification, const LLSD& response) +{ + LLViewerMediaImpl *impl = LLViewerMedia::getMediaImplFromTextureID(notification["payload"]["media_id"]); + if(impl) + { + LLPluginClassMedia* media = impl->getMediaPlugin(); + if(media) + { + if (response["ok"]) + { + media->sendAuthResponse(true, response["username"], response["password"]); + } + else + { + media->sendAuthResponse(false, "", ""); + } + } + } + return false; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -470,12 +1202,9 @@ void LLViewerMedia::clearAllCookies() LLDirIterator dir_iter(base_dir, "*_*"); while (dir_iter.next(filename)) { - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += "browser_profile"; - target += gDirUtilp->getDirDelimiter(); - target += "cookies"; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, "browser_profile"); + gDirUtilp->append(target, "cookies"); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -483,10 +1212,8 @@ void LLViewerMedia::clearAllCookies() } // Other accounts may have new-style cookie files too -- delete them as well - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += PLUGIN_COOKIE_FILE_NAME; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, PLUGIN_COOKIE_FILE_NAME); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -529,6 +1256,24 @@ void LLViewerMedia::setCookiesEnabled(bool enabled) } } } + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setProxyConfig(bool enable, const std::string &host, int port) +{ + // Set the proxy config for all loaded plugins + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + for (; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + LLPluginClassMedia* plugin = pimpl->getMediaPlugin(); + if(plugin) + { + plugin->proxy_setup(enable, host, port); + } + } +} ///////////////////////////////////////////////////////////////////////////////////////// // static @@ -760,72 +1505,258 @@ void LLViewerMedia::openIDCookieResponse(const std::string &cookie) setOpenIDCookie(); } + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::proxyWindowOpened(const std::string &target, const std::string &uuid) +{ + if(uuid.empty()) + return; + + for (impl_list::iterator iter = sViewerMediaImplList.begin(); iter != sViewerMediaImplList.end(); iter++) + { + LLPluginClassMedia* plugin = (*iter)->getMediaPlugin(); + if(plugin && plugin->pluginSupportsMediaBrowser()) + { + plugin->proxyWindowOpened(target, uuid); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::proxyWindowClosed(const std::string &uuid) +{ + if(uuid.empty()) + return; + + for (impl_list::iterator iter = sViewerMediaImplList.begin(); iter != sViewerMediaImplList.end(); iter++) + { + LLPluginClassMedia* plugin = (*iter)->getMediaPlugin(); + if(plugin && plugin->pluginSupportsMediaBrowser()) + { + plugin->proxyWindowClosed(uuid); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::createSpareBrowserMediaSource() +{ + // If we don't have a spare browser media source, create one. + // However, if PluginAttachDebuggerToPlugins is set then don't spawn a spare + // SLPlugin process in order to not be confused by an unrelated gdb terminal + // popping up at the moment we start a media plugin. + if (!sSpareBrowserMediaSource && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")) + { + // The null owner will keep the browser plugin from fully initializing + // (specifically, it keeps LLPluginClassMedia from negotiating a size change, + // which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color) + sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType("text/html", NULL, 0, 0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource() +{ + LLPluginClassMedia* result = sSpareBrowserMediaSource; + sSpareBrowserMediaSource = NULL; + return result; +}; + +bool LLViewerMedia::hasInWorldMedia() +{ + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + // This should be quick, because there should be very few non-in-world-media impls + for (; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + if (!pimpl->getUsedInUI() && !pimpl->isParcelMedia()) + { + // Found an in-world media impl + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::hasParcelMedia() +{ + return !LLViewerParcelMedia::getURL().empty(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::hasParcelAudio() +{ + return !LLViewerMedia::getParcelAudioURL().empty(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +std::string LLViewerMedia::getParcelAudioURL() +{ + return LLViewerParcelMgr::getInstance()->getAgentParcel()->getMusicURL(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::initClass() +{ + gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL); + sTeleportFinishConnection = LLViewerParcelMgr::getInstance()-> + setTeleportFinishedCallback(boost::bind(&LLViewerMedia::onTeleportFinished)); +} + ////////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerMedia::cleanupClass() { - // This is no longer necessary, since the list is no longer smart pointers. -#if 0 - while(!sViewerMediaImplList.empty()) - { - sViewerMediaImplList.pop_back(); - } -#endif + gIdleCallbacks.deleteFunction(LLViewerMedia::updateMedia, NULL); + sTeleportFinishConnection.disconnect(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::onTeleportFinished() +{ + // On teleport, clear this setting (i.e. set it to true) + gSavedSettings.setBOOL("MediaTentativeAutoPlay", true); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setOnlyAudibleMediaTextureID(const LLUUID& texture_id) +{ + sOnlyAudibleTextureID = texture_id; + sForceUpdate = true; } ////////////////////////////////////////////////////////////////////////////////////////// // LLViewerMediaImpl ////////////////////////////////////////////////////////////////////////////////////////// -LLViewerMediaImpl::LLViewerMediaImpl(const std::string& media_url, - const LLUUID& texture_id, +LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop, - const std::string& mime_type) + U8 media_loop) : mMovieImageHasMips(false), - mTextureId(texture_id), mMediaWidth(media_width), mMediaHeight(media_height), mMediaAutoScale(media_auto_scale), mMediaLoop(media_loop), - mMediaURL(media_url), - mMimeType(mime_type), mNeedsNewTexture(true), mTextureUsedWidth(0), mTextureUsedHeight(0), mSuspendUpdates(false), mVisible(true), + mLastSetCursor( UI_CURSOR_ARROW ), + mMediaNavState( MEDIANAVSTATE_NONE ), + mInterest(0.0f), + mUsedInUI(false), mHasFocus(false), + mPriority(PRIORITY_UNLOADED), + mNavigateRediscoverType(false), + mNavigateServerRequest(false), + mMediaSourceFailed(false), + mRequestedVolume(1.0f), + mIsMuted(false), + mNeedsMuteCheck(false), + mPreviousMediaState(MEDIA_NONE), + mPreviousMediaTime(0.0f), + mIsDisabled(false), + mIsParcelMedia(false), + mProximity(-1), + mProximityDistance(0.0f), + mMimeTypeProbe(NULL), + mMediaAutoPlay(false), + mInNearbyMediaList(false), mClearCache(false), - mBackgroundColor(LLColor4::white) + mBackgroundColor(LLColor4::white), + mNavigateSuspended(false), + mNavigateSuspendedDeferred(false), + mIsUpdated(false), + mTrustedBrowser(false), + mZoomFactor(1.0) { - createMediaSource(); + + // Set up the mute list observer if it hasn't been set up already. + if(!sViewerMediaMuteListObserverInitialized) + { + LLMuteList::getInstance()->addObserver(&sViewerMediaMuteListObserver); + sViewerMediaMuteListObserverInitialized = true; + } + + add_media_impl(this); + + setTextureID(texture_id); + + // connect this media_impl to the media texture, creating it if it doesn't exist.0 + // This is necessary because we need to be able to use getMaxVirtualSize() even if the media plugin is not loaded. + LLViewerMediaTexture* media_tex = LLViewerTextureManager::getMediaTexture(mTextureId); + if(media_tex) + { + media_tex->setMediaImpl(); + } + } ////////////////////////////////////////////////////////////////////////////////////////// LLViewerMediaImpl::~LLViewerMediaImpl() { destroyMediaSource(); - LLViewerMedia::removeMedia(this); + + LLViewerMediaTexture::removeMediaImplFromTexture(mTextureId) ; + + setTextureID(); + remove_media_impl(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::emitEvent(LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event) +{ + // Broadcast to observers using the superclass version + LLViewerMediaEventEmitter::emitEvent(plugin, event); + + // If this media is on one or more LLVOVolume objects, tell them about the event as well. + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + while(iter != mObjectList.end()) + { + LLVOVolume *self = *iter; + ++iter; + self->mediaEvent(this, plugin, event); + } } ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) { - if((mPluginBase == NULL) || (mMimeType != mime_type)) + bool mimeTypeChanged = (mMimeType != mime_type); + bool pluginChanged = (LLMIMETypes::implType(mCurrentMimeType) != LLMIMETypes::implType(mime_type)); + if(!mPluginBase || pluginChanged) { if(! initializePlugin(mime_type)) { - LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << mime_type << LL_ENDL; + /*LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << mime_type << LL_ENDL; LLSD args; args["MIME_TYPE"] = mime_type; LLNotificationsUtil::add("NoPlugin", args); - return false; + return false;*/ } } + else if(mimeTypeChanged) + { + // The same plugin should be able to handle the new media -- just update the stored mime type. + mMimeType = mime_type; + } // play(); return (mPluginBase != NULL); @@ -834,28 +1765,46 @@ bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::createMediaSource() { + if(mPriority == PRIORITY_UNLOADED) + { + // This media shouldn't be created yet. + return; + } + if(! mMediaURL.empty()) { - navigateTo(mMediaURL, mMimeType, true); + navigateInternal(); } else if(! mMimeType.empty()) { - initializeMedia(mMimeType); + if (!initializeMedia(mMimeType)) + { + LL_WARNS("Media") << "Failed to initialize media for mime type " << mMimeType << LL_ENDL; + } } - + } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::destroyMediaSource() { mNeedsNewTexture = true; - if (!mPluginBase) + + // Tell the viewer media texture it's no longer active + LLViewerMediaTexture* oldImage = LLViewerTextureManager::findMediaTexture( mTextureId ); + if (oldImage) { - return; + oldImage->setPlaying(FALSE) ; } - // Restore the texture - updateMovieImage(LLUUID::null, false); - destroyPlugin(); + + cancelMimeTypeProbe(); + + if(mPluginBase) + { + mPluginBase->setDeleteOK(true) ; + destroyPlugin(); + } + } ////////////////////////////////////////////////////////////////////////////////////////// @@ -866,9 +1815,25 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type) ////////////////////////////////////////////////////////////////////////////////////////// /*static*/ -LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height) +LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target) { std::string plugin_basename = LLMIMETypes::implType(media_type); + LLPluginClassMedia* media_source = NULL; + + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + // If a spare was already created before PluginAttachDebuggerToPlugins was set, don't use it. + if(plugin_basename == "media_plugin_webkit" && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")) + { + media_source = LLViewerMedia::getSpareBrowserMediaSource(); + if(media_source) + { + media_source->setOwner(owner); + media_source->setTarget(target); + media_source->setSize(default_width, default_height); + + return media_source; + } + } if(plugin_basename.empty()) { @@ -908,7 +1873,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ } else { - LLPluginClassMedia* media_source = new LLPluginClassMedia(owner); + media_source = new LLPluginClassMedia(owner); media_source->setSize(default_width, default_height); media_source->setUserDataPath(user_data_path); media_source->setLanguageCode(LLUI::getLanguage()); @@ -928,6 +1893,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging"); media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled ); + media_source->setTarget(target); + const std::string plugin_dir = gDirUtilp->getLLPluginDir(); if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))) { @@ -966,7 +1933,21 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) // and unconditionally set the mime type mMimeType = media_type; - LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight); + if(mPriority == PRIORITY_UNLOADED) + { + // This impl should not be loaded at this time. + LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; + + return false; + } + + // If we got here, we want to ignore previous init failures. + mMediaSourceFailed = false; + + // Save the MIME type that really caused the plugin to load + mCurrentMimeType = mMimeType; + + LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget); if (media_source) { @@ -989,6 +1970,8 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "CA.pem" ); media_source->addCertificateFilePath( ca_path ); + media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort")); + if(mClearCache) { mClearCache = false; @@ -1007,13 +1990,71 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) } mPluginBase = media_source; + mPluginBase->setDeleteOK(false) ; + updateVolume(); return true; } + // Make sure the timer doesn't try re-initing this plugin repeatedly until something else changes. + mMediaSourceFailed = true; + return false; } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::loadURI() +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + // trim whitespace from front and back of URL - fixes EXT-5363 + LLStringUtil::trim( mMediaURL ); + + // *HACK: we don't know if the URI coming in is properly escaped + // (the contract doesn't specify whether it is escaped or not. + // but LLQtWebKit expects it to be, so we do our best to encode + // special characters) + // The strings below were taken right from http://www.ietf.org/rfc/rfc1738.txt + // Note especially that '%' and '/' are there. + std::string uri = LLURI::escape(mMediaURL, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "$-_.+" + "!*'()," + "{}|\\^~[]`" + "<>#%" + ";/?:@&=", + false); + llinfos << "Asking media source to load URI: " << uri << llendl; + + + plugin->loadURI( uri ); + + // A non-zero mPreviousMediaTime means that either this media was previously unloaded by the priority code while playing/paused, + // or a seek happened before the media loaded. In either case, seek to the saved time. + if(mPreviousMediaTime != 0.0f) + { + seek(mPreviousMediaTime); + } + + if(mPreviousMediaState == MEDIA_PLAYING) + { + // This media was playing before this instance was unloaded. + start(); + } + else if(mPreviousMediaState == MEDIA_PAUSED) + { + // This media was paused before this instance was unloaded. + pause(); + } + else + { + // No relevant previous media play state -- if we're loading the URL, we want to start playing. + start(); + } + } +} void LLViewerMediaImpl::setSize(int width, int height) { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1025,32 +2066,38 @@ void LLViewerMediaImpl::setSize(int width, int height) } } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::showNotification(LLNotificationPtr notify) +{ + mNotification = notify; +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::hideNotification() +{ + mNotification.reset(); +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::play() { LLPluginClassMedia* plugin = getMediaPlugin(); - // first stop any previously playing media - // stop(); - - // plugin->addObserver( this ); + // If the media source isn't there, try to initialize it and load an URL. if (!plugin) { - if(!initializePlugin(mMimeType)) + if(!initializeMedia(mMimeType)) { - // Plugin failed initialization... should assert or something + // This may be the case where the plugin's priority is PRIORITY_UNLOADED return; } - plugin = getMediaPlugin(); + + // Only do this if the media source was just loaded. + loadURI(); } - // updateMovieImage(mTextureId, true); - - plugin->loadURI( mMediaURL ); - if(/*plugin->pluginSupportsMediaTime()*/ true) - { - start(); - } + // always start the media + start(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1072,6 +2119,10 @@ void LLViewerMediaImpl::pause() { plugin->pause(); } + else + { + mPreviousMediaState = MEDIA_PAUSED; + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1082,6 +2133,10 @@ void LLViewerMediaImpl::start() { plugin->start(); } + else + { + mPreviousMediaState = MEDIA_PLAYING; + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1092,19 +2147,99 @@ void LLViewerMediaImpl::seek(F32 time) { plugin->seek(time); } + else + { + // Save the seek time to be set when the media is loaded. + mPreviousMediaTime = time; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::skipBack(F32 step_scale) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + if(plugin->pluginSupportsMediaTime()) + { + F64 back_step = plugin->getCurrentTime() - (plugin->getDuration()*step_scale); + if(back_step < 0.0) + { + back_step = 0.0; + } + plugin->seek(back_step); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::skipForward(F32 step_scale) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + if(plugin->pluginSupportsMediaTime()) + { + F64 forward_step = plugin->getCurrentTime() + (plugin->getDuration()*step_scale); + if(forward_step > plugin->getDuration()) + { + forward_step = plugin->getDuration(); + } + plugin->seek(forward_step); + } + } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::setVolume(F32 volume) +{ + mRequestedVolume = volume; + updateVolume(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateVolume() { LLPluginClassMedia* plugin = getMediaPlugin(); - if (plugin) + if(plugin) { - plugin->setVolume(volume); + // always scale the volume by the global media volume + F32 volume = mRequestedVolume * LLViewerMedia::getVolume(); + + if (mProximityCamera > 0) + { + if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMax")) + { + volume = 0; + } + else if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMin")) + { + // attenuated_volume = 1 / (roll_off_rate * (d - min))^2 + // the +1 is there so that for distance 0 the volume stays the same + F64 adjusted_distance = mProximityCamera - gSavedSettings.getF32("MediaRollOffMin"); + F64 attenuation = 1.0 + (gSavedSettings.getF32("MediaRollOffRate") * adjusted_distance); + attenuation = 1.0 / (attenuation * attenuation); + // the attenuation multiplier should never be more than one since that would increase volume + volume = volume * llmin(1.0, attenuation); + } + } + + if (sOnlyAudibleTextureID == LLUUID::null || sOnlyAudibleTextureID == mTextureId) + { + plugin->setVolume(volume); + } + else + { + plugin->setVolume(0.0f); + } } } ////////////////////////////////////////////////////////////////////////////////////////// +F32 LLViewerMediaImpl::getVolume() +{ + return mRequestedVolume; +} void LLViewerMediaImpl::focus(bool focus) { mHasFocus = focus; @@ -1131,6 +2266,17 @@ bool LLViewerMediaImpl::hasFocus() const return mHasFocus; } +std::string LLViewerMediaImpl::getCurrentMediaURL() +{ + if(!mCurrentMediaURL.empty()) + { + return mCurrentMediaURL; + } + + return mMediaURL; +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::clearCache() { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1144,6 +2290,18 @@ void LLViewerMediaImpl::clearCache() } } + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::setPageZoomFactor( double factor ) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin && factor != mZoomFactor) + { + mZoomFactor = factor; + plugin->set_page_zoom_factor( factor ); + } +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseDown(S32 x, S32 y, MASK mask, S32 button) { @@ -1298,6 +2456,78 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) } ////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateJavascriptObject() +{ + static LLFrameTimer timer ; + + LLPluginClassMedia* plugin = getMediaPlugin(); + if ( plugin ) + { + // flag to expose this information to internal browser or not. + bool enable = gSavedSettings.getBOOL("BrowserEnableJSObject"); + + if(!enable) + { + return ; //no need to go further. + } + + if(timer.getElapsedTimeF32() < 1.0f) + { + return ; //do not update more than once per second. + } + timer.reset() ; + + plugin->jsEnableObject( enable ); + + // these values are only menaingful after login so don't set them before + //bool logged_in = LLLoginInstance::getInstance()->authSuccess(); + bool logged_in = (LLStartUp::getStartupState() >= STATE_STARTED); + if ( logged_in ) + { + // current location within a region + LLVector3 agent_pos = gAgent.getPositionAgent(); + double x = agent_pos.mV[ VX ]; + double y = agent_pos.mV[ VY ]; + double z = agent_pos.mV[ VZ ]; + plugin->jsAgentLocationEvent( x, y, z ); + + // current location within the grid + LLVector3d agent_pos_global = gAgent.getLastPositionGlobal(); + double global_x = agent_pos_global.mdV[ VX ]; + double global_y = agent_pos_global.mdV[ VY ]; + double global_z = agent_pos_global.mdV[ VZ ]; + plugin->jsAgentGlobalLocationEvent( global_x, global_y, global_z ); + + // current agent orientation + double rotation = atan2( gAgent.getAtAxis().mV[VX], gAgent.getAtAxis().mV[VY] ); + double angle = rotation * RAD_TO_DEG; + if ( angle < 0.0f ) angle = 360.0f + angle; // TODO: has to be a better way to get orientation! + plugin->jsAgentOrientationEvent( angle ); + + // current region agent is in + std::string region_name(""); + LLViewerRegion* region = gAgent.getRegion(); + if ( region ) + { + region_name = region->getName(); + }; + plugin->jsAgentRegionEvent( region_name ); + } + + // language code the viewer is set to + plugin->jsAgentLanguageEvent( LLUI::getLanguage() ); + + // maturity setting the agent has selected + if ( gAgent.prefersAdult() ) + plugin->jsAgentMaturityEvent( "GMA" ); // Adult means see adult, mature and general content + else + if ( gAgent.prefersMature() ) + plugin->jsAgentMaturityEvent( "GM" ); // Mature means see mature and general content + else + if ( gAgent.prefersPG() ) + plugin->jsAgentMaturityEvent( "G" ); // PG means only see General content + } +} const std::string& LLViewerMediaImpl::getName() const { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1308,70 +2538,178 @@ const std::string& LLViewerMediaImpl::getName() const return LLStringUtil::null; }; + ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::navigateHome() +void LLViewerMediaImpl::navigateBack() { LLPluginClassMedia* plugin = getMediaPlugin(); if (plugin) { - plugin->loadURI( mHomeURL ); + plugin->browse_back(); } } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type) +void LLViewerMediaImpl::navigateForward() { LLPluginClassMedia* plugin = getMediaPlugin(); - if(rediscover_type) + if (plugin) + { + plugin->browse_forward(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateReload() +{ + navigateTo(getCurrentMediaURL(), "", true, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateHome() +{ + bool rediscover_mimetype = mHomeMimeType.empty(); + navigateTo(mHomeURL, mHomeMimeType, rediscover_mimetype, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::unload() +{ + // Unload the media impl and clear its state. + destroyMediaSource(); + resetPreviousMediaState(); + mMediaURL.clear(); + mMimeType.clear(); + mCurrentMediaURL.clear(); + mCurrentMimeType.clear(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request) +{ + cancelMimeTypeProbe(); + + if(mMediaURL != url) + { + // Don't carry media play state across distinct URLs. + resetPreviousMediaState(); + } + + // Always set the current URL and MIME type. + mMediaURL = url; + mMimeType = mime_type; + + // Clear the current media URL, since it will no longer be correct. + mCurrentMediaURL.clear(); + + // if mime type discovery was requested, we'll need to do it when the media loads + mNavigateRediscoverType = rediscover_type; + + // and if this was a server request, the navigate on load will also need to be one. + mNavigateServerRequest = server_request; + + // An explicit navigate resets the "failed" flag. + mMediaSourceFailed = false; + + if(mPriority == PRIORITY_UNLOADED) + { + // Helpful to have media urls in log file. Shouldn't be spammy. + llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; + + // This impl should not be loaded at this time. + LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; + + return; + } + + navigateInternal(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateInternal() +{ + // Helpful to have media urls in log file. Shouldn't be spammy. + llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl; + + if(mNavigateSuspended) + { + llwarns << "Deferring navigate." << llendl; + mNavigateSuspendedDeferred = true; + return; + } + + if(mMimeTypeProbe != NULL) + { + llwarns << "MIME type probe already in progress -- bailing out." << llendl; + return; + } + + if(mNavigateServerRequest) + { + setNavState(MEDIANAVSTATE_SERVER_SENT); + } + else + { + setNavState(MEDIANAVSTATE_NONE); + } + + // If the caller has specified a non-empty MIME type, look that up in our MIME types list. + // If we have a plugin for that MIME type, use that instead of attempting auto-discovery. + // This helps in supporting legacy media content where the server the media resides on returns a bogus MIME type + // but the parcel owner has correctly set the MIME type in the parcel media settings. + + if(!mMimeType.empty() && (mMimeType != LLMIMETypes::getDefaultMimeType())) + { + std::string plugin_basename = LLMIMETypes::implType(mMimeType); + if(!plugin_basename.empty()) + { + // We have a plugin for this mime type + mNavigateRediscoverType = false; + } + } + + if(mNavigateRediscoverType) { - LLURI uri(url); + LLURI uri(mMediaURL); std::string scheme = uri.scheme(); - if(scheme.empty() || ("http" == scheme || "https" == scheme)) + if(scheme.empty() || "http" == scheme || "https" == scheme) { - if(mime_type.empty()) - { - LLHTTPClient::getHeaderOnly(url, new LLMimeDiscoveryResponder(this, "text/html")); - } - else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) - { - plugin->loadURI( url ); - } + // If we don't set an Accept header, LLHTTPClient will add one like this: + // Accept: application/llsd+xml + // which is really not what we want. + AIHTTPHeaders headers; + headers.addHeader("Accept", "*/*"); + headers.addHeader("Cookie", ""); + LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this, "text/html"), headers); } else if("data" == scheme || "file" == scheme || "about" == scheme) { // FIXME: figure out how to really discover the type for these schemes // We use "data" internally for a text/html url for loading the login screen - if(initializeMedia("text/html") && (plugin = getMediaPlugin())) + if(initializeMedia("text/html")) { - plugin->loadURI( url ); + loadURI(); } } else { // This catches 'rtsp://' urls - if(initializeMedia(scheme) && (plugin = getMediaPlugin())) + if(initializeMedia(scheme)) { - plugin->loadURI( url ); + loadURI(); } } } - else if (plugin) + else if(initializeMedia(mMimeType)) { - plugin->loadURI( url ); - } - else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) - { - plugin->loadURI( url ); + loadURI(); } else { - LL_WARNS("Media") << "Couldn't navigate to: " << url << " as there is no media type for: " << mime_type << LL_ENDL; - return; + LL_WARNS("Media") << "Couldn't navigate to: " << mMediaURL << " as there is no media type for: " << mMimeType << LL_ENDL; } - mMediaURL = url; - } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1475,50 +2813,55 @@ bool LLViewerMediaImpl::canNavigateBack() return result; } - ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::updateMovieImage(const LLUUID& uuid, BOOL active) -{ - // IF the media image hasn't changed, do nothing - if (mTextureId == uuid) - { - return; - } - // If we have changed media uuid, restore the old one - if (!mTextureId.isNull()) - { - LLViewerTexture* oldImage = LLViewerTextureManager::findTexture( mTextureId ); - if (oldImage) - { - // Casting to LLViewerMediaTexture is a huge hack. Implement LLViewerMediaTexture some time later. - ((LLViewerMediaTexture*)oldImage)->reinit(mMovieImageHasMips); - oldImage->mIsMediaTexture = FALSE; - } - } - // If the movie is playing, set the new media image - if (active && !uuid.isNull()) - { - LLViewerTexture* viewerImage = LLViewerTextureManager::findTexture( uuid ); - if( viewerImage ) - { - mTextureId = uuid; - // Can't use mipmaps for movies because they don't update the full image - // Casting to LLViewerMediaTexture is a huge hack. Implement LLViewerMediaTexture some time later. - mMovieImageHasMips = ((LLViewerMediaTexture*)viewerImage)->getUseMipMaps(); - ((LLViewerMediaTexture*)viewerImage)->reinit(FALSE); - viewerImage->mIsMediaTexture = TRUE; - } - } -} +static LLFastTimer::DeclareTimer FTM_MEDIA_DO_UPDATE("Do Update"); +static LLFastTimer::DeclareTimer FTM_MEDIA_GET_DATA("Get Data"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SET_SUBIMAGE("Set Subimage"); + -////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::update() { - LLPluginClassMedia* plugin = getMediaPlugin(); + LLFastTimer t(FTM_MEDIA_DO_UPDATE); - if(plugin) + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin == NULL) { - // If we didn't just create the impl, it may need to get cookie updates. + if(mPriority == PRIORITY_UNLOADED) + { + // This media source should not be loaded. + } + else if(mPriority <= PRIORITY_SLIDESHOW) + { + // Don't load new instances that are at PRIORITY_SLIDESHOW or below. They're just kept around to preserve state. + } + else if(mMimeTypeProbe != NULL) + { + // this media source is doing a MIME type probe -- don't try loading it again. + } + else + { + // This media may need to be loaded. + if(sMediaCreateTimer.hasExpired()) + { + LL_DEBUGS("PluginPriority") << this << ": creating media based on timer expiration" << LL_ENDL; + createMediaSource(); + sMediaCreateTimer.setTimerExpirySec(LLVIEWERMEDIA_CREATE_DELAY); + } + else + { + LL_DEBUGS("PluginPriority") << this << ": NOT creating media (waiting on timer)" << LL_ENDL; + } + } + } + else + { + updateVolume(); + + // TODO: this is updated every frame - is this bad? + updateJavascriptObject(); + + // If we didn't just create the impl, it may need to get cookie updates. if(!sUpdatedCookies.empty()) { // TODO: Only send cookies to plugins that need them @@ -1526,15 +2869,28 @@ void LLViewerMediaImpl::update() } } - if (!plugin) + + if(plugin == NULL) { return; } + // Make sure a navigate doesn't happen during the idle -- it can cause mMediaSource to get destroyed, which can cause a crash. + setNavigateSuspended(true); + plugin->idle(); + + setNavigateSuspended(false); + + plugin = getMediaPlugin(); + if(plugin == NULL) + { + return; + } if (plugin->isPluginExited()) { + resetPreviousMediaState(); destroyMediaSource(); return; } @@ -1549,11 +2905,15 @@ void LLViewerMediaImpl::update() return; } - LLViewerTexture* placeholder_image = updatePlaceholderImage(); + LLViewerMediaTexture* placeholder_image = updatePlaceholderImage(); if(placeholder_image) { LLRect dirty_rect; + + // Since we're updating this texture, we know it's playing. Tell the texture to do its replacement magic so it gets rendered. + placeholder_image->setPlaying(TRUE); + if (plugin->getDirty(&dirty_rect)) { // Constrain the dirty rect to be inside the texture @@ -1565,21 +2925,28 @@ void LLViewerMediaImpl::update() if(width > 0 && height > 0) { - U8* data = plugin->getBitsData(); + U8* data = NULL; + { + LLFastTimer t(FTM_MEDIA_GET_DATA); + data = plugin->getBitsData(); + } // Offset the pixels pointer to match x_pos and y_pos data += ( x_pos * plugin->getTextureDepth() * plugin->getBitsWidth() ); data += ( y_pos * plugin->getTextureDepth() ); - placeholder_image->setSubImage( - data, - plugin->getBitsWidth(), - plugin->getBitsHeight(), - x_pos, - y_pos, - width, - height, - TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) + { + LLFastTimer t(FTM_MEDIA_SET_SUBIMAGE); + placeholder_image->setSubImage( + data, + plugin->getBitsWidth(), + plugin->getBitsHeight(), + x_pos, + y_pos, + width, + height, + TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) + } } @@ -1596,7 +2963,7 @@ void LLViewerMediaImpl::updateImagesMediaStreams() ////////////////////////////////////////////////////////////////////////////////////////// -/*LLViewerMediaTexture*/LLViewerTexture* LLViewerMediaImpl::updatePlaceholderImage() +LLViewerMediaTexture* LLViewerMediaImpl::updatePlaceholderImage() { if(mTextureId.isNull()) { @@ -1604,14 +2971,12 @@ void LLViewerMediaImpl::updateImagesMediaStreams() return NULL; } - LLViewerMediaTexture* placeholder_image = (LLViewerMediaTexture*)LLViewerTextureManager::getFetchedTexture( mTextureId ); - placeholder_image->getLastReferencedTimer()->reset(); - + LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mTextureId ); + LLPluginClassMedia* plugin = getMediaPlugin(); if (mNeedsNewTexture || placeholder_image->getUseMipMaps() - || !placeholder_image->mIsMediaTexture || (placeholder_image->getWidth() != plugin->getTextureWidth()) || (placeholder_image->getHeight() != plugin->getTextureHeight()) || (mTextureUsedWidth != plugin->getWidth()) @@ -1647,10 +3012,11 @@ void LLViewerMediaImpl::updateImagesMediaStreams() placeholder_image->createGLTexture(discard_level, raw); // placeholder_image->setExplicitFormat() - placeholder_image->setUseMipMaps(FALSE); + //placeholder_image->setUseMipMaps(FALSE); // MEDIAOPT: set this dynamically on play/stop - placeholder_image->mIsMediaTexture = true; + // FIXME +// placeholder_image->mIsMediaTexture = true; mNeedsNewTexture = false; // If the amount of the texture being drawn by the media goes down in either width or height, @@ -1664,7 +3030,7 @@ void LLViewerMediaImpl::updateImagesMediaStreams() ////////////////////////////////////////////////////////////////////////////////////////// -LLUUID LLViewerMediaImpl::getMediaTextureID() +LLUUID LLViewerMediaImpl::getMediaTextureID() const { return mTextureId; } @@ -1687,11 +3053,6 @@ void LLViewerMediaImpl::setVisible(bool visible) createMediaSource(); } } - - if(plugin) - { - plugin->setPriority(mVisible?LLPluginClassBasic::PRIORITY_NORMAL:LLPluginClassBasic::PRIORITY_SLEEP); - } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1700,36 +3061,6 @@ void LLViewerMediaImpl::mouseCapture() gFocusMgr.setMouseCapture(this); } -////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::getTextureSize(S32 *texture_width, S32 *texture_height) -{ - LLPluginClassMedia* plugin = getMediaPlugin(); - if(plugin && plugin->textureValid()) - { - S32 real_texture_width = plugin->getBitsWidth(); - S32 real_texture_height = plugin->getBitsHeight(); - - { - // The "texture width" coming back from the plugin may not be a power of two (thanks to webkit). - // It will be the correct "data width" to pass to setSubImage - int i; - - for(i = 1; i < real_texture_width; i <<= 1) - ; - *texture_width = i; - - for(i = 1; i < real_texture_height; i <<= 1) - ; - *texture_height = i; - } - - } - else - { - *texture_width = 0; - *texture_height = 0; - } -} ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) { @@ -1744,6 +3075,20 @@ void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) #endif } +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::isMediaTimeBased() +{ + bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin) + { + result = plugin->pluginSupportsMediaTime(); + } + + return result; +} + ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::isMediaPlaying() { @@ -1776,29 +3121,337 @@ bool LLViewerMediaImpl::isMediaPaused() ////////////////////////////////////////////////////////////////////////////////////////// // -bool LLViewerMediaImpl::hasMedia() +bool LLViewerMediaImpl::hasMedia() const { return mPluginBase != NULL; } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event) +// +void LLViewerMediaImpl::resetPreviousMediaState() { + mPreviousMediaState = MEDIA_NONE; + mPreviousMediaTime = 0.0f; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaImpl::setDisabled(bool disabled, bool forcePlayOnEnable) +{ + if(mIsDisabled != disabled) + { + // Only do this on actual state transitions. + mIsDisabled = disabled; + + if(mIsDisabled) + { + // We just disabled this media. Clear all state. + unload(); + } + else + { + // We just (re)enabled this media. Do a navigate if auto-play is in order. + if(isAutoPlayable() || forcePlayOnEnable) + { + navigateTo(mMediaEntryURL, "", true, true); + } + } + + } +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isForcedUnloaded() const +{ + if(mIsMuted || mMediaSourceFailed || mIsDisabled) + { + return true; + } + + // If this media's class is not supposed to be shown, unload + if (!shouldShowBasedOnClass()) + { + return true; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isPlayable() const +{ + if(isForcedUnloaded()) + { + // All of the forced-unloaded criteria also imply not playable. + return false; + } + + if(hasMedia()) + { + // Anything that's already playing is, by definition, playable. + return true; + } + + if(!mMediaURL.empty()) + { + // If something has navigated the instance, it's ready to be played. + return true; + } + + return false; +} + +static void handle_pick_file_request_continued(LLPluginClassMedia* plugin, AIFilePicker* filepicker) +{ + std::string response; + + if(filepicker->hasFilename()) + { + response = filepicker->getFilename(); + } + + plugin->sendPickFileResponse(response); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent event) +{ + bool pass_through = true; switch(event) { - case MEDIA_EVENT_PLUGIN_FAILED: + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: { + LL_DEBUGS("Media") << "MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is: " << plugin->getClickURL() << LL_ENDL; + std::string url = plugin->getClickURL(); + std::string nav_type = plugin->getClickNavType(); + LLURLDispatcher::dispatch(url, nav_type, NULL, mTrustedBrowser); + } + break; + case MEDIA_EVENT_CLICK_LINK_HREF: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << plugin->getClickTarget() << "\", uri is " << plugin->getClickURL() << LL_ENDL; + }; + break; + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + { + // The plugin failed to load properly. Make sure the timer doesn't retry. + // TODO: maybe mark this plugin as not loadable somehow? + mMediaSourceFailed = true; + + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); + + // TODO: may want a different message for this case? LLSD args; - args["PLUGIN"] = LLMIMETypes::implType(mMimeType); + args["PLUGIN"] = LLMIMETypes::implType(mCurrentMimeType); LLNotificationsUtil::add("MediaPluginFailed", args); } break; + + case MEDIA_EVENT_PLUGIN_FAILED: + { + // The plugin crashed. + mMediaSourceFailed = true; + + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); + + LLSD args; + args["PLUGIN"] = LLMIMETypes::implType(mCurrentMimeType); + // SJB: This is getting called every frame if the plugin fails to load, continuously respawining the alert! + //LLNotificationsUtil::add("MediaPluginFailed", args); + } + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << plugin->getCursorName() << LL_ENDL; + + std::string cursor = plugin->getCursorName(); + + if(cursor == "arrow") + mLastSetCursor = UI_CURSOR_ARROW; + else if(cursor == "ibeam") + mLastSetCursor = UI_CURSOR_IBEAM; + else if(cursor == "splith") + mLastSetCursor = UI_CURSOR_SIZEWE; + else if(cursor == "splitv") + mLastSetCursor = UI_CURSOR_SIZENS; + else if(cursor == "hand") + mLastSetCursor = UI_CURSOR_HAND; + else // for anything else, default to the arrow + mLastSetCursor = UI_CURSOR_ARROW; + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_BEGIN: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_BEGIN, uri is: " << plugin->getNavigateURI() << LL_ENDL; + hideNotification(); + + if(getNavState() == MEDIANAVSTATE_SERVER_SENT) + { + setNavState(MEDIANAVSTATE_SERVER_BEGUN); + } + else + { + setNavState(MEDIANAVSTATE_BEGUN); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_COMPLETE, uri is: " << plugin->getNavigateURI() << LL_ENDL; + + std::string url = plugin->getNavigateURI(); + if(getNavState() == MEDIANAVSTATE_BEGUN) + { + if(mCurrentMediaURL == url) + { + // This is a navigate that takes us to the same url as the previous navigate. + setNavState(MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS); + } + else + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED); + } + } + else if(getNavState() == MEDIANAVSTATE_SERVER_BEGUN) + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED); + } + else + { + // all other cases need to leave the state alone. + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_LOCATION_CHANGED, uri is: " << plugin->getLocation() << LL_ENDL; + + std::string url = plugin->getLocation(); + + if(getNavState() == MEDIANAVSTATE_BEGUN) + { + if(mCurrentMediaURL == url) + { + // This is a navigate that takes us to the same url as the previous navigate. + setNavState(MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS); + } + else + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_FIRST_LOCATION_CHANGED); + } + } + else if(getNavState() == MEDIANAVSTATE_SERVER_BEGUN) + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED); + } + else + { + // Don't track redirects. + setNavState(MEDIANAVSTATE_NONE); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_PICK_FILE_REQUEST: + { + AIFilePicker* filepicker = AIFilePicker::create(); + filepicker->open(FFLOAD_ALL, "", "openfile", true); + filepicker->run(boost::bind(&handle_pick_file_request_continued, plugin, filepicker)); + // Display a file picker + //std::string response; + + /*LLFilePicker& picker = LLFilePicker::instance(); + if (!picker.getOpenFile(LLFilePicker::FFLOAD_ALL)) + { + // The user didn't pick a file -- the empty response string will indicate this. + } + + response = picker.getFirstFile(); + + plugin->sendPickFileResponse(response);*/ + } + break; + + + case LLViewerMediaObserver::MEDIA_EVENT_AUTH_REQUEST: + { + LLNotification::Params auth_request_params("AuthRequest"); + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( plugin->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = plugin->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mTextureId); + auth_request_params.functor(boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2)); + LLNotifications::instance().add(auth_request_params); + }; + break; + + case LLViewerMediaObserver::MEDIA_EVENT_CLOSE_REQUEST: + { + std::string uuid = plugin->getClickUUID(); + + llinfos << "MEDIA_EVENT_CLOSE_REQUEST for uuid " << uuid << llendl; + + if(uuid.empty()) + { + // This close request is directed at this instance, let it fall through. + } + else + { + // This close request is directed at another instance + pass_through = false; + LLFloaterWebContent::closeRequest(uuid); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_GEOMETRY_CHANGE: + { + std::string uuid = plugin->getClickUUID(); + + llinfos << "MEDIA_EVENT_GEOMETRY_CHANGE for uuid " << uuid << llendl; + + if(uuid.empty()) + { + // This geometry change request is directed at this instance, let it fall through. + } + else + { + // This request is directed at another instance + pass_through = false; + LLFloaterWebContent::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight()); + } + } + break; + default: break; } - // Just chain the event to observers. - emitEvent(self, event); + + if(pass_through) + { + // Just chain the event to observers. + emitEvent(plugin, event); + } } + //////////////////////////////////////////////////////////////////////////////// // virtual void LLViewerMediaImpl::handleCookieSet(LLPluginClassMedia* self, const std::string &cookie) @@ -1872,6 +3525,133 @@ LLViewerMediaImpl::canPaste() const return FALSE; } +void LLViewerMediaImpl::setUpdated(BOOL updated) +{ + mIsUpdated = updated ; +} + +BOOL LLViewerMediaImpl::isUpdated() +{ + return mIsUpdated ; +} + +static LLFastTimer::DeclareTimer FTM_MEDIA_CALCULATE_INTEREST("Calculate Interest"); + +void LLViewerMediaImpl::calculateInterest() +{ + LLFastTimer t(FTM_MEDIA_CALCULATE_INTEREST); + LLViewerMediaTexture* texture = LLViewerTextureManager::findMediaTexture( mTextureId ); + + if(texture != NULL) + { + mInterest = texture->getMaxVirtualSize(); + } + else + { + // This will be a relatively common case now, since it will always be true for unloaded media. + mInterest = 0.0f; + } + + // Calculate distance from the avatar, for use in the proximity calculation. + mProximityDistance = 0.0f; + mProximityCamera = 0.0f; + if(!mObjectList.empty()) + { + // Just use the first object in the list. We could go through the list and find the closest object, but this should work well enough. + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + LLVOVolume* objp = *iter ; + llassert_always(objp != NULL) ; + + // The distance calculation is invalid for HUD attachments -- leave both mProximityDistance and mProximityCamera at 0 for them. + if(!objp->isHUDAttachment()) + { + LLVector3d obj_global = objp->getPositionGlobal() ; + LLVector3d agent_global = gAgent.getPositionGlobal() ; + LLVector3d global_delta = agent_global - obj_global ; + mProximityDistance = global_delta.magVecSquared(); // use distance-squared because it's cheaper and sorts the same. + + LLVector3d camera_delta = gAgentCamera.getCameraPositionGlobal() - obj_global; + mProximityCamera = camera_delta.magVec(); + } + } + + if(mNeedsMuteCheck) + { + // Check all objects this instance is associated with, and those objects' owners, against the mute list + mIsMuted = false; + + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + for(; iter != mObjectList.end() ; ++iter) + { + LLVOVolume *obj = *iter; + llassert(obj); + if (!obj) continue; + if(LLMuteList::getInstance() && + LLMuteList::getInstance()->isMuted(obj->getID())) + { + mIsMuted = true; + } + else + { + // We won't have full permissions data for all objects. Attempt to mute objects when we can tell their owners are muted. + if (LLSelectMgr::getInstance()) + { + LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(obj); + if(obj_perm) + { + if(LLMuteList::getInstance() && + LLMuteList::getInstance()->isMuted(obj_perm->getOwner())) + mIsMuted = true; + } + } + } + } + + mNeedsMuteCheck = false; + } +} + +F64 LLViewerMediaImpl::getApproximateTextureInterest() +{ + F64 result = 0.0f; + + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + result = plugin->getFullWidth(); + result *= plugin->getFullHeight(); + } + else + { + // No media source is loaded -- all we have to go on is the texture size that has been set on the impl, if any. + result = mMediaWidth; + result *= mMediaHeight; + } + + return result; +} + +void LLViewerMediaImpl::setUsedInUI(bool used_in_ui) +{ + mUsedInUI = used_in_ui; + + // HACK: Force elements used in UI to load right away. + // This fixes some issues where UI code that uses the browser instance doesn't expect it to be unloaded. + if(mUsedInUI && (mPriority == PRIORITY_UNLOADED)) + { + if(getVisible()) + { + setPriority(PRIORITY_NORMAL); + } + else + { + setPriority(PRIORITY_HIDDEN); + } + + createMediaSource(); + } +}; + void LLViewerMediaImpl::setBackgroundColor(LLColor4 color) { mBackgroundColor = color; @@ -1882,3 +3662,317 @@ void LLViewerMediaImpl::setBackgroundColor(LLColor4 color) plugin->setBackgroundColor(mBackgroundColor); } }; + +F64 LLViewerMediaImpl::getCPUUsage() const +{ + F64 result = 0.0f; + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin) + { + result = plugin->getCPUUsage(); + } + + return result; +} + +char const* PRIORITYToString(LLViewerMediaImpl::EPriority priority) +{ + switch(priority) + { + case LLViewerMediaImpl::PRIORITY_UNLOADED: return "unloaded"; + case LLViewerMediaImpl::PRIORITY_HIDDEN: return "hidden"; + case LLViewerMediaImpl::PRIORITY_SLIDESHOW: return "slideshow"; + case LLViewerMediaImpl::PRIORITY_LOW: return "low"; + case LLViewerMediaImpl::PRIORITY_NORMAL: return "normal"; + case LLViewerMediaImpl::PRIORITY_HIGH: return "high"; + default: return "UNKNOWN"; + } +} + +void LLViewerMediaImpl::setPriority(EPriority priority) +{ + if(mPriority != priority) + { + LL_DEBUGS("PluginPriority") + << "changing priority of media id " << mTextureId + << " from " << ::PRIORITYToString(mPriority) + << " to " << ::PRIORITYToString(priority) + << LL_ENDL; + } + + mPriority = priority; + + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(priority == PRIORITY_UNLOADED) + { + if(plugin) + { + // Need to unload the media source + + // First, save off previous media state + mPreviousMediaState = plugin->getStatus(); + mPreviousMediaTime = plugin->getCurrentTime(); + + destroyMediaSource(); + } + } + + + + if(plugin) + { + if(mPriority >= PRIORITY_LOW) + plugin->setPriority((LLPluginClassBasic::EPriority)((U32)mPriority-((U32)PRIORITY_LOW-1))); + else + plugin->setPriority(LLPluginClassBasic::PRIORITY_SLEEP); + } + + // NOTE: loading (or reloading) media sources whose priority has risen above PRIORITY_UNLOADED is done in update(). +} + +void LLViewerMediaImpl::setLowPrioritySizeLimit(int size) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + plugin->setLowPrioritySizeLimit(size); + } +} + +void LLViewerMediaImpl::setNavState(EMediaNavState state) +{ + mMediaNavState = state; + + switch (state) + { + case MEDIANAVSTATE_NONE: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_NONE" << llendl; break; + case MEDIANAVSTATE_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_BEGUN" << llendl; break; + case MEDIANAVSTATE_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS" << llendl; break; + case MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS" << llendl; break; + case MEDIANAVSTATE_SERVER_SENT: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_SENT" << llendl; break; + case MEDIANAVSTATE_SERVER_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_BEGUN" << llendl; break; + case MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; + } +} + +void LLViewerMediaImpl::setNavigateSuspended(bool suspend) +{ + if(mNavigateSuspended != suspend) + { + mNavigateSuspended = suspend; + if(!suspend) + { + // We're coming out of suspend. If someone tried to do a navigate while suspended, do one now instead. + if(mNavigateSuspendedDeferred) + { + mNavigateSuspendedDeferred = false; + navigateInternal(); + } + } + } +} + +void LLViewerMediaImpl::cancelMimeTypeProbe() +{ + if(mMimeTypeProbe != NULL) + { + // There doesn't seem to be a way to actually cancel an outstanding request. + // Simulate it by telling the LLMimeDiscoveryResponder not to write back any results. + mMimeTypeProbe->cancelRequest(); + + // The above should already have set mMimeTypeProbe to NULL. + if(mMimeTypeProbe != NULL) + { + llerrs << "internal error: mMimeTypeProbe is not NULL after cancelling request." << llendl; + } + } +} + +void LLViewerMediaImpl::addObject(LLVOVolume* obj) +{ + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + for(; iter != mObjectList.end() ; ++iter) + { + if(*iter == obj) + { + return ; //already in the list. + } + } + + mObjectList.push_back(obj) ; + mNeedsMuteCheck = true; +} + +void LLViewerMediaImpl::removeObject(LLVOVolume* obj) +{ + mObjectList.remove(obj) ; + mNeedsMuteCheck = true; +} + +const std::list< LLVOVolume* >* LLViewerMediaImpl::getObjectList() const +{ + return &mObjectList ; +} + +LLVOVolume *LLViewerMediaImpl::getSomeObject() +{ + LLVOVolume *result = NULL; + + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + if(iter != mObjectList.end()) + { + result = *iter; + } + + return result; +} + +void LLViewerMediaImpl::setTextureID(LLUUID id) +{ + if(id != mTextureId) + { + if(mTextureId.notNull()) + { + // Remove this item's entry from the map + sViewerMediaTextureIDMap.erase(mTextureId); + } + + if(id.notNull()) + { + sViewerMediaTextureIDMap.insert(LLViewerMedia::impl_id_map::value_type(id, this)); + } + + mTextureId = id; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isAutoPlayable() const +{ + return (mMediaAutoPlay && + gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay")); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::shouldShowBasedOnClass() const +{ + // If this is parcel media or in the UI, return true always + if (getUsedInUI() || isParcelMedia()) return true; + + bool attached_to_another_avatar = isAttachedToAnotherAvatar(); + bool inside_parcel = isInAgentParcel(); + + // llinfos << " hasFocus = " << hasFocus() << + // " others = " << (attached_to_another_avatar && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING)) << + // " within = " << (inside_parcel && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING)) << + // " outside = " << (!inside_parcel && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING)) << llendl; + + // If it has focus, we should show it + // This is incorrect, and causes EXT-6750 (disabled attachment media still plays) +// if (hasFocus()) +// return true; + + // If it is attached to an avatar and the pref is off, we shouldn't show it + if (attached_to_another_avatar) + { + static LLCachedControl show_media_on_others(gSavedSettings, LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING); + return show_media_on_others; + } + if (inside_parcel) + { + static LLCachedControl show_media_within_parcel(gSavedSettings, LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING); + + return show_media_within_parcel; + } + else + { + static LLCachedControl show_media_outside_parcel(gSavedSettings, LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING); + + return show_media_outside_parcel; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isAttachedToAnotherAvatar() const +{ + bool result = false; + + std::list< LLVOVolume* >::const_iterator iter = mObjectList.begin(); + std::list< LLVOVolume* >::const_iterator end = mObjectList.end(); + for ( ; iter != end; iter++) + { + if (isObjectAttachedToAnotherAvatar(*iter)) + { + result = true; + break; + } + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +//static +bool LLViewerMediaImpl::isObjectAttachedToAnotherAvatar(LLVOVolume *obj) +{ + bool result = false; + LLXform *xform = obj; + // Walk up parent chain + while (NULL != xform) + { + LLViewerObject *object = dynamic_cast (xform); + if (NULL != object) + { + LLVOAvatar *avatar = object->asAvatar(); + if ((NULL != avatar) && (avatar != gAgentAvatarp)) + { + result = true; + break; + } + } + xform = xform->getParent(); + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isInAgentParcel() const +{ + bool result = false; + + std::list< LLVOVolume* >::const_iterator iter = mObjectList.begin(); + std::list< LLVOVolume* >::const_iterator end = mObjectList.end(); + for ( ; iter != end; iter++) + { + LLVOVolume *object = *iter; + if (LLViewerMediaImpl::isObjectInAgentParcel(object)) + { + result = true; + break; + } + } + return result; +} + +LLNotificationPtr LLViewerMediaImpl::getCurrentNotification() const +{ + return mNotification; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// static +bool LLViewerMediaImpl::isObjectInAgentParcel(LLVOVolume *obj) +{ + return (LLViewerParcelMgr::getInstance()->inAgentParcel(obj->getPositionGlobal())); +} diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 0c9e4e77f..8be43590c 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -34,48 +34,109 @@ #define LLVIEWERMEDIA_H #include "llfocusmgr.h" +#include "lleditmenuhandler.h" + #include "llpanel.h" -#include "llviewermediaeventemitter.h" -#include "llviewerpluginmanager.h" +#include "llpluginclassmediaowner.h" + +#include "llviewermediaobserver.h" + #include "llpluginclassmedia.h" #include "v4color.h" +#include "llnotificationptr.h" + #include "llurl.h" +#include "llviewerpluginmanager.h" + class LLViewerMediaImpl; class LLUUID; -class LLSD; -class LLViewerTexture; +class LLViewerMediaTexture; +class LLMediaEntry; +class LLVOVolume; +class LLMimeDiscoveryResponder; class LLPluginCookieStore; class AIHTTPHeaders; typedef LLPointer viewer_media_t; +/////////////////////////////////////////////////////////////////////////////// +// +class LLViewerMediaEventEmitter +{ +public: + virtual ~LLViewerMediaEventEmitter(); + + bool addObserver( LLViewerMediaObserver* subject ); + bool remObserver( LLViewerMediaObserver* subject ); + virtual void emitEvent(LLPluginClassMedia* self, LLViewerMediaObserver::EMediaEvent event); + +private: + typedef std::list< LLViewerMediaObserver* > observerListType; + observerListType mObservers; +}; + +class LLViewerMediaImpl; class LLViewerMedia { LOG_CLASS(LLViewerMedia); - public: - // Special case early init for just web browser component - // so we can show login screen. See .cpp file for details. JC - - static viewer_media_t newMediaImpl(const std::string& media_url, - const LLUUID& texture_id, - S32 media_width, - S32 media_height, - U8 media_auto_scale, - U8 media_loop, - std::string mime_type = "none/none"); - - static void removeMedia(LLViewerMediaImpl* media); - static LLViewerMediaImpl* getMediaImplFromTextureID(const LLUUID& texture_id); - static std::string getCurrentUserAgent(); - static void updateBrowserUserAgent(); - static bool handleSkinCurrentChanged(const LLSD& /*newvalue*/); - static bool textureHasMedia(const LLUUID& texture_id); - static void setVolume(F32 volume); - - static void updateMedia(); - - static void cleanupClass(); +public: + + // String to get/set media autoplay in gSavedSettings + static const char* AUTO_PLAY_MEDIA_SETTING; + static const char* SHOW_MEDIA_ON_OTHERS_SETTING; + static const char* SHOW_MEDIA_WITHIN_PARCEL_SETTING; + static const char* SHOW_MEDIA_OUTSIDE_PARCEL_SETTING; + + typedef std::list impl_list; + + typedef std::map impl_id_map; + // Special case early init for just web browser component + // so we can show login screen. See .cpp file for details. JC + + static viewer_media_t newMediaImpl(const LLUUID& texture_id, + S32 media_width = 0, + S32 media_height = 0, + U8 media_auto_scale = false, + U8 media_loop = false); + + static viewer_media_t updateMediaImpl(LLMediaEntry* media_entry, const std::string& previous_url, bool update_from_self); + static LLViewerMediaImpl* getMediaImplFromTextureID(const LLUUID& texture_id); + static std::string getCurrentUserAgent(); + static void updateBrowserUserAgent(); + static bool handleSkinCurrentChanged(const LLSD& /*newvalue*/); + static bool textureHasMedia(const LLUUID& texture_id); + static void setVolume(F32 volume); + + // Is any media currently "showing"? Includes Parcel Media. Does not include media in the UI. + static bool isAnyMediaShowing(); + // Set all media enabled or disabled, depending on val. Does not include media in the UI. + static void setAllMediaEnabled(bool val); + + static void updateMedia(void* dummy_arg = NULL); + + static void initClass(); + static void cleanupClass(); + + static F32 getVolume(); + static void muteListChanged(); + static bool isInterestingEnough(const LLVOVolume* object, const F64 &object_interest); + + // Returns the priority-sorted list of all media impls. + static impl_list &getPriorityList(); + + // This is the comparitor used to sort the list. + static bool priorityComparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2); + + // These are just helper functions for the convenience of others working with media + static bool hasInWorldMedia(); + static std::string getParcelAudioURL(); + static bool hasParcelMedia(); + static bool hasParcelAudio(); + static bool isParcelMediaPlaying(); + static bool isParcelAudioPlaying(); + + static bool onAuthSubmit(const LLSD& notification, const LLSD& response); // Clear all cookies for all plugins static void clearAllCookies(); @@ -86,6 +147,9 @@ class LLViewerMedia // Set the "cookies enabled" flag for all loaded plugins static void setCookiesEnabled(bool enabled); + // Set the proxy config for all loaded plugins + static void setProxyConfig(bool enable, const std::string &host, int port); + static LLPluginCookieStore *getCookieStore(); static void loadCookieFile(); static void saveCookieFile(); @@ -95,15 +159,25 @@ class LLViewerMedia static void openIDSetup(const std::string &openid_url, const std::string &openid_token); static void openIDCookieResponse(const std::string &cookie); + + static void proxyWindowOpened(const std::string &target, const std::string &uuid); + static void proxyWindowClosed(const std::string &uuid); + + static void createSpareBrowserMediaSource(); + static LLPluginClassMedia* getSpareBrowserMediaSource(); + + static void setOnlyAudibleMediaTextureID(const LLUUID& texture_id); static AIHTTPHeaders getHeaders(); private: static void setOpenIDCookie(); - + static void onTeleportFinished(); + static LLPluginCookieStore *sCookieStore; static LLURL sOpenIDURL; static std::string sOpenIDCookie; + static LLPluginClassMedia* sSpareBrowserMediaSource; }; // Implementation functions not exported into header file @@ -112,32 +186,44 @@ class LLViewerMediaImpl { LOG_CLASS(LLViewerMediaImpl); public: - + friend class LLViewerMedia; - - LLViewerMediaImpl(const std::string& media_url, + friend class LLMimeDiscoveryResponder; + + LLViewerMediaImpl( const LLUUID& texture_id, S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop, - const std::string& mime_type); + U8 media_loop); ~LLViewerMediaImpl(); + + // Override inherited version from LLViewerMediaEventEmitter + virtual void emitEvent(LLPluginClassMedia* self, LLViewerMediaObserver::EMediaEvent event); + void createMediaSource(); void destroyMediaSource(); void setMediaType(const std::string& media_type); bool initializeMedia(const std::string& mime_type); bool initializePlugin(const std::string& media_type); + void loadURI(); LLPluginClassMedia* getMediaPlugin() const { return (LLPluginClassMedia*)mPluginBase; } void setSize(int width, int height); + void showNotification(LLNotificationPtr notify); + void hideNotification(); + void play(); void stop(); void pause(); void start(); void seek(F32 time); + void skipBack(F32 step_scale); + void skipForward(F32 step_scale); void setVolume(F32 volume); + void updateVolume(); + F32 getVolume(); void focus(bool focus); // True if the impl has user focus. bool hasFocus() const; @@ -150,37 +236,68 @@ public: void mouseDoubleClick(S32 x,S32 y, MASK mask, S32 button = 0); void scrollWheel(S32 x, S32 y, MASK mask); void mouseCapture(); - + + void navigateBack(); + void navigateForward(); + void navigateReload(); void navigateHome(); - void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false); + void unload(); + void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false); + void navigateInternal(); void navigateStop(); bool handleKeyHere(KEY key, MASK mask); bool handleUnicodeCharHere(llwchar uni_char); bool canNavigateForward(); bool canNavigateBack(); - std::string getMediaURL() { return mMediaURL; } + std::string getMediaURL() const { return mMediaURL; } + std::string getCurrentMediaURL(); std::string getHomeURL() { return mHomeURL; } - void setHomeURL(const std::string& home_url) { mHomeURL = home_url; } + std::string getMediaEntryURL() { return mMediaEntryURL; } + void setHomeURL(const std::string& home_url, const std::string& mime_type = LLStringUtil::null) { mHomeURL = home_url; mHomeMimeType = mime_type;}; void clearCache(); + void setPageZoomFactor( double factor ); std::string getMimeType() { return mMimeType; } - void getTextureSize(S32 *texture_width, S32 *texture_height); void scaleMouse(S32 *mouse_x, S32 *mouse_y); void scaleTextureCoords(const LLVector2& texture_coords, S32 *x, S32 *y); - void updateMovieImage(const LLUUID& image_id, BOOL active); void update(); void updateImagesMediaStreams(); - LLUUID getMediaTextureID(); + LLUUID getMediaTextureID() const; - void suspendUpdates(bool suspend) { mSuspendUpdates = suspend; }; + void suspendUpdates(bool suspend) { mSuspendUpdates = suspend; } void setVisible(bool visible); + bool getVisible() const { return mVisible; } + bool isVisible() const { return mVisible; } + bool isMediaTimeBased(); bool isMediaPlaying(); bool isMediaPaused(); - bool hasMedia(); + bool hasMedia() const; + bool isMediaFailed() const { return mMediaSourceFailed; } + void setMediaFailed(bool val) { mMediaSourceFailed = val; } + void resetPreviousMediaState(); + + void setDisabled(bool disabled, bool forcePlayOnEnable = false); + bool isMediaDisabled() const { return mIsDisabled; }; + + void setInNearbyMediaList(bool in_list) { mInNearbyMediaList = in_list; } + bool getInNearbyMediaList() { return mInNearbyMediaList; } + + // returns true if this instance should not be loaded (disabled, muted object, crashed, etc.) + bool isForcedUnloaded() const; + + // returns true if this instance could be playable based on autoplay setting, current load state, etc. + bool isPlayable() const; + + void setIsParcelMedia(bool is_parcel_media) { mIsParcelMedia = is_parcel_media; } + bool isParcelMedia() const { return mIsParcelMedia; } + ECursorType getLastSetCursor() { return mLastSetCursor; } + + void setTarget(const std::string& target) { mTarget = target; } + // utility function to create a ready-to-use media instance from a desired media type. - static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height); + static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null); // Internally set our desired browser user agent string, including // the Second Life version and skin name. Used because we can @@ -208,12 +325,13 @@ public: /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask) {return FALSE; }; /*virtual*/ const std::string& getName() const; /*virtual*/ BOOL isView() const { return FALSE; }; + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}; /*virtual*/ BOOL hasMouseCapture() { return gFocusMgr.getMouseCapture() == this; }; // Inherited from LLPluginClassMediaOwner - /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent); /*virtual*/ void handleCookieSet(LLPluginClassMedia* self, const std::string &cookie); // LLEditMenuHandler overrides @@ -225,32 +343,152 @@ public: /*virtual*/ void paste(); /*virtual*/ BOOL canPaste() const; + + void addObject(LLVOVolume* obj) ; + void removeObject(LLVOVolume* obj) ; + const std::list< LLVOVolume* >* getObjectList() const ; + LLVOVolume *getSomeObject(); + void setUpdated(BOOL updated) ; + BOOL isUpdated() ; + + // updates the javascript object in the embedded browser with viewer values + void updateJavascriptObject(); + + // Updates the "interest" value in this object + void calculateInterest(); + F64 getInterest() const { return mInterest; }; + F64 getApproximateTextureInterest(); + S32 getProximity() const { return mProximity; }; + F64 getProximityDistance() const { return mProximityDistance; }; + + // Mark this object as being used in a UI panel instead of on a prim + // This will be used as part of the interest sorting algorithm. + void setUsedInUI(bool used_in_ui); + bool getUsedInUI() const { return mUsedInUI; }; - bool mNeedsNewTexture; void setBackgroundColor(LLColor4 color); + + F64 getCPUUsage() const; + + typedef enum + { + PRIORITY_UNLOADED, // media plugin isn't even loaded. + PRIORITY_HIDDEN, // media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc. + PRIORITY_SLIDESHOW, // media is in the far distance, updates very infrequently + PRIORITY_LOW, // media is in the distance, may be rendered at reduced size + PRIORITY_NORMAL, // normal (default) priority + PRIORITY_HIGH // media has user focus and/or is taking up most of the screen + }EPriority; + + void setPriority(EPriority priority); + EPriority getPriority() { return mPriority; }; + + void setLowPrioritySizeLimit(int size); + + void setTextureID(LLUUID id = LLUUID::null); + + bool isTrustedBrowser() { return mTrustedBrowser; } + void setTrustedBrowser(bool trusted) { mTrustedBrowser = trusted; } + + typedef enum + { + MEDIANAVSTATE_NONE, // State is outside what we need to track for navigation. + MEDIANAVSTATE_BEGUN, // a MEDIA_EVENT_NAVIGATE_BEGIN has been received which was not server-directed + MEDIANAVSTATE_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a non-server-directed BEGIN + MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS, // Same as above, but the new URL is identical to the previously navigated URL. + MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED, // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED + MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS,// Same as above, but the new URL is identical to the previously navigated URL. + MEDIANAVSTATE_SERVER_SENT, // server-directed nav has been requested, but MEDIA_EVENT_NAVIGATE_BEGIN hasn't been received yet + MEDIANAVSTATE_SERVER_BEGUN, // MEDIA_EVENT_NAVIGATE_BEGIN has been received which was server-directed + MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a server-directed BEGIN + MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED + + }EMediaNavState; + + // Returns the current nav state of the media. + // note that this will be updated BEFORE listeners and objects receive media messages + EMediaNavState getNavState() { return mMediaNavState; } + void setNavState(EMediaNavState state); + + void setNavigateSuspended(bool suspend); + bool isNavigateSuspended() { return mNavigateSuspended; }; + + void cancelMimeTypeProbe(); + + // Is this media attached to an avatar *not* self + bool isAttachedToAnotherAvatar() const; + + // Is this media in the agent's parcel? + bool isInAgentParcel() const; + + // get currently active notification associated with this media instance + LLNotificationPtr getCurrentNotification() const; + +private: + bool isAutoPlayable() const; + bool shouldShowBasedOnClass() const; + static bool isObjectAttachedToAnotherAvatar(LLVOVolume *obj); + static bool isObjectInAgentParcel(LLVOVolume *obj); + private: // a single media url with some data and an impl. + F64 mZoomFactor; LLUUID mTextureId; bool mMovieImageHasMips; - std::string mMediaURL; + std::string mMediaURL; // The last media url set with NavigateTo std::string mHomeURL; + std::string mHomeMimeType; // forced mime type for home url std::string mMimeType; + std::string mCurrentMediaURL; // The most current media url from the plugin (via the "location changed" or "navigate complete" events). + std::string mCurrentMimeType; // The MIME type that caused the currently loaded plugin to be loaded. S32 mLastMouseX; // save the last mouse coord we get, so when we lose capture we can simulate a mouseup at that point. S32 mLastMouseY; S32 mMediaWidth; S32 mMediaHeight; bool mMediaAutoScale; bool mMediaLoop; + bool mNeedsNewTexture; S32 mTextureUsedWidth; S32 mTextureUsedHeight; bool mSuspendUpdates; bool mVisible; + ECursorType mLastSetCursor; + EMediaNavState mMediaNavState; + F64 mInterest; + bool mUsedInUI; bool mHasFocus; + EPriority mPriority; + bool mNavigateRediscoverType; + bool mNavigateServerRequest; + bool mMediaSourceFailed; + F32 mRequestedVolume; + bool mIsMuted; + bool mNeedsMuteCheck; + int mPreviousMediaState; + F64 mPreviousMediaTime; + bool mIsDisabled; + bool mIsParcelMedia; + S32 mProximity; + F64 mProximityDistance; + F64 mProximityCamera; + LLMimeDiscoveryResponder *mMimeTypeProbe; + bool mMediaAutoPlay; + std::string mMediaEntryURL; + bool mInNearbyMediaList; // used by LLPanelNearbyMedia::refreshList() for performance reasons bool mClearCache; LLColor4 mBackgroundColor; + bool mNavigateSuspended; + bool mNavigateSuspendedDeferred; + bool mTrustedBrowser; + std::string mTarget; + LLNotificationPtr mNotification; private: - /*LLViewerMediaTexture*/LLViewerTexture *updatePlaceholderImage(); + BOOL mIsUpdated ; + std::list< LLVOVolume* > mObjectList ; + +private: + LLViewerMediaTexture *updatePlaceholderImage(); }; #endif // LLVIEWERMEDIA_H diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index 9ad2c8f0b..77239a848 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -2,31 +2,25 @@ * @file llviewermediafocus.cpp * @brief Governs focus on Media prims * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,7 +30,7 @@ //LLViewerMediaFocus #include "llviewerobjectlist.h" -#include "llpanelmediahud.h" +#include "llpanelprimmediacontrols.h" #include "llpluginclassmedia.h" #include "llagent.h" #include "llagentcamera.h" @@ -49,12 +43,18 @@ #include "llparcel.h" #include "llviewerparcelmgr.h" #include "llweb.h" +#include "llmediaentry.h" +#include "llkeyboard.h" +#include "lltoolmgr.h" +#include "llvovolume.h" + // // LLViewerMediaFocus // LLViewerMediaFocus::LLViewerMediaFocus() -: mMouseOverFlag(false) +: mFocusedObjectFace(0), + mHoverObjectFace(0) { } @@ -64,83 +64,136 @@ LLViewerMediaFocus::~LLViewerMediaFocus() // Clean up in cleanupClass() instead. } -void LLViewerMediaFocus::cleanupClass() -{ - LLViewerMediaFocus *self = LLViewerMediaFocus::getInstance(); +void LLViewerMediaFocus::setFocusFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(self) + LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl(); + if(old_media_impl) { - // mMediaHUD will have been deleted by this point -- don't try to delete it. - - /* Richard says: - all widgets are supposed to be destroyed at the same time - you shouldn't hold on to pointer to them outside of ui code - you can use the LLHandle approach - if you want to be type safe, you'll need to add a LLRootHandle to whatever derived class you are pointing to - look at llview::gethandle - its our version of a weak pointer - */ - if(self->mMediaHUD.get()) - { - self->mMediaHUD.get()->setMediaImpl(NULL); - } - self->mMediaImpl = NULL; + old_media_impl->focus(false); } -} + // Always clear the current selection. If we're setting focus on a face, we'll reselect the correct object below. + LLSelectMgr::getInstance()->deselectAll(); + mSelection = NULL; - -void LLViewerMediaFocus::setFocusFace( BOOL b, LLPointer objectp, S32 face, viewer_media_t media_impl ) -{ - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (b && media_impl.notNull()) + if (media_impl.notNull() && objectp.notNull()) { - mMediaImpl = media_impl; - LLSelectMgr::getInstance()->deselectAll(); - LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + bool face_auto_zoom = false; - mFocus = LLSelectMgr::getInstance()->getSelection(); - if(mMediaHUD.get() && ! parcel->getMediaPreventCameraZoom()) + mFocusedImplID = media_impl->getMediaTextureID(); + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + mFocusedObjectNormal = pick_normal; + + // Set the selection in the selection manager so we can draw the focus ring. + mSelection = LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + + // Focusing on a media face clears its disable flag. + media_impl->setDisabled(false); + + LLTextureEntry* tep = objectp->getTE(face); + if(tep->hasMedia()) { - mMediaHUD.get()->resetZoomLevel(); - mMediaHUD.get()->nextZoomLevel(); + LLMediaEntry* mep = tep->getMediaData(); + face_auto_zoom = mep->getAutoZoom(); + if(!media_impl->hasMedia()) + { + std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL(); + media_impl->navigateTo(url, "", true); + } } - if (!mFocus->isEmpty()) + else { - gFocusMgr.setKeyboardFocus(this); + // This should never happen. + llwarns << "Can't find media entry for focused face" << llendl; } - mObjectID = objectp->getID(); - // LLViewerMedia::addObserver(this, mObjectID); + media_impl->focus(true); + gFocusMgr.setKeyboardFocus(this); + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (impl) + { + LLEditMenuHandler::gEditMenuHandler = impl; + } + + // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. + update(); + if(mMediaControls.get()) + { + if(face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) + { + // Zoom in on this face + mMediaControls.get()->resetZoomLevel(false); + mMediaControls.get()->nextZoomLevel(); + } + else + { + // Reset the controls' zoom level without moving the camera. + // This fixes the case where clicking focus between two non-autozoom faces doesn't change the zoom-out button back to a zoom-in button. + mMediaControls.get()->resetZoomLevel(false); + } + } } else { - gFocusMgr.setKeyboardFocus(NULL); - if(! parcel->getMediaPreventCameraZoom()) + if(hasFocus()) { - if (!mFocus->isEmpty()) - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - } - } - mFocus = NULL; - // LLViewerMedia::remObserver(this, mObjectID); - - // Null out the media hud media pointer - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaImpl(NULL); + gFocusMgr.setKeyboardFocus(NULL); } - // and null out the media impl - mMediaImpl = NULL; - } - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaFocus(b); + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (LLEditMenuHandler::gEditMenuHandler == impl) + { + LLEditMenuHandler::gEditMenuHandler = NULL; + } + + + mFocusedImplID = LLUUID::null; + if (objectp.notNull()) + { + // Still record the focused object...it may mean we need to load media data. + // This will aid us in determining this object is "important enough" + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + } + else { + mFocusedObjectID = LLUUID::null; + mFocusedObjectFace = 0; + } } } + +void LLViewerMediaFocus::clearFocus() +{ + setFocusFace(NULL, 0, NULL); +} + +void LLViewerMediaFocus::setHoverFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull()) + { + mHoverImplID = media_impl->getMediaTextureID(); + mHoverObjectID = objectp->getID(); + mHoverObjectFace = face; + mHoverObjectNormal = pick_normal; + } + else + { + mHoverObjectID = LLUUID::null; + mHoverObjectFace = 0; + mHoverImplID = LLUUID::null; + } +} + +void LLViewerMediaFocus::clearHover() +{ + setHoverFace(NULL, 0, NULL); +} + + bool LLViewerMediaFocus::getFocus() { if (gFocusMgr.getKeyboardFocus() == this) @@ -150,22 +203,15 @@ bool LLViewerMediaFocus::getFocus() return false; } -// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value -void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) +// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value +void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) { - LLPickInfo& pick = LLToolPie::getInstance()->getPick(); - - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - pick = mPickInfo; - setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); - } - - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + if (object) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + LLBBox bbox = object->getBoundingBoxAgent(); + LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); F32 height; F32 width; F32 depth; @@ -173,8 +219,10 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. - F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); + F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); + + lldebugs << "normal = " << normal << ", aspect_ratio = " << aspect_ratio << ", camera_aspect = " << camera_aspect << llendl; // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is @@ -191,88 +239,137 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect()); distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + + lldebugs << "using width (" << width << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } else { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + + lldebugs << "using height (" << height << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } distance += depth * 0.5; // Finally animate the camera to this new position and focal point - gAgentCamera.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), - LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + LLVector3d camera_pos, target_pos; + // The target lookat position is the center of the selection (in global coords) + target_pos = center; + // Target look-from (camera) position is "distance" away from the target along the normal + LLVector3d pickNormal = LLVector3d(normal); + pickNormal.normalize(); + camera_pos = target_pos + pickNormal * distance; + if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) + { + // If the normal points directly up, the camera will "flip" around. + // We try to avoid this by adjusting the target camera position a + // smidge towards current camera position + // *NOTE: this solution is not perfect. All it attempts to solve is the + // "looking down" problem where the camera flips around when it animates + // to that position. You still are not guaranteed to be looking at the + // media in the correct orientation. What this solution does is it will + // put the camera into position keeping as best it can the current + // orientation with respect to the face. In other words, if before zoom + // the media appears "upside down" from the camera, after zooming it will + // still be upside down, but at least it will not flip. + LLVector3d cur_camera_pos = LLVector3d(gAgentCamera.getCameraPositionGlobal()); + LLVector3d delta = (cur_camera_pos - camera_pos); + F64 len = delta.length(); + delta.normalize(); + // Move 1% of the distance towards original camera location + camera_pos += 0.01 * len * delta; + } + + // If we are not allowing zooming out and the old camera position is closer to + // the center then the new intended camera position, don't move camera and return + if (zoom_in_only && + (dist_vec_squared(gAgentCamera.getCameraPositionGlobal(), target_pos) < dist_vec_squared(camera_pos, target_pos))) + { + return; + } + + gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); + + } + else + { + // If we have no object, focus back on the avatar. + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); } } void LLViewerMediaFocus::onFocusReceived() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(true); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->focus(true); LLFocusableElement::onFocusReceived(); } void LLViewerMediaFocus::onFocusLost() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(false); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->focus(false); + gViewerWindow->focusClient(); - mFocus = NULL; LLFocusableElement::onFocusLost(); } -void LLViewerMediaFocus::setMouseOverFlag(bool b, viewer_media_t media_impl) -{ - if (b && media_impl.notNull()) - { - if(! mMediaHUD.get()) - { - LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(mMediaImpl); - mMediaHUD = media_hud->getHandle(); - gHUDView->addChild(media_hud); - } - mMediaHUD.get()->setMediaImpl(media_impl); - mMediaImpl = media_impl; - } - mMouseOverFlag = b; -} -LLUUID LLViewerMediaFocus::getSelectedUUID() -{ - LLViewerObject* object = mFocus->getFirstObject(); - return object ? object->getID() : LLUUID::null; -} -#if 0 // Must re-implement when the new media api event system is ready -void LLViewerMediaFocus::onNavigateComplete( const EventType& event_in ) -{ - if (hasFocus() && mLastURL != event_in.getStringValue()) - { - LLViewerMedia::focus(true, mObjectID); - // spoof mouse event to reassert focus - LLViewerMedia::mouseDown(1,1, mObjectID); - LLViewerMedia::mouseUp(1,1, mObjectID); - } - mLastURL = event_in.getStringValue(); -} -#endif + BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleKeyHere(key, mask); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + { + media_impl->handleKeyHere(key, mask); + + if (KEY_ESCAPE == key) + { + // Reset camera zoom in this case. + if(mFocusedImplID.notNull()) + { + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(true); + } + } + + clearFocus(); + } + + /*if ( KEY_F1 == key && LLUI::sHelpImpl && mMediaControls.get()) + { + std::string help_topic; + if (mMediaControls.get()->findHelpTopic(help_topic)) + { + LLUI::sHelpImpl->showTopic(help_topic); + } + }*/ + } + return true; } BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleUnicodeCharHere(uni_char); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->handleUnicodeCharHere(uni_char); return true; } BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) { BOOL retval = FALSE; - if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl && media_impl->hasMedia()) { - mMediaImpl->getMediaPlugin()->scrollEvent(x, y, clicks); + // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y. + // The latter is the position of the mouse at the time of the event + // The former is the 'scroll amount' in x and y, respectively. + // All we have for 'scroll amount' here is 'clicks'. + // We're also not passed the keyboard modifier mask, but we can get that from gKeyboard. + media_impl->getMediaPlugin()->scrollEvent(0, clicks, gKeyboard->currentMask(TRUE)); retval = TRUE; } return retval; @@ -280,19 +377,67 @@ BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLViewerMediaFocus::update() { - if (mMediaHUD.get()) + if(mFocusedImplID.notNull()) { - if(mFocus.notNull() || mMouseOverFlag || mMediaHUD.get()->isMouseOver()) + // We have a focused impl/face. + if(!getFocus()) { - // mMediaHUD.get()->setVisible(true); - mMediaHUD.get()->updateShape(); + // We've lost keyboard focus -- check to see whether the media controls have it + if(mMediaControls.get() && mMediaControls.get()->hasFocus()) + { + // the media controls have focus -- don't clear. + } + else + { + // Someone else has focus -- back off. + clearFocus(); + } } - else + else if(LLToolMgr::getInstance()->inBuildMode()) { - mMediaHUD.get()->setVisible(false); + // Build tools are selected -- clear focus. + clearFocus(); + } + } + + + LLViewerMediaImpl *media_impl = getFocusedMediaImpl(); + LLViewerObject *viewer_object = getFocusedObject(); + S32 face = mFocusedObjectFace; + LLVector3 normal = mFocusedObjectNormal; + + if(!media_impl || !viewer_object) + { + media_impl = getHoverMediaImpl(); + viewer_object = getHoverObject(); + face = mHoverObjectFace; + normal = mHoverObjectNormal; + } + + if(media_impl && viewer_object) + { + // We have an object and impl to point at. + + // Make sure the media HUD object exists. + if(! mMediaControls.get()) + { + LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls(); + mMediaControls = media_controls->getHandle(); + gHUDView->addChild(media_controls); + } + mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal); + } + else + { + // The media HUD is no longer needed. + if(mMediaControls.get()) + { + mMediaControls.get()->setMediaFace(NULL, 0, NULL); } } } + + // This function calculates the aspect ratio and the world aligned components of a selection bounding box. F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) { @@ -305,40 +450,38 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& LLVector3 bbox_max = bbox.getExtentLocal(); F32 dot1 = 0.f; F32 dot2 = 0.f; + + lldebugs << "bounding box local size = " << bbox_max << ", local_normal = " << local_normal << llendl; // The largest component of the localized normal vector is the depth component // meaning that the other two are the legs of the rectangle. local_normal.abs(); - if(local_normal.mV[VX] > local_normal.mV[VY]) + + // Using temporary variables for these makes the logic a bit more readable. + bool XgtY = (local_normal.mV[VX] > local_normal.mV[VY]); + bool XgtZ = (local_normal.mV[VX] > local_normal.mV[VZ]); + bool YgtZ = (local_normal.mV[VY] > local_normal.mV[VZ]); + + if(XgtY && XgtZ) { - if(local_normal.mV[VX] > local_normal.mV[VZ]) - { - // Use the y and z comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VX]; - } - else - { - // Use the x and y comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VZ]; - } + lldebugs << "x component of normal is longest, using y and z" << llendl; + comp1.mV[VY] = bbox_max.mV[VY]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VX]; } - else if(local_normal.mV[VY] > local_normal.mV[VZ]) + else if(!XgtY && YgtZ) { - // Use the x and z comps + lldebugs << "y component of normal is longest, using x and z" << llendl; comp1.mV[VX] = bbox_max.mV[VX]; comp2.mV[VZ] = bbox_max.mV[VZ]; *depth = bbox_max.mV[VY]; } else { - // Use the x and y comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VX]; + lldebugs << "z component of normal is longest, using x and y" << llendl; + comp1.mV[VX] = bbox_max.mV[VX]; + comp2.mV[VY] = bbox_max.mV[VY]; + *depth = bbox_max.mV[VZ]; } // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value) @@ -348,13 +491,114 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& { *height = comp1.length(); *width = comp2.length(); + + lldebugs << "comp1 = " << comp1 << ", height = " << *height << llendl; + lldebugs << "comp2 = " << comp2 << ", width = " << *width << llendl; } else { *height = comp2.length(); *width = comp1.length(); + + lldebugs << "comp2 = " << comp2 << ", height = " << *height << llendl; + lldebugs << "comp1 = " << comp1 << ", width = " << *width << llendl; } + + lldebugs << "returning " << (*width / *height) << llendl; // Return the aspect ratio. return *width / *height; } + +bool LLViewerMediaFocus::isFocusedOnFace(LLPointer objectp, S32 face) +{ + return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace; +} + +bool LLViewerMediaFocus::isHoveringOverFace(LLPointer objectp, S32 face) +{ + return objectp->getID() == mHoverObjectID && face == mHoverObjectFace; +} + + +LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mFocusedImplID); +} + +LLViewerObject* LLViewerMediaFocus::getFocusedObject() +{ + return gObjectList.findObject(mFocusedObjectID); +} + +LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mHoverImplID); +} + +LLViewerObject* LLViewerMediaFocus::getHoverObject() +{ + return gObjectList.findObject(mHoverObjectID); +} + +void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id) +{ + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(media_id); + + if(impl) + { + // Get the first object from the media impl's object list. This is completely arbitrary, but should suffice. + LLVOVolume *obj = impl->getSomeObject(); + if(obj) + { + // This media is attached to at least one object. Figure out which face it's on. + S32 face = obj->getFaceIndexWithMediaImpl(impl, -1); + + // We don't have a proper pick normal here, and finding a face's real normal is... complicated. + LLVector3 normal = obj->getApproximateFaceNormal(face); + if(normal.isNull()) + { + // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction. +// llinfos << "approximate face normal invalid, using camera direction." << llendl; + normal = LLViewerCamera::getInstance()->getAtAxis(); + normal *= (F32)-1.0f; + } + + // Attempt to focus/zoom on that face. + setFocusFace(obj, face, impl, normal); + + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(); + mMediaControls.get()->nextZoomLevel(); + } + } + } +} + +void LLViewerMediaFocus::unZoom() +{ + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(); + } +} + +bool LLViewerMediaFocus::isZoomed() const +{ + return (mMediaControls.get() && mMediaControls.get()->getZoomLevel() != LLPanelPrimMediaControls::ZOOM_NONE); +} + +LLUUID LLViewerMediaFocus::getControlsMediaID() +{ + if(getFocusedMediaImpl()) + { + return mFocusedImplID; + } + else if(getHoverMediaImpl()) + { + return mHoverImplID; + } + + return LLUUID::null; +} diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index ed9597e61..f03dd8751 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -2,31 +2,25 @@ * @file llpanelmsgs.h * @brief Message popup preferences panel * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,7 +35,7 @@ #include "llselectmgr.h" class LLViewerMediaImpl; -class LLPanelMediaHUD; +class LLPanelPrimMediaControls; class LLViewerMediaFocus : public LLFocusableElement, @@ -51,40 +45,66 @@ public: LLViewerMediaFocus(); ~LLViewerMediaFocus(); - static void cleanupClass(); - - void setFocusFace(BOOL b, LLPointer objectp, S32 face, viewer_media_t media_impl); - void clearFocus() { setFocusFace(false, NULL, 0, NULL); } + // Set/clear the face that has media focus (takes keyboard input and has the full set of controls) + void setFocusFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearFocus(); + + // Set/clear the face that has "media hover" (has the mimimal set of controls to zoom in or pop out into a media browser). + // If a media face has focus, the media hover will be ignored. + void setHoverFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearHover(); + /*virtual*/ bool getFocus(); - /*virtual*/ // void onNavigateComplete( const EventType& event_in ); - /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - LLUUID getSelectedUUID(); - LLObjectSelectionHandle getSelection() { return mFocus; } - void update(); + + static void setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only = false); + static F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - void setCameraZoom(F32 padding_factor); - void setMouseOverFlag(bool b, viewer_media_t media_impl = NULL); - bool getMouseOverFlag() { return mMouseOverFlag; } - void setPickInfo(LLPickInfo pick_info) { mPickInfo = pick_info; } - F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); + bool isFocusedOnFace(LLPointer objectp, S32 face); + bool isHoveringOverFace(LLPointer objectp, S32 face); + + // These look up (by uuid) and return the values that were set with setFocusFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getFocusedMediaImpl(); + LLViewerObject* getFocusedObject(); + S32 getFocusedFace() { return mFocusedObjectFace; } + LLUUID getFocusedObjectID() { return mFocusedObjectID; } + + // These look up (by uuid) and return the values that were set with setHoverFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getHoverMediaImpl(); + LLViewerObject* getHoverObject(); + S32 getHoverFace() { return mHoverObjectFace; } + + // Try to focus/zoom on the specified media (if it's on an object in world). + void focusZoomOnMedia(LLUUID media_id); + // Are we zoomed in? + bool isZoomed() const; + void unZoom(); + + // Return the ID of the media instance the controls are currently attached to (either focus or hover). + LLUUID getControlsMediaID(); protected: /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); private: - LLObjectSelectionHandle mFocus; - std::string mLastURL; - bool mMouseOverFlag; - LLPickInfo mPickInfo; - LLHandle mMediaHUD; - LLUUID mObjectID; - viewer_media_t mMediaImpl; + + LLHandle mMediaControls; + LLObjectSelectionHandle mSelection; + + LLUUID mFocusedObjectID; + S32 mFocusedObjectFace; + LLUUID mFocusedImplID; + LLVector3 mFocusedObjectNormal; + + LLUUID mHoverObjectID; + S32 mHoverObjectFace; + LLUUID mHoverImplID; + LLVector3 mHoverObjectNormal; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 52799fdf4..6b3c27489 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -86,7 +86,6 @@ #include "llfloatergroupinvite.h" #include "llfloatergroups.h" #include "llfloaterhtmlcurrency.h" -#include "llfloatermediabrowser.h" // gViewerHtmlHelp #include "llfloaterhud.h" #include "llfloaterinspect.h" #include "llfloaterinventory.h" @@ -115,6 +114,7 @@ #include "llfloatertest.h" #include "llfloatertools.h" #include "llfloaterwater.h" +#include "llfloaterwebcontent.h" #include "llfloaterwindlight.h" #include "llfloaterworldmap.h" #include "llfloatermemleak.h" @@ -6463,11 +6463,12 @@ class LLShowFloater : public view_listener_t LLViewerParcelMgr::getInstance()->startBuyLand(); } - else if (floater_name == "help f1") + //Singu TODO: Re-implement f1 help. + /*else if (floater_name == "help f1") { llinfos << "Spawning HTML help window" << llendl; gViewerHtmlHelp.show(); - } + }*/ else if (floater_name == "complaint reporter") { @@ -8725,7 +8726,7 @@ static void handle_load_from_xml_continued(AIFilePicker* filepicker) void handle_web_browser_test(void*) { - LLFloaterMediaBrowser::showInstance("http://secondlife.com/app/search/slurls.html"); + LLWeb::loadURL("http://secondlife.com/app/search/slurls.html"); } void handle_buy_currency_test(void*) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 3d22f49a5..673462beb 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -84,6 +84,7 @@ #include "llselectmgr.h" #include "llstartup.h" #include "llsky.h" +#include "llslurl.h" #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" @@ -1839,6 +1840,63 @@ bool goto_url_callback(const LLSD& notification, const LLSD& response) return false; } static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); +static bool parse_lure_bucket(const std::string& bucket, + U64& region_handle, + LLVector3& pos, + LLVector3& look_at, + U8& region_access) +{ + // tokenize the bucket + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(bucket, sep); + tokenizer::iterator iter = tokens.begin(); + + S32 gx,gy,rx,ry,rz,lx,ly,lz; + try + { + gx = boost::lexical_cast((*(iter)).c_str()); + gy = boost::lexical_cast((*(++iter)).c_str()); + rx = boost::lexical_cast((*(++iter)).c_str()); + ry = boost::lexical_cast((*(++iter)).c_str()); + rz = boost::lexical_cast((*(++iter)).c_str()); + lx = boost::lexical_cast((*(++iter)).c_str()); + ly = boost::lexical_cast((*(++iter)).c_str()); + lz = boost::lexical_cast((*(++iter)).c_str()); + } + catch( boost::bad_lexical_cast& ) + { + LL_WARNS("parse_lure_bucket") + << "Couldn't parse lure bucket." + << LL_ENDL; + return false; + } + // Grab region access + region_access = SIM_ACCESS_MIN; + if (++iter != tokens.end()) + { + std::string access_str((*iter).c_str()); + LLStringUtil::trim(access_str); + if ( access_str == "A" ) + { + region_access = SIM_ACCESS_ADULT; + } + else if ( access_str == "M" ) + { + region_access = SIM_ACCESS_MATURE; + } + else if ( access_str == "PG" ) + { + region_access = SIM_ACCESS_PG; + } + } + + pos.setVec((F32)rx, (F32)ry, (F32)rz); + look_at.setVec((F32)lx, (F32)ly, (F32)lz); + + region_handle = to_region_handle(gx, gy); + return true; +} // Strip out "Resident" for display, but only if the message came from a user // (rather than a script) @@ -1957,8 +2015,11 @@ std::string replace_wildcards(std::string autoresponse, const LLUUID& id, const // Add in their legacy name boost::algorithm::replace_all(autoresponse, "#n", name); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + // Add in our location's slurl - boost::algorithm::replace_all(autoresponse, "#r", gAgent.getSLURL()); + boost::algorithm::replace_all(autoresponse, "#r", slurl.getSLURLString()); // Add in their display name LLAvatarName av_name; @@ -2936,15 +2997,69 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } } // [/RLVa:KB] + LLVector3 pos, look_at; + U64 region_handle(0); + U8 region_access(SIM_ACCESS_MIN); + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + std::string region_access_lc = LLStringUtil::null; + bool canUserAccessDstRegion = true; + bool doesUserRequireMaturityIncrease = false; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + region_access_lc = region_access_str; + LLStringUtil::toLower(region_access_lc); + + if (!gAgent.isGodlike()) + { + switch (region_access) + { + case SIM_ACCESS_MIN : + case SIM_ACCESS_PG : + break; + case SIM_ACCESS_MATURE : + if (gAgent.isTeen()) + { + canUserAccessDstRegion = false; + } + else if (gAgent.prefersPG()) + { + doesUserRequireMaturityIncrease = true; + } + break; + case SIM_ACCESS_ADULT : + if (!gAgent.isAdult()) + { + canUserAccessDstRegion = false; + } + else if (!gAgent.prefersAdult()) + { + doesUserRequireMaturityIncrease = true; + } + break; + default : + llassert(0); + break; + } + } + } LLSD args; // *TODO: Translate -> [FIRST] [LAST] (maybe) args["NAME"] = name; args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + args["REGION_CONTENT_MATURITY"] = region_access_lc; LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = FALSE; + payload["region_maturity"] = region_access; //LLNotificationsUtil::add("TeleportOffered", args, payload); // [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Modified: RLVa-1.2.2c @@ -2959,62 +3074,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { LLNotificationsUtil::add("TeleportOffered", args, payload); // - if(binary_bucket_size) - { - char* dest = new char[binary_bucket_size]; - strncpy(dest, (char*)binary_bucket, binary_bucket_size-1); /* Flawfinder: ignore */ - dest[binary_bucket_size-1] = '\0'; - - llinfos << "IM_LURE_USER binary_bucket " << dest << llendl; - - std::string str(dest); - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|","",boost::keep_empty_tokens); - tokenizer tokens(str, sep); - tokenizer::iterator iter = tokens.begin(); - std::string global_x_str(*iter++); - std::string global_y_str(*iter++); - std::string x_str(*iter++); - std::string y_str(*iter++); - std::string z_str(*iter++); - // skip what I think must be LookAt - if(iter != tokens.end()) - iter++; // x - if(iter != tokens.end()) - iter++; // y - if(iter != tokens.end()) - iter++; // z - std::string mat_str(""); - if(iter != tokens.end()) - mat_str.assign(*iter++); - mat_str = utf8str_trim(mat_str); - - llinfos << "IM_LURE_USER tokenized " << global_x_str << "|" << global_y_str << "|" << x_str << "|" << y_str << "|" << z_str << "|" << mat_str << llendl; - - std::istringstream gxstr(global_x_str); - int global_x; - gxstr >> global_x; - - std::istringstream gystr(global_y_str); - int global_y; - gystr >> global_y; - - std::istringstream xstr(x_str); - int x; - xstr >> x; - - std::istringstream ystr(y_str); - int y; - ystr >> y; - - std::istringstream zstr(z_str); - int z; - zstr >> z; - - llinfos << "IM_LURE_USER parsed " << global_x << "|" << global_y << "|" << x << "|" << y << "|" << z << "|" << mat_str << llendl; - - gAgent.showLureDestination(name, global_x, global_y, x, y, z, mat_str); - } + gAgent.showLureDestination(name, region_handle, pos.mV[VX], pos.mV[VY], pos.mV[VZ]); // } // [/RLVa:KB] @@ -3025,10 +3085,71 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_GODLIKE_LURE_USER: { + LLVector3 pos, look_at; + U64 region_handle(0); + U8 region_access(SIM_ACCESS_MIN); + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + std::string region_access_lc = LLStringUtil::null; + + bool canUserAccessDstRegion = true; + bool doesUserRequireMaturityIncrease = false; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + region_access_lc = region_access_str; + LLStringUtil::toLower(region_access_lc); + + if (!gAgent.isGodlike()) + { + switch (region_access) + { + case SIM_ACCESS_MIN : + case SIM_ACCESS_PG : + break; + case SIM_ACCESS_MATURE : + if (gAgent.isTeen()) + { + canUserAccessDstRegion = false; + } + else if (gAgent.prefersPG()) + { + doesUserRequireMaturityIncrease = true; + } + break; + case SIM_ACCESS_ADULT : + if (!gAgent.isAdult()) + { + canUserAccessDstRegion = false; + } + else if (!gAgent.prefersAdult()) + { + doesUserRequireMaturityIncrease = true; + } + break; + default : + llassert(0); + break; + } + } + } + + LLSD args; + // *TODO: Translate -> [FIRST] [LAST] (maybe) + args["NAME"] = name; + args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + args["REGION_CONTENT_MATURITY"] = region_access_lc; LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = TRUE; + payload["region_maturity"] = region_access; + // do not show a message box, because you're about to be // teleported. LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); @@ -4296,7 +4417,9 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { // Chat the "back" SLURL. (DEV-4907) - LLChat chat(LLTrans::getString("completed_from") + " " + gAgent.getTeleportSourceSLURL()); + LLSLURL slurl; + gAgent.getTeleportSourceSLURL(slurl); + LLChat chat(LLTrans::getString("completed_from") + " " + slurl.getSLURLString()); chat.mSourceType = CHAT_SOURCE_SYSTEM; LLFloaterChat::addChatHistory(chat); @@ -7160,8 +7283,23 @@ void send_group_notice(const LLUUID& group_id, bool handle_lure_callback(const LLSD& notification, const LLSD& response) { + static const unsigned OFFER_RECIPIENT_LIMIT = 250; + if(notification["payload"]["ids"].size() > OFFER_RECIPIENT_LIMIT) + { + // More than OFFER_RECIPIENT_LIMIT targets will overload the message + // producing an llerror. + LLSD args; + args["OFFERS"] = notification["payload"]["ids"].size(); + args["LIMIT"] = static_cast(OFFER_RECIPIENT_LIMIT); + LLNotificationsUtil::add("TooManyTeleportOffers", args); + return false; + } + std::string text = response["message"].asString(); - S32 option = LLNotification::getSelectedOption(notification, response); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index c721e936a..362048462 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -97,6 +97,7 @@ #include "llviewernetwork.h" #include "llvowlsky.h" #include "llmanip.h" +#include "llmediaentry.h" // [RLVa:KB] #include "rlvhandler.h" diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index b5522be92..12aea19c1 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -156,8 +156,9 @@ public: // Return codes for processUpdateMessage enum { MEDIA_URL_REMOVED = 0x1, - MEDIA_URL_ADDED = 0x2, - MEDIA_URL_UPDATED = 0x4, + MEDIA_URL_ADDED = 0x2, + MEDIA_URL_UPDATED = 0x4, + MEDIA_FLAGS_CHANGED = 0x8, INVALID_UPDATE = 0x80000000 }; diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index ee56d7b5d..814ce92aa 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -215,41 +215,57 @@ void LLViewerParcelMedia::play(LLParcel* parcel, bool filter) S32 media_width = parcel->getMediaWidth(); S32 media_height = parcel->getMediaHeight(); - // Debug print - // LL_DEBUGS("Media") << "Play media type : " << mime_type << ", url : " << media_url << LL_ENDL; - - if (!sMediaImpl || (sMediaImpl && - (sMediaImpl->getMediaURL() != media_url || - sMediaImpl->getMimeType() != mime_type || - sMediaImpl->getMediaTextureID() != placeholder_texture_id))) + if(sMediaImpl) { - if (sMediaImpl) + // If the url and mime type are the same, call play again + if(sMediaImpl->getMediaURL() == media_url + && sMediaImpl->getMimeType() == mime_type + && sMediaImpl->getMediaTextureID() == placeholder_texture_id) { - // Delete the old media impl first so they don't fight over the texture. - sMediaImpl->stop(); + LL_DEBUGS("Media") << "playing with existing url " << media_url << LL_ENDL; + + sMediaImpl->play(); } + // Else if the texture id's are the same, navigate and rediscover type + // MBW -- This causes other state from the previous parcel (texture size, autoscale, and looping) to get re-used incorrectly. + // It's also not really necessary -- just creating a new instance is fine. +// else if(sMediaImpl->getMediaTextureID() == placeholder_texture_id) +// { +// sMediaImpl->navigateTo(media_url, mime_type, true); +// } + else + { + // Since the texture id is different, we need to generate a new impl - LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; - - // There is no media impl, or it has just been deprecated, make a new one - sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, - media_width, media_height, media_auto_scale, - media_loop, mime_type); + // Delete the old one first so they don't fight over the texture. + sMediaImpl = NULL; + + // A new impl will be created below. } - - // The url, mime type and texture are now the same, call play again - if (sMediaImpl->getMediaURL() == media_url - && sMediaImpl->getMimeType() == mime_type - && sMediaImpl->getMediaTextureID() == placeholder_texture_id) - { - LL_DEBUGS("Media") << "playing with existing url " << media_url << LL_ENDL; - - sMediaImpl->play(); } - LLFirstUse::useMedia(); + // Don't ever try to play if the media type is set to "none/none" + if(stricmp(mime_type.c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) + { + if(!sMediaImpl) + { + LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; - LLViewerParcelMediaAutoPlay::playStarted(); + // There is no media impl, make a new one + sMediaImpl = LLViewerMedia::newMediaImpl( + placeholder_texture_id, + media_width, + media_height, + media_auto_scale, + media_loop); + sMediaImpl->setIsParcelMedia(true); + sMediaImpl->navigateTo(media_url, mime_type, true); + } + + LLFirstUse::useMedia(); + + LLViewerParcelMediaAutoPlay::playStarted(); + } } // static @@ -327,6 +343,33 @@ std::string LLViewerParcelMedia::getMimeType() return sMediaImpl.notNull() ? sMediaImpl->getMimeType() : LLMIMETypes::getDefaultMimeType(); } +//static +std::string LLViewerParcelMedia::getURL() +{ + std::string url; + if(sMediaImpl.notNull()) + url = sMediaImpl->getMediaURL(); + + if(stricmp(LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaType().c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) + { + if (url.empty()) + url = LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaCurrentURL(); + + if (url.empty()) + url = LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaURL(); + } + + return url; +} + +//static +std::string LLViewerParcelMedia::getName() +{ + if(sMediaImpl.notNull()) + return sMediaImpl->getName(); + return ""; +} + viewer_media_t LLViewerParcelMedia::getParcelMedia() { return sMediaImpl; diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h index 50e7aaa2c..f6c85bf3b 100644 --- a/indra/newview/llviewerparcelmedia.h +++ b/indra/newview/llviewerparcelmedia.h @@ -86,6 +86,8 @@ class LLViewerParcelMedia : public LLViewerMediaObserver static LLViewerMediaImpl::EMediaStatus getStatus(); static std::string getMimeType(); + static std::string getURL(); + static std::string getName(); static viewer_media_t getParcelMedia(); static void processParcelMediaCommandMessage( LLMessageSystem *msg, void ** ); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index cfdf5ef43..73ee75104 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -181,7 +181,7 @@ class LLRegionHandler : public LLCommandHandler { public: // requests will be throttled from a non-trusted browser - LLRegionHandler() : LLCommandHandler("region", /*V3: UNTRUSTED_THROTTLE*/ true) {} + LLRegionHandler() : LLCommandHandler("region", UNTRUSTED_THROTTLE) {} bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { @@ -204,7 +204,7 @@ public: } // Process the SLapp as if it was a secondlife://{PLACE} SLurl - LLURLDispatcher::dispatch(url, /*V3: "clicked",*/ web, true); + LLURLDispatcher::dispatch(url, "clicked", web, true); return true; } }; @@ -1658,8 +1658,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("MeshUploadFlag"); capabilityNames.append("NavMeshGenerationStatus"); capabilityNames.append("NewFileAgentInventory"); - /*capabilityNames.append("ObjectMedia"); - capabilityNames.append("ObjectMediaNavigate");*/ + capabilityNames.append("ObjectMedia"); + capabilityNames.append("ObjectMediaNavigate"); capabilityNames.append("ObjectNavMeshProperties"); capabilityNames.append("ParcelNavigateMedia"); //Singu Note: Removed by Baker, do we need this? capabilityNames.append("ParcelPropertiesUpdate"); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index a1de57f87..19681d38e 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -59,9 +59,7 @@ //#include "lltextureatlasmanager.h" #include "lltextureentry.h" #include "lltexturemanagerbridge.h" -#if NEW_MEDIA_TEXTURE #include "llmediaentry.h" -#endif //NEW_MEDIA_TEXTURE #include "llvovolume.h" #include "llviewermedia.h" /////////////////////////////////////////////////////////////////////////////// @@ -73,9 +71,7 @@ LLPointer LLViewerFetchedTexture::sMissingAssetImagep = LLPointer LLViewerFetchedTexture::sWhiteImagep = NULL; LLPointer LLViewerFetchedTexture::sDefaultImagep = NULL; LLPointer LLViewerFetchedTexture::sSmokeImagep = NULL; -#if NEW_MEDIA_TEXTURE LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap ; -#endif //NEW_MEDIA_TEXTURE #if 0 LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL ; #endif @@ -167,26 +163,22 @@ void LLLoadedCallbackEntry::cleanUpCallbackList(LLLoadedCallbackEntry::source_ca } } -#if NEW_MEDIA_TEXTURE LLViewerMediaTexture* LLViewerTextureManager::createMediaTexture(const LLUUID &media_id, BOOL usemipmaps, LLImageGL* gl_image) { return new LLViewerMediaTexture(media_id, usemipmaps, gl_image) ; } -#endif //NEW_MEDIA_TEXTURE LLViewerTexture* LLViewerTextureManager::findTexture(const LLUUID& id) { LLViewerTexture* tex ; //search fetched texture list tex = gTextureList.findImage(id) ; - -#if NEW_MEDIA_TEXTURE + //search media texture list if(!tex) { tex = LLViewerTextureManager::findMediaTexture(id) ; } -#endif //NEW_MEDIA_TEXTURE return tex ; } @@ -194,8 +186,7 @@ LLViewerFetchedTexture* LLViewerTextureManager::findFetchedTexture(const LLUUID { return gTextureList.findImage(id); } - -#if NEW_MEDIA_TEXTURE + LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id) { return LLViewerMediaTexture::findMediaTexture(media_id) ; @@ -213,7 +204,6 @@ LLViewerMediaTexture* LLViewerTextureManager::getMediaTexture(const LLUUID& id, return tex ; } -#endif //NEW_MEDIA_TEXTURE LLViewerFetchedTexture* LLViewerTextureManager::staticCastToFetchedTexture(LLTexture* tex, BOOL report_error) { @@ -419,9 +409,7 @@ void LLViewerTextureManager::cleanup() LLViewerFetchedTexture::sMissingAssetImagep = NULL; LLViewerFetchedTexture::sWhiteImagep = NULL; -#if NEW_MEDIA_TEXTURE - LLViewerMediaTexture::cleanUpClass() ; -#endif //NEW_MEDIA_TEXTURE + LLViewerMediaTexture::cleanUpClass() ; } //---------------------------------------------------------------------------------------------- @@ -500,9 +488,7 @@ bool LLViewerTexture::isMemoryForTextureLow() return low_mem ; } -#if NEW_MEDIA_TEXTURE static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_MEDIA("Media"); -#endif //NEW_MEDIA_TEXTURE #if 0 static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_TEST("Test"); #endif @@ -519,12 +505,10 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity tester->update() ; } #endif -#if NEW_MEDIA_TEXTURE { LLFastTimer t(FTM_TEXTURE_UPDATE_MEDIA); LLViewerMediaTexture::updateClass() ; } -#endif //NEW_MEDIA_TEXTURE sBoundTextureMemoryInBytes = LLImageGL::sBoundTextureMemoryInBytes;//in bytes sTotalTextureMemoryInBytes = LLImageGL::sGlobalTextureMemoryInBytes;//in bytes @@ -634,9 +618,6 @@ void LLViewerTexture::init(bool firstinit) mNumVolumes = 0; mFaceList.clear() ; mVolumeList.clear(); -#if !NEW_MEDIA_TEXTURE - mIsMediaTexture = false; -#endif //!NEW_MEDIA_TEXTURE } //virtual @@ -3027,7 +3008,6 @@ bool LLViewerLODTexture::scaleDown() //---------------------------------------------------------------------------------------------- //start of LLViewerMediaTexture //---------------------------------------------------------------------------------------------- -#if NEW_MEDIA_TEXTURE //static void LLViewerMediaTexture::updateClass() { @@ -3112,7 +3092,7 @@ LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LL setMediaImpl() ; - setCategory(LLViewerTexture::MEDIA) ; + setCategory(LLGLTexture::MEDIA) ; LLViewerTexture* tex = gTextureList.findImage(mID) ; if(tex) //this media is a parcel media for tex. @@ -3130,7 +3110,7 @@ LLViewerMediaTexture::~LLViewerMediaTexture() tex->setParcelMedia(NULL) ; } } -#endif //NEW_MEDIA_TEXTURE + void LLViewerMediaTexture::reinit(BOOL usemipmaps /* = TRUE */) { llassert(mGLTexturep.notNull()) ; @@ -3150,7 +3130,7 @@ void LLViewerMediaTexture::setUseMipMaps(BOOL mipmap) mGLTexturep->setUseMipMaps(mipmap) ; } } -#if NEW_MEDIA_TEXTURE + //virtual S8 LLViewerMediaTexture::getType() const { @@ -3524,7 +3504,6 @@ F32 LLViewerMediaTexture::getMaxVirtualSize() return mMaxVirtualSize ; } -#endif //NEW_MEDIA_TEXTURE //---------------------------------------------------------------------------------------------- //end of LLViewerMediaTexture //---------------------------------------------------------------------------------------------- diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index cf8c6d003..b378f578d 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -518,7 +518,6 @@ private: // class LLViewerMediaTexture : public LLViewerTexture { -#if NEW_MEDIA_TEXTURE protected: /*virtual*/ ~LLViewerMediaTexture() ; @@ -526,16 +525,11 @@ public: LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; /*virtual*/ S8 getType() const; -#endif //NEW_MEDIA_TEXTURE -#if !NEW_MEDIA_TEXTURE -public: -#endif //!NEW_MEDIA_TEXTURE void reinit(BOOL usemipmaps = TRUE); BOOL getUseMipMaps() {return mUseMipMaps ; } void setUseMipMaps(BOOL mipmap) ; -#if NEW_MEDIA_TEXTURE void setPlaying(BOOL playing) ; BOOL isPlaying() const {return mIsPlaying;} void setMediaImpl() ; @@ -580,7 +574,6 @@ public: private: typedef std::map< LLUUID, LLPointer > media_map_t ; static media_map_t sMediaMap ; -#endif //NEW_MEDIA_TEXTURE }; //just an interface class, do not create instance from this class. @@ -604,17 +597,15 @@ public: // static LLViewerTexture* findTexture(const LLUUID& id) ; static LLViewerFetchedTexture* findFetchedTexture(const LLUUID& id) ; -#if NEW_MEDIA_TEXTURE static LLViewerMediaTexture* findMediaTexture(const LLUUID& id) ; - + static LLViewerMediaTexture* createMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; // //"get-texture" will create a new texture if the texture does not exist. // static LLViewerMediaTexture* getMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; -#endif //NEW_MEDIA_TEXTURE - + static LLPointer getLocalTexture(BOOL usemipmaps = TRUE, BOOL generate_gl_tex = TRUE); static LLPointer getLocalTexture(const LLUUID& id, BOOL usemipmaps, BOOL generate_gl_tex = TRUE) ; static LLPointer getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps) ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 80efedbed..b875555e7 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -744,12 +744,7 @@ void LLViewerTextureList::updateImages(F32 max_time) } } } - //Required for old media system - if (!gNoRender && !gGLManager.mIsDisabled) - { - LLFastTimer t(FTM_IMAGE_MEDIA); - LLViewerMedia::updateMedia(); - } + { LLFastTimer t(FTM_IMAGE_STATS); updateImagesUpdateStats(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 1c7007eea..fceaa770b 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -74,6 +74,7 @@ #include "lltimer.h" #include "timing.h" #include "llviewermenu.h" +#include "llmediaentry.h" #include "raytrace.h" // newview includes @@ -756,6 +757,12 @@ public: static const LLCachedControl beacons_visible("BeaconsVisible",false); if (LLPipeline::getRenderBeacons(NULL) && beacons_visible) { + if (LLPipeline::getRenderMOAPBeacons(NULL)) + { + addText(xpos, ypos, "Viewing media beacons (white)"); + ypos += y_inc; + } + if (LLPipeline::toggleRenderTypeControlNegated((void*)LLPipeline::RENDER_TYPE_PARTICLES)) { addText(xpos, ypos, particle_hiding); @@ -1092,6 +1099,149 @@ BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MAS return TRUE; } +LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data) +{ + LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE; + + const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop"); + const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop"); + + if ( prim_media_dnd_enabled || slurl_dnd_enabled ) + { + switch(action) + { + // Much of the handling for these two cases is the same. + case LLWindowCallbacks::DNDA_TRACK: + case LLWindowCallbacks::DNDA_DROPPED: + case LLWindowCallbacks::DNDA_START_TRACKING: + { + bool drop = (LLWindowCallbacks::DNDA_DROPPED == action); + + if (slurl_dnd_enabled) + { + LLSLURL dropped_slurl(data); + if(dropped_slurl.isSpatial()) + { + if (drop) + { + LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), "clicked", NULL, true ); + return LLWindowCallbacks::DND_MOVE; + } + return LLWindowCallbacks::DND_COPY; + } + } + + if (prim_media_dnd_enabled) + { + LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ ); + + LLUUID object_id = pick_info.getObjectID(); + S32 object_face = pick_info.mObjectFace; + std::string url = data; + + lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl; + + LLVOVolume *obj = dynamic_cast(static_cast(pick_info.getObject())); + + if (obj && !obj->getRegion()->getCapability("ObjectMedia").empty()) + { + LLTextureEntry *te = obj->getTE(object_face); + + // can modify URL if we can modify the object or we have navigate permissions + bool allow_modify_url = obj->permModify() || obj->hasMediaPermission( te->getMediaData(), LLVOVolume::MEDIA_PERM_INTERACT ); + + if (te && allow_modify_url ) + { + if (drop) + { + // object does NOT have media already + if ( ! te->hasMedia() ) + { + // we are allowed to modify the object + if ( obj->permModify() ) + { + // Create new media entry + LLSD media_data; + // XXX Should we really do Home URL too? + media_data[LLMediaEntry::HOME_URL_KEY] = url; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + media_data[LLMediaEntry::AUTO_PLAY_KEY] = true; + obj->syncMediaData(object_face, media_data, true, true); + // XXX This shouldn't be necessary, should it ?!? + if (obj->getMediaImpl(object_face)) + obj->getMediaImpl(object_face)->navigateReload(); + obj->sendMediaDataUpdate(); + + result = LLWindowCallbacks::DND_COPY; + } + } + else + // object HAS media already + { + // URL passes the whitelist + if (te->getMediaData()->checkCandidateUrl( url ) ) + { + // just navigate to the URL + if (obj->getMediaImpl(object_face)) + { + obj->getMediaImpl(object_face)->navigateTo(url); + } + else + { + // This is very strange. Navigation should + // happen via the Impl, but we don't have one. + // This sends it to the server, which /should/ + // trigger us getting it. Hopefully. + LLSD media_data; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + obj->syncMediaData(object_face, media_data, true, true); + obj->sendMediaDataUpdate(); + } + result = LLWindowCallbacks::DND_LINK; + + } + } + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + + } + else + { + // Check the whitelist, if there's media (otherwise just show it) + if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) + { + if ( obj != mDragHoveredObject) + { + // Highlight the dragged object + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = obj; + LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject); + } + result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK; + + } + } + } + } + } + } + break; + + case LLWindowCallbacks::DNDA_STOP_TRACKING: + // The cleanup case below will make sure things are unhilighted if necessary. + break; + } + + if (prim_media_dnd_enabled && + result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull()) + { + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + } + } + + return result; +} BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; @@ -1420,7 +1570,8 @@ void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) std::string url = (const char*)data; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + // don't treat slapps coming from external browsers as "clicks" as this would bypass throttling + if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) { // bring window to foreground, as it has just been "launched" from a URL mWindow->bringToFront(); @@ -1510,7 +1661,6 @@ LLViewerWindow::LLViewerWindow( mMouseInWindow( FALSE ), mLastMask( MASK_NONE ), mToolStored( NULL ), - mSuppressToolbox( FALSE ), mHideCursorPermanent( FALSE ), mCursorHidden(FALSE), mIgnoreActivate( FALSE ), @@ -1570,9 +1720,11 @@ LLViewerWindow::LLViewerWindow( { LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL; } - + + const bool do_not_enforce = false; + mWindow->setMinSize(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, do_not_enforce); // root view not set LLCoordScreen scr; - mWindow->getSize(&scr); + mWindow->getSize(&scr); if(fullscreen && ( scr.mX!=width || scr.mY!=height)) { @@ -2590,7 +2742,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) } // Explicit hack for debug menu. - if ((mask == (MASK_SHIFT | MASK_CONTROL)) && + //Singu note: We do not use the ForceShowGrid setting. Grid selection should always be visible. + /*if ((mask == (MASK_SHIFT | MASK_CONTROL)) && ('G' == key || 'g' == key)) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) //on splash page @@ -2599,9 +2752,9 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) gSavedSettings.setBOOL("ForceShowGrid", visible); // Initialize visibility (and don't force visibility - use prefs) - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } - } + }*/ // Debugging view for unified notifications: CTRL-SHIFT-5 // *FIXME: Having this special-cased right here (just so this can be invoked from the login screen) sucks. @@ -2908,213 +3061,237 @@ void LLViewerWindow::updateUI() } updateMouseDelta(); - - + if (gNoRender) { return; } - - // clean up current focus - LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (cur_focus) - { - if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) - { - gFocusMgr.releaseFocusIfNeeded(cur_focus); - - LLUICtrl* parent = cur_focus->getParentUICtrl(); - const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); - while(parent) - { - if (parent->isCtrl() && - (parent->hasTabStop() || parent == focus_root) && - !parent->getIsChrome() && - parent->isInVisibleChain() && - parent->isInEnabledChain()) - { - if (!parent->focusFirstItem()) - { - parent->setFocus(TRUE); - } - break; - } - parent = parent->getParentUICtrl(); - } - } - else if (cur_focus->isFocusRoot()) - { - // focus roots keep trying to delegate focus to their first valid descendant - // this assumes that focus roots are not valid focus holders on their own - cur_focus->focusFirstItem(); - } - } + + updateKeyboardFocus(); BOOL handled = FALSE; - BOOL handled_by_top_ctrl = FALSE; LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); - if( mouse_captor ) - { - // Pass hover events to object capturing mouse events. - S32 local_x; - S32 local_y; - mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); - handled = mouse_captor->handleHover(local_x, local_y, mask); - if (LLView::sDebugMouseHandling) - { - llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; - } + { - if( !handled ) + if( mouse_captor ) { - lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; - } - } - else - { - if (top_ctrl) - { - S32 local_x, local_y; - top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); - handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); - handled_by_top_ctrl = TRUE; - } - - if ( !handled ) - { - // x and y are from last time mouse was in window - // mMouseInWindow tracks *actual* mouse location - if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + // Pass hover events to object capturing mouse events. + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + handled = mouse_captor->handleHover(local_x, local_y, mask); + if (LLView::sDebugMouseHandling) { - if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) - { - last_handle_msg = LLView::sMouseHandlerMessage; - llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; - } - handled = TRUE; + llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; } - else if (LLView::sDebugMouseHandling) + + if( !handled ) { - if (last_handle_msg != LLStringUtil::null) - { - last_handle_msg.clear(); - llinfos << "Hover not handled by view" << llendl; - } - } - } - - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl; - } - } - - // *NOTE: sometimes tools handle the mouse as a captor, so this - // logic is a little confusing - LLTool *tool = NULL; - if (gHoverView) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - - if(!handled && tool) - { - handled = tool->handleHover(x, y, mask); - - if (!mWindow->isCursorHidden()) - { - gHoverView->updateHover(tool); + lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; } } else { - // Cancel hovering if any UI element handled the event. - gHoverView->cancelHover(); + if (top_ctrl) + { + S32 local_x, local_y; + top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); + } + + if ( !handled ) + { + // x and y are from last time mouse was in window + // mMouseInWindow tracks *actual* mouse location + if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + { + if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) + { + last_handle_msg = LLView::sMouseHandlerMessage; + llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; + } + handled = TRUE; + } + else if (LLView::sDebugMouseHandling) + { + if (last_handle_msg != LLStringUtil::null) + { + last_handle_msg.clear(); + llinfos << "Hover not handled by view" << llendl; + } + } + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl; + } } + // *NOTE: sometimes tools handle the mouse as a captor, so this + // logic is a little confusing + LLTool *tool = NULL; + if (gHoverView) + { + tool = LLToolMgr::getInstance()->getCurrentTool(); + + if(!handled && tool) + { + handled = tool->handleHover(x, y, mask); + + if (!mWindow->isCursorHidden()) + { + gHoverView->updateHover(tool); + } + } + else + { + // Cancel hovering if any UI element handled the event. + gHoverView->cancelHover(); + } + + } + + // Show a new tool tip (or update one that is already shown) + BOOL tool_tip_handled = FALSE; + std::string tool_tip_msg; + static const LLCachedControl tool_tip_delay("ToolTipDelay",.7f); + F32 tooltip_delay = tool_tip_delay; + //HACK: hack for tool-based tooltips which need to pop up more quickly + //Also for show xui names as tooltips debug mode + if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames) + { + static const LLCachedControl drag_and_drop_tool_tip_delay("DragAndDropToolTipDelay",.1f); + tooltip_delay = drag_and_drop_tool_tip_delay; + } + if( handled && + gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay && + !mWindow->isCursorHidden() ) + { + LLRect screen_sticky_rect; + LLMouseHandler *mh; + S32 local_x, local_y; + if (mouse_captor) + { + mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); + mh = mouse_captor; + } + else if (top_ctrl) + { + top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); + mh = top_ctrl; + } + else + { + local_x = x; local_y = y; + mh = mRootView; + } + + BOOL tooltip_vis = FALSE; + if (shouldShowToolTipFor(mh)) + { + tool_tip_handled = mh->handleToolTip(local_x, local_y, tool_tip_msg, &screen_sticky_rect ); + + if( tool_tip_handled && !tool_tip_msg.empty() ) + { + mToolTipStickyRect = screen_sticky_rect; + mToolTip->setWrappedText( tool_tip_msg, 200 ); + mToolTip->reshapeToFitText(); + mToolTip->setOrigin( x, y ); + LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0); + mToolTip->translateIntoRect( virtual_window_rect, FALSE ); + tooltip_vis = TRUE; + } + } + + if (mToolTip) + { + mToolTip->setVisible( tooltip_vis ); + } + } + } + + updateLayout(); + + mLastMousePoint = mCurrentMousePoint; + + // cleanup unused selections when no modal dialogs are open + if (LLModalDialog::activeCount() == 0) + { + LLViewerParcelMgr::getInstance()->deselectUnused(); + } + + if (LLModalDialog::activeCount() == 0) + { + LLSelectMgr::getInstance()->deselectUnused(); + } + + // per frame picking - for tooltips and changing cursor over interactive objects + static S32 previous_x = -1; + static S32 previous_y = -1; + static BOOL mouse_moved_since_pick = FALSE; + + if ((previous_x != x) || (previous_y != y)) + mouse_moved_since_pick = TRUE; + + static const LLCachedControl picks_moving("PicksPerSecondMouseMoving",5.f); + static const LLCachedControl picks_stationary("PicksPerSecondMouseStationary",0.f); + if( !getCursorHidden() + // When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly. + && (LLViewerMediaFocus::getInstance()->getFocus() + || ((mouse_moved_since_pick) && (picks_moving > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_moving)) + || ((!mouse_moved_since_pick) && (picks_stationary > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_stationary)))) + { + mouse_moved_since_pick = FALSE; + mPickTimer.reset(); + pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE, TRUE); + } + + previous_x = x; + previous_y = y; + + return; +} + +/* static */ +void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info) +{ + gViewerWindow->mHoverPick = pick_info; +} + +void LLViewerWindow::updateLayout() +{ + static const LLCachedControl freeze_time("FreezeTime",0); + LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); + if (gHoverView != NULL && + gFloaterTools != NULL + && tool != NULL + && tool != gToolNull + && tool != LLToolCompInspect::getInstance() + && tool != LLToolDragAndDrop::getInstance() + && !freeze_time) + { // Suppress the toolbox view if our source tool was the pie tool, // and we've overridden to something else. - mSuppressToolbox = + bool suppress_toolbox = (LLToolMgr::getInstance()->getBaseTool() == LLToolPie::getInstance()) && (LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()); - } - - // Show a new tool tip (or update one that is already shown) - BOOL tool_tip_handled = FALSE; - std::string tool_tip_msg; - static const LLCachedControl tool_tip_delay("ToolTipDelay",.7f); - F32 tooltip_delay = tool_tip_delay; - //HACK: hack for tool-based tooltips which need to pop up more quickly - //Also for show xui names as tooltips debug mode - if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames) - { - static const LLCachedControl drag_and_drop_tool_tip_delay("DragAndDropToolTipDelay",.1f); - tooltip_delay = drag_and_drop_tool_tip_delay; - } - if( handled && - gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay && - !mWindow->isCursorHidden() ) - { - LLRect screen_sticky_rect; - LLMouseHandler *mh; - S32 local_x, local_y; - if (mouse_captor) - { - mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); - mh = mouse_captor; - } - else if (handled_by_top_ctrl) - { - top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); - mh = top_ctrl; - } - else - { - local_x = x; local_y = y; - mh = mRootView; - } - - BOOL tooltip_vis = FALSE; - if (shouldShowToolTipFor(mh)) - { - tool_tip_handled = mh->handleToolTip(local_x, local_y, tool_tip_msg, &screen_sticky_rect ); - - if( tool_tip_handled && !tool_tip_msg.empty() ) - { - mToolTipStickyRect = screen_sticky_rect; - mToolTip->setWrappedText( tool_tip_msg, 200 ); - mToolTip->reshapeToFitText(); - mToolTip->setOrigin( x, y ); - LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0); - mToolTip->translateIntoRect( virtual_window_rect, FALSE ); - tooltip_vis = TRUE; - } - } - - if (mToolTip) - { - mToolTip->setVisible( tooltip_vis ); - } - } - - static const LLCachedControl freeze_time("FreezeTime",0); - if (tool && tool != gToolNull && tool != LLToolCompInspect::getInstance() && tool != LLToolDragAndDrop::getInstance() && !freeze_time) - { LLMouseHandler *captor = gFocusMgr.getMouseCapture(); // With the null, inspect, or drag and drop tool, don't muck // with visibility. if (gFloaterTools->isMinimized() - || (tool != LLToolPie::getInstance() // not default tool - && tool != LLToolCompGun::getInstance() // not coming out of mouselook - && !mSuppressToolbox // not override in third person - && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode - && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset - && (!captor || captor->isView())) // not dragging - ) + || (tool != LLToolPie::getInstance() // not default tool + && tool != LLToolCompGun::getInstance() // not coming out of mouselook + && !suppress_toolbox // not override in third person + && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode + && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset + && (!captor || dynamic_cast(captor) != NULL))) // not dragging + { // Force floater tools to be visible (unless minimized) if (!gFloaterTools->getVisible()) @@ -3123,6 +3300,7 @@ void LLViewerWindow::updateUI() } // Update the location of the blue box tool popup LLCoordGL select_center_screen; + MASK mask = gKeyboard->currentMask(TRUE); gFloaterTools->updatePopup( select_center_screen, mask ); } else @@ -3208,40 +3386,6 @@ void LLViewerWindow::updateUI() gConsole->setRect(console_rect); } - mLastMousePoint = mCurrentMousePoint; - - // last ditch force of edit menu to selection manager - if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) - { - LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); - } - - if (gFloaterView->getCycleMode()) - { - // sync all floaters with their focus state - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) - { - // control key no longer held down, finish cycle mode - gFloaterView->setCycleMode(FALSE); - - gFloaterView->syncFloaterTabOrder(); - } - else - { - // user holding down CTRL, don't update tab order of floaters - } - } - else - { - // update focused floater - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - // make sure floater visible order is in sync with tab order - gFloaterView->syncFloaterTabOrder(); - } - static const LLCachedControl chat_bar_steals_focus("ChatBarStealsFocus",true); if (chat_bar_steals_focus && gChatBar @@ -3250,49 +3394,6 @@ void LLViewerWindow::updateUI() { gChatBar->startChat(NULL); } - - // cleanup unused selections when no modal dialogs are open - if (LLModalDialog::activeCount() == 0) - { - LLViewerParcelMgr::getInstance()->deselectUnused(); - } - - if (LLModalDialog::activeCount() == 0) - { - LLSelectMgr::getInstance()->deselectUnused(); - } - - // per frame picking - for tooltips and changing cursor over interactive objects - static S32 previous_x = -1; - static S32 previous_y = -1; - static BOOL mouse_moved_since_pick = FALSE; - - if ((previous_x != x) || (previous_y != y)) - mouse_moved_since_pick = TRUE; - - static const LLCachedControl picks_moving("PicksPerSecondMouseMoving",5.f); - static const LLCachedControl picks_stationary("PicksPerSecondMouseStationary",0.f); - if( !getCursorHidden() - // When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly. - && (LLViewerMediaFocus::getInstance()->getFocus() - || ((mouse_moved_since_pick) && (picks_moving > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_moving)) - || ((!mouse_moved_since_pick) && (picks_stationary > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_stationary)))) - { - mouse_moved_since_pick = FALSE; - mPickTimer.reset(); - pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE, TRUE); - } - - previous_x = x; - previous_y = y; - - return; -} - -/* static */ -void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info) -{ - gViewerWindow->mHoverPick = pick_info; } void LLViewerWindow::updateMouseDelta() @@ -3339,6 +3440,81 @@ void LLViewerWindow::updateMouseDelta() mMouseVelocityStat.addValue(mouse_vel.magVec()); } +void LLViewerWindow::updateKeyboardFocus() +{ + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + gFocusMgr.setKeyboardFocus(NULL); + } + + // clean up current focus + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (cur_focus) + { + if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) + { + gFocusMgr.releaseFocusIfNeeded(cur_focus); + + LLUICtrl* parent = cur_focus->getParentUICtrl(); + const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + while(parent) + { + if (parent->isCtrl() + && (parent->hasTabStop() || parent == focus_root) + && !parent->getIsChrome() + && parent->isInVisibleChain() + && parent->isInEnabledChain()) + { + if (!parent->focusFirstItem()) + { + parent->setFocus(TRUE); + } + break; + } + parent = parent->getParentUICtrl(); + } + } + else if (cur_focus->isFocusRoot()) + { + // focus roots keep trying to delegate focus to their first valid descendant + // this assumes that focus roots are not valid focus holders on their own + cur_focus->focusFirstItem(); + } + } + // last ditch force of edit menu to selection manager + if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) + { + LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); + } + + if (gFloaterView->getCycleMode()) + { + // sync all floaters with their focus state + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + MASK mask = gKeyboard->currentMask(TRUE); + if ((mask & MASK_CONTROL) == 0) + { + // control key no longer held down, finish cycle mode + gFloaterView->setCycleMode(FALSE); + + gFloaterView->syncFloaterTabOrder(); + } + else + { + // user holding down CTRL, don't update tab order of floaters + } + } + else + { + // update focused floater + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + // make sure floater visible order is in sync with tab order + gFloaterView->syncFloaterTabOrder(); + } +} + void LLViewerWindow::saveLastMouse(const LLCoordGL &point) { // Store last mouse location. diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 2eb84e8db..a84413734 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -163,7 +163,8 @@ public: /*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ BOOL handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ BOOL handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); - /*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask); + /*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data); + void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ void handleMouseLeave(LLWindow *window); /*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y); /*virtual*/ void handleFocus(LLWindow *window); @@ -282,8 +283,10 @@ public: void updateObjectUnderCursor(); - void updateUI(); // Once per frame, update UI based on mouse position + void updateUI(); // Once per frame, update UI based on mouse position, calls following update* functions + void updateLayout(); void updateMouseDelta(); + void updateKeyboardFocus(); BOOL handleKey(KEY key, MASK mask); void handleScrollWheel (S32 clicks); @@ -434,8 +437,6 @@ protected: // Variables used for tool override switching based on modifier keys. JC MASK mLastMask; // used to detect changes in modifier mask LLTool* mToolStored; // the tool we're overriding - BOOL mSuppressToolbox; // sometimes hide the toolbox, despite - // having a camera tool selected BOOL mHideCursorPermanent; // true during drags, mouselook BOOL mCursorHidden; LLPickInfo mLastPick; @@ -462,6 +463,9 @@ protected: static std::string sSnapshotDir; static std::string sMovieBaseName; + + // Object temporarily hovered over while dragging + LLPointer mDragHoveredObject; }; class LLBottomPanel : public LLPanel diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index b3faa57ac..f16436493 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -363,6 +363,12 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable) facep->mCenterLocal = part->mPosAgent; facep->setFaceColor(part->mColor); facep->setTexture(part->mImagep); + + //check if this particle texture is replaced by a parcel media texture. + if(part->mImagep.notNull() && part->mImagep->hasParcelMedia()) + { + part->mImagep->getParcelMedia()->addMediaToFace(facep) ; + } mPixelArea = tot_area * pixel_meter_ratio; const F32 area_scale = 10.f; // scale area to increase priority a bit diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index dbf9e1295..ae4ea1e9d 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -35,6 +35,8 @@ #include "llvovolume.h" +#include + #include "llviewercontrol.h" #include "lldir.h" #include "llflexibleobject.h" @@ -47,6 +49,7 @@ #include "llvolumemessage.h" #include "material_codes.h" #include "message.h" +#include "llpluginclassmedia.h" // for code in the mediaEvent handler #include "object_flags.h" #include "llagentconstants.h" #include "lldrawable.h" @@ -69,8 +72,11 @@ #include "pipeline.h" #include "llsdutil.h" #include "llmatrix4a.h" +#include "llmediaentry.h" +#include "llmediadataclient.h" #include "llmeshrepository.h" #include "llagent.h" +#include "llviewermediafocus.h" #include "lldatapacker.h" #include "llviewershadermgr.h" #include "llvoavatar.h" @@ -94,10 +100,114 @@ F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; S32 LLVOVolume::mRenderComplexity_last = 0; S32 LLVOVolume::mRenderComplexity_current = 0; +LLPointer LLVOVolume::sObjectMediaClient = NULL; +LLPointer LLVOVolume::sObjectMediaNavigateClient = NULL; + static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures"); +// Implementation class of LLMediaDataClientObject. See llmediadataclient.h +class LLMediaDataClientObjectImpl : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) + { + mObject->addMDCImpl(); + } + ~LLMediaDataClientObjectImpl() + { + mObject->removeMDCImpl(); + } + + virtual U8 getMediaDataCount() const + { return mObject->getNumTEs(); } + + virtual LLSD getMediaDataLLSD(U8 index) const + { + LLSD result; + LLTextureEntry *te = mObject->getTE(index); + if (NULL != te) + { + llassert((te->getMediaData() != NULL) == te->hasMedia()); + if (te->getMediaData() != NULL) + { + result = te->getMediaData()->asLLSD(); + // XXX HACK: workaround bug in asLLSD() where whitelist is not set properly + // See DEV-41949 + if (!result.has(LLMediaEntry::WHITELIST_KEY)) + { + result[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray(); + } + } + } + return result; + } + virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const + { + LLTextureEntry *te = mObject->getTE(index); + if (te) + { + if (te->getMediaData()) + { + return (te->getMediaData()->getCurrentURL() == url); + } + } + return url.empty(); + } + + virtual LLUUID getID() const + { return mObject->getID(); } + + virtual void mediaNavigateBounceBack(U8 index) + { mObject->mediaNavigateBounceBack(index); } + + virtual bool hasMedia() const + { return mObject->hasMedia(); } + + virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string) + { mObject->updateObjectMediaData(data, version_string); } + + virtual F64 getMediaInterest() const + { + F64 interest = mObject->getTotalMediaInterest(); + if (interest < (F64)0.0) + { + // media interest not valid yet, try pixel area + interest = mObject->getPixelArea(); + // HACK: force recalculation of pixel area if interest is the "magic default" of 1024. + if (interest == 1024.f) + { + const_cast(static_cast(mObject))->setPixelAreaAndAngle(gAgent); + interest = mObject->getPixelArea(); + } + } + return interest; + } + + virtual bool isInterestingEnough() const + { + return LLViewerMedia::isInterestingEnough(mObject, getMediaInterest()); + } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mObject->getRegion()->getCapability(name); } + + virtual bool isDead() const + { return mObject->isDead(); } + + virtual U32 getMediaVersion() const + { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); } + + virtual bool isNew() const + { return mNew; } + +private: + LLPointer mObject; + bool mNew; +}; + + LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mVolumeImpl(NULL) @@ -115,7 +225,11 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mLODChanged = FALSE; mSculptChanged = FALSE; mSpotLightPriority = 0.f; + + mMediaImplList.resize(getNumTEs()); + mLastFetchedMediaVersion = -1; mIndexInTex = 0; + mMDCImplCount = 0; } LLVOVolume::~LLVOVolume() @@ -124,12 +238,35 @@ LLVOVolume::~LLVOVolume() mTextureAnimp = NULL; delete mVolumeImpl; mVolumeImpl = NULL; + + if(!mMediaImplList.empty()) + { + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) + { + if(mMediaImplList[i].notNull()) + { + mMediaImplList[i]->removeObject(this) ; + } + } + } } void LLVOVolume::markDead() { if (!mDead) { + if(getMDCImplCount() > 0) + { + LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast(this), false); + if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj); + if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj); + } + + // Detach all media impls from this object + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) + { + removeMediaImpl(i); + } if (mSculptTexture.notNull()) { @@ -144,11 +281,26 @@ void LLVOVolume::markDead() // static void LLVOVolume::initClass() { + // gSavedSettings better be around + if (gSavedSettings.getBOOL("PrimMediaMasterEnabled")) + { + const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); + const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); + const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries"); + const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize"); + const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize"); + sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries, + max_sorted_queue_size, max_round_robin_queue_size); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, + max_retries, max_sorted_queue_size, max_round_robin_queue_size); + } } // static void LLVOVolume::cleanupClass() { + sObjectMediaClient = NULL; + sObjectMediaNavigateClient = NULL; } U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, @@ -157,6 +309,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, LLDataPacker *dp) { LLColor4U color; + const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA); // Do base class updates... U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); @@ -224,10 +377,16 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, // // Unpack texture entry data // - if (unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + + S32 result = unpackTEMessage(mesgsys, _PREHASH_ObjectData, (S32) block_num); + if (result & teDirtyBits) { updateTEData(); } + if (result & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } else { @@ -262,9 +421,16 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, llwarns << "Bogus TE data in " << getID() << llendl; } - else if (res2 & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + else { - updateTEData(); + if (res2 & teDirtyBits) + { + updateTEData(); + } + if (res2 & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } U32 value = dp->getPassFlags(); @@ -302,13 +468,38 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, U8 tdpbuffer[1024]; LLDataPackerBinaryBuffer tdp(tdpbuffer, 1024); mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num); - if ( unpackTEMessage(tdp) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + S32 result = unpackTEMessage(tdp); + if (result & teDirtyBits) { updateTEData(); } + if (result & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } } } + if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED)) + { + // If only the media URL changed, and it isn't a media version URL, + // ignore it + if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) && + mMedia && ! mMedia->mMediaURL.empty() && + ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) ) + { + // If the media changed at all, request new media data + LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED); + } + else { + LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + } + } + // ...and clean up any media impls + cleanUpMediaImpls(); return retval; } @@ -1187,6 +1378,20 @@ void LLVOVolume::regenFaces() facep->setTEOffset(i); facep->setTexture(getTEImage(i)); facep->setViewerObject(this); + + // If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face. + // Re-establish the link. + if((int)mMediaImplList.size() > i) + { + if(mMediaImplList[i]) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[i]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->addMediaToFace(facep) ; + } + } + } } if (!count_changed) @@ -1540,6 +1745,58 @@ BOOL LLVOVolume::isRootEdit() const return TRUE; } +//virtual +void LLVOVolume::setNumTEs(const U8 num_tes) +{ + const U8 old_num_tes = getNumTEs() ; + + if(old_num_tes && old_num_tes < num_tes) //new faces added + { + LLViewerObject::setNumTEs(num_tes) ; + + if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists. + { + mMediaImplList.resize(num_tes) ; + const LLTextureEntry* te = getTE(old_num_tes - 1) ; + for(U8 i = old_num_tes; i < num_tes ; i++) + { + setTE(i, *te) ; + mMediaImplList[i] = mMediaImplList[old_num_tes -1] ; + } + mMediaImplList[old_num_tes -1]->setUpdated(TRUE) ; + } + } + else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed + { + U8 end = mMediaImplList.size() ; + for(U8 i = num_tes; i < end ; i++) + { + removeMediaImpl(i) ; + } + mMediaImplList.resize(num_tes) ; + + LLViewerObject::setNumTEs(num_tes) ; + } + else + { + LLViewerObject::setNumTEs(num_tes) ; + } + + return ; +} + +//virtual +void LLVOVolume::changeTEImage(S32 index, LLViewerTexture* imagep) +{ + BOOL changed = (mTEImages[index] != imagep); + LLViewerObject::changeTEImage(index, imagep); + if (changed) + { + gPipeline.markTextured(mDrawable); + mFaceMappingChanged = TRUE; + } +} + void LLVOVolume::setTEImage(const U8 te, LLViewerTexture *imagep) { BOOL changed = (mTEImages[te] != imagep); @@ -1727,6 +1984,503 @@ void LLVOVolume::updateTEData() }*/ } +bool LLVOVolume::hasMedia() const +{ + bool result = false; + const U8 numTEs = getNumTEs(); + for (U8 i = 0; i < numTEs; i++) + { + const LLTextureEntry* te = getTE(i); + if(te->hasMedia()) + { + result = true; + break; + } + } + return result; +} + +LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id) +{ + LLVolume* volume = getVolume(); + LLVector4a result; + result.clear(); + + LLVector3 ret; + + if (volume && face_id < volume->getNumVolumeFaces()) + { + const LLVolumeFace& face = volume->getVolumeFace(face_id); + for (S32 i = 0; i < (S32)face.mNumVertices; ++i) + { + result.add(face.mNormals[i]); + } + + LLVector3 ret(result.getF32ptr()); + ret = volumeDirectionToAgent(ret); + ret.normVec(); + } + + return ret; +} + +void LLVOVolume::requestMediaDataUpdate(bool isNew) +{ + if (sObjectMediaClient) + sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew)); +} + +bool LLVOVolume::isMediaDataBeingFetched() const +{ + // I know what I'm doing by const_casting this away: this is just + // a wrapper class that is only going to do a lookup. + return (sObjectMediaClient) ? sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast(this), false)) : false; +} + +void LLVOVolume::cleanUpMediaImpls() +{ + // Iterate through our TEs and remove any Impls that are no longer used + const U8 numTEs = getNumTEs(); + for (U8 i = 0; i < numTEs; i++) + { + const LLTextureEntry* te = getTE(i); + if( ! te->hasMedia()) + { + // Delete the media IMPL! + removeMediaImpl(i) ; + } + } +} + +void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version) +{ + // media_data_array is an array of media entry maps + // media_version is the version string in the response. + U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version); + + // Only update it if it is newer! + if ( (S32)fetched_version > mLastFetchedMediaVersion) + { + mLastFetchedMediaVersion = fetched_version; + //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl; + + LLSD::array_const_iterator iter = media_data_array.beginArray(); + LLSD::array_const_iterator end = media_data_array.endArray(); + U8 texture_index = 0; + for (; iter != end; ++iter, ++texture_index) + { + syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/); + } + } +} + +void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool merge, bool ignore_agent) +{ + if(mDead) + { + // If the object has been marked dead, don't process media updates. + return; + } + + LLTextureEntry *te = getTE(texture_index); + if(!te) + { + return ; + } + + LL_DEBUGS("MediaOnAPrim") << "BEFORE: texture_index = " << texture_index + << " hasMedia = " << te->hasMedia() << " : " + << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl; + + std::string previous_url; + LLMediaEntry* mep = te->getMediaData(); + if(mep) + { + // Save the "current url" from before the update so we can tell if + // it changes. + previous_url = mep->getCurrentURL(); + } + + if (merge) + { + te->mergeIntoMediaData(media_data); + } + else { + // XXX Question: what if the media data is undefined LLSD, but the + // update we got above said that we have media flags?? Here we clobber + // that, assuming the data from the service is more up-to-date. + te->updateMediaData(media_data); + } + + mep = te->getMediaData(); + if(mep) + { + bool update_from_self = false; + if (!ignore_agent) + { + LLUUID updating_agent = LLTextureEntry::getAgentIDFromMediaVersionString(getMediaURL()); + update_from_self = (updating_agent == gAgent.getID()); + } + viewer_media_t media_impl = LLViewerMedia::updateMediaImpl(mep, previous_url, update_from_self); + + addMediaImpl(media_impl, texture_index) ; + } + else + { + removeMediaImpl(texture_index); + } + + LL_DEBUGS("MediaOnAPrim") << "AFTER: texture_index = " << texture_index + << " hasMedia = " << te->hasMedia() << " : " + << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl; +} + +void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) +{ + // Find the media entry for this navigate + const LLMediaEntry* mep = NULL; + viewer_media_t impl = getMediaImpl(texture_index); + LLTextureEntry *te = getTE(texture_index); + if(te) + { + mep = te->getMediaData(); + } + + if (mep && impl) + { + std::string url = mep->getCurrentURL(); + // Look for a ":", if not there, assume "http://" + if (!url.empty() && std::string::npos == url.find(':')) + { + url = "http://" + url; + } + // If the url we're trying to "bounce back" to is either empty or not + // allowed by the whitelist, try the home url. If *that* doesn't work, + // set the media as failed and unload it + if (url.empty() || !mep->checkCandidateUrl(url)) + { + url = mep->getHomeURL(); + // Look for a ":", if not there, assume "http://" + if (!url.empty() && std::string::npos == url.find(':')) + { + url = "http://" + url; + } + } + if (url.empty() || !mep->checkCandidateUrl(url)) + { + // The url to navigate back to is not good, and we have nowhere else + // to go. + LL_WARNS("MediaOnAPrim") << "FAILED to bounce back URL \"" << url << "\" -- unloading impl" << LL_ENDL; + impl->setMediaFailed(true); + } + else { + // Okay, navigate now + LL_INFOS("MediaOnAPrim") << "bouncing back to URL: " << url << LL_ENDL; + impl->navigateTo(url, "", false, true); + } + } +} + +bool LLVOVolume::hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type) +{ + // NOTE: This logic ALMOST duplicates the logic in the server (in particular, in llmediaservice.cpp). + if (NULL == media_entry ) return false; // XXX should we assert here? + + // The agent has permissions if: + // - world permissions are on, or + // - group permissions are on, and agent_id is in the group, or + // - agent permissions are on, and agent_id is the owner + + // *NOTE: We *used* to check for modify permissions here (i.e. permissions were + // granted if permModify() was true). However, this doesn't make sense in the + // viewer: we don't want to show controls or allow interaction if the author + // has deemed it so. See DEV-42115. + + U8 media_perms = (perm_type == MEDIA_PERM_INTERACT) ? media_entry->getPermsInteract() : media_entry->getPermsControl(); + + // World permissions + if (0 != (media_perms & LLMediaEntry::PERM_ANYONE)) + { + return true; + } + + // Group permissions + else if (0 != (media_perms & LLMediaEntry::PERM_GROUP)) + { + LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this); + if (obj_perm && gAgent.isInGroup(obj_perm->getGroup())) + { + return true; + } + } + + // Owner permissions + else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner()) + { + return true; + } + + return false; + +} + +void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location) +{ + bool block_navigation = false; + // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed + // to deal with multiple face indices. + int face_index = getFaceIndexWithMediaImpl(impl, -1); + + // Find the media entry for this navigate + LLMediaEntry* mep = NULL; + LLTextureEntry *te = getTE(face_index); + if(te) + { + mep = te->getMediaData(); + } + + if(mep) + { + if(!mep->checkCandidateUrl(new_location)) + { + block_navigation = true; + } + if (!block_navigation && !hasMediaPermission(mep, MEDIA_PERM_INTERACT)) + { + block_navigation = true; + } + } + else + { + LL_WARNS("MediaOnAPrim") << "Couldn't find media entry!" << LL_ENDL; + } + + if(block_navigation) + { + LL_INFOS("MediaOnAPrim") << "blocking navigate to URI " << new_location << LL_ENDL; + + // "bounce back" to the current URL from the media entry + mediaNavigateBounceBack(face_index); + } + else if (sObjectMediaNavigateClient) + { + + LL_DEBUGS("MediaOnAPrim") << "broadcasting navigate with URI " << new_location << LL_ENDL; + + sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location); + } +} + +void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event) +{ + switch(event) + { + + case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED: + { + switch(impl->getNavState()) + { + case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED: + { + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getLocation()); + } + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS: + // This navigate didn't change the current URL. + LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL; + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED: + // This is the first location changed event after the start of a server-directed nav. Don't broadcast it. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL; + break; + + default: + // This is a subsequent location-changed due to a redirect. Don't broadcast. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (redirect)" << LL_ENDL; + break; + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: + { + switch(impl->getNavState()) + { + case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: + { + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getNavigateURI()); + } + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS: + // This navigate didn't change the current URL. + LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL; + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: + // This is the the navigate complete event from a server-directed nav. Don't broadcast it. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL; + break; + + default: + // For all other states, the navigate should have been handled by LOCATION_CHANGED events already. + break; + } + } + break; + + default: + break; + } + +} + +void LLVOVolume::sendMediaDataUpdate() +{ + if (sObjectMediaClient) + sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false)); +} + +void LLVOVolume::removeMediaImpl(S32 texture_index) +{ + if(mMediaImplList.size() <= (U32)texture_index || mMediaImplList[texture_index].isNull()) + { + return ; + } + + //make the face referencing to mMediaImplList[texture_index] to point back to the old texture. + if(mDrawable && texture_index < mDrawable->getNumFaces()) + { + LLFace* facep = mDrawable->getFace(texture_index) ; + if(facep) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->removeMediaFromFace(facep) ; + } + } + } + + //check if some other face(s) of this object reference(s)to this media impl. + S32 i ; + S32 end = (S32)mMediaImplList.size() ; + for(i = 0; i < end ; i++) + { + if( i != texture_index && mMediaImplList[i] == mMediaImplList[texture_index]) + { + break ; + } + } + + if(i == end) //this object does not need this media impl. + { + mMediaImplList[texture_index]->removeObject(this) ; + } + + mMediaImplList[texture_index] = NULL ; + return ; +} + +void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) +{ + if((S32)mMediaImplList.size() < texture_index + 1) + { + mMediaImplList.resize(texture_index + 1) ; + } + + if(mMediaImplList[texture_index].notNull()) + { + if(mMediaImplList[texture_index] == media_impl) + { + return ; + } + + removeMediaImpl(texture_index) ; + } + + mMediaImplList[texture_index] = media_impl; + media_impl->addObject(this) ; + + //add the face to show the media if it is in playing + if(mDrawable) + { + LLFace* facep(NULL); + if( texture_index < mDrawable->getNumFaces() ) + { + facep = mDrawable->getFace(texture_index) ; + } + + if(facep) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->addMediaToFace(facep) ; + } + } + else //the face is not available now, start media on this face later. + { + media_impl->setUpdated(TRUE) ; + } + } + return ; +} + +viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const +{ + if(mMediaImplList.size() > face_id) + { + return mMediaImplList[face_id]; + } + return NULL; +} + +F64 LLVOVolume::getTotalMediaInterest() const +{ + // If this object is currently focused, this object has "high" interest + if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == getID()) + return F64_MAX; + + F64 interest = (F64)-1.0; // means not interested; + + // If this object is selected, this object has "high" interest, but since + // there can be more than one, we still add in calculated impl interest + // XXX Sadly, 'contains()' doesn't take a const :( + if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast(this))) + interest = F64_MAX / 2.0; + + int i = 0; + const int end = getNumTEs(); + for ( ; i < end; ++i) + { + const viewer_media_t &impl = getMediaImpl(i); + if (!impl.isNull()) + { + if (interest == (F64)-1.0) interest = (F64)0.0; + interest += impl->getInterest(); + } + } + return interest; +} + +S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id) +{ + S32 end = (S32)mMediaImplList.size() ; + for(S32 face_id = start_face_id + 1; face_id < end; face_id++) + { + if(mMediaImplList[face_id] == media_impl) + { + return face_id ; + } + } + return -1 ; +} + //---------------------------------------------------------------------------- void LLVOVolume::setLightTextureID(LLUUID id) @@ -2393,10 +3147,10 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const { invisi = 1; } - /*if (face->hasMedia()) + if (face->hasMedia()) { media_faces++; - }*/ + } if (te) { diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a83a33680..86ebdf584 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -35,6 +35,7 @@ #include "llviewerobject.h" #include "llviewertexture.h" +#include "llviewermedia.h" #include "llframetimer.h" #include "llapr.h" #include "m3math.h" // LLMatrix3 @@ -44,9 +45,13 @@ class LLViewerTextureAnim; class LLDrawPool; class LLSelectNode; +class LLObjectMediaDataClient; +class LLObjectMediaNavigateClient; class LLVOAvatar; class LLMeshSkinInfo; +typedef std::vector media_list_t; + enum LLVolumeInterfaceType { INTERFACE_FLEXIBLE = 1, @@ -174,6 +179,8 @@ public: /*virtual*/ void setScale(const LLVector3 &scale, BOOL damped); + /*virtual*/ void changeTEImage(S32 index, LLViewerTexture* new_image) ; + /*virtual*/ void setNumTEs(const U8 num_tes); /*virtual*/ void setTEImage(const U8 te, LLViewerTexture *imagep); /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid); /*virtual*/ S32 setTEColor(const U8 te, const LLColor3 &color); @@ -255,6 +262,41 @@ public: BOOL canBeFlexible() const; BOOL setIsFlexible(BOOL is_flexible); + + // Functions that deal with media, or media navigation + + // Update this object's media data with the given media data array + // (typically this is only called upon a response from a server request) + void updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version); + + // Bounce back media at the given index to its current URL (or home URL, if current URL is empty) + void mediaNavigateBounceBack(U8 texture_index); + + // Returns whether or not this object has permission to navigate or control + // the given media entry + enum MediaPermType { + MEDIA_PERM_INTERACT, MEDIA_PERM_CONTROL + }; + bool hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type); + + void mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location); + void mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event); + + + // Sync the given media data with the impl and the given te + void syncMediaData(S32 te, const LLSD &media_data, bool merge, bool ignore_agent); + + // Send media data update to the simulator. + void sendMediaDataUpdate(); + + viewer_media_t getMediaImpl(U8 face_id) const; + S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); + F64 getTotalMediaInterest() const; + + bool hasMedia() const; + + LLVector3 getApproximateFaceNormal(U8 face_id); + // tag: vaa emerald local_asset_browser void setSculptChanged(BOOL has_changed) { mSculptChanged = has_changed; } @@ -263,6 +305,12 @@ public: // Returns 'true' iff the media data for this object is in flight bool isMediaDataBeingFetched() const; + // Returns the "last fetched" media version, or -1 if not fetched yet + S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; } + + void addMDCImpl() { ++mMDCImplCount; } + void removeMDCImpl() { --mMDCImplCount; } + S32 getMDCImplCount() { return mMDCImplCount; } //rigged volume update (for raycasting) @@ -288,10 +336,16 @@ protected: // stats tracking for render complexity static S32 mRenderComplexity_last; static S32 mRenderComplexity_current; + + void requestMediaDataUpdate(bool isNew); + void cleanUpMediaImpls(); + void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ; + void removeMediaImpl(S32 texture_index) ; public: static S32 getRenderComplexityMax() {return mRenderComplexity_last;} static void updateRenderComplexity(); + LLViewerTextureAnim *mTextureAnimp; U8 mTexAnimMode; private: @@ -311,7 +365,10 @@ private: LLVolumeInterface *mVolumeImpl; LLPointer mSculptTexture; LLPointer mLightTexture; + media_list_t mMediaImplList; + S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1 S32 mIndexInTex; + S32 mMDCImplCount; LLPointer mRiggedVolume; @@ -321,6 +378,9 @@ public: static F32 sLODFactor; // LOD scale factor static F32 sDistanceFactor; // LOD distance factor + static LLPointer sObjectMediaClient; + static LLPointer sObjectMediaNavigateClient; + protected: static S32 sNumLODChanges; diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 4dbcfc578..514db939c 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -40,43 +40,117 @@ #include "llagent.h" #include "llappviewer.h" -#include "llfloatermediabrowser.h" +#include "llfloaterwebcontent.h" #include "llparcel.h" +#include "llsd.h" +#include "llui.h" +#include "lluri.h" #include "llviewercontrol.h" +#include "llviewermedia.h" #include "llviewernetwork.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewerwindow.h" +#include "llnotificationsutil.h" +#include "llalertdialog.h" #include "sgversion.h" +bool on_load_url_external_response(const LLSD& notification, const LLSD& response, bool async ); + +class URLLoader : public LLAlertDialog::URLLoader +{ + virtual void load(const std::string& url , bool force_open_externally) + { + if (force_open_externally) + { + LLWeb::loadURLExternal(url); + } + else + { + LLWeb::loadURL(url); + } + } +}; +static URLLoader sAlertURLLoader; + + // static void LLWeb::initClass() { LLAlertDialog::setURLLoader(&sAlertURLLoader); } + + + // static -void LLWeb::loadURL(const std::string& url) +void LLWeb::loadURL(const std::string& url, const std::string& target, const std::string& uuid) { - if (gSavedSettings.getBOOL("UseExternalBrowser")) + if(target == "_internal") + { + // Force load in the internal browser, as if with a blank target. + loadURLInternal(url, "", uuid); + } + else if (gSavedSettings.getBOOL("UseExternalBrowser") || (target == "_external")) { loadURLExternal(url); } else { - LLFloaterMediaBrowser::showInstance(url); + loadURLInternal(url, target, uuid); } } - // static -void LLWeb::loadURLExternal(const std::string& url) +// Explicitly open a Web URL using the Web content floater +void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid) { - std::string escaped_url = escapeURL(url); - gViewerWindow->getWindow()->spawnWebBrowser(escaped_url,true); + LLFloaterWebContent::Params p; + p.url(url).target(target).id(uuid); + LLFloaterWebContent::showInstance("web_content", p); } +// static +void LLWeb::loadURLExternal(const std::string& url, const std::string& uuid) +{ + loadURLExternal(url, true, uuid); +} + +// static +void LLWeb::loadURLExternal(const std::string& url, bool async, const std::string& uuid) +{ + // Act like the proxy window was closed, since we won't be able to track targeted windows in the external browser. + LLViewerMedia::proxyWindowClosed(uuid); + + if(gSavedSettings.getBOOL("DisableExternalBrowser")) + { + // Don't open an external browser under any circumstances. + llwarns << "Blocked attempt to open external browser." << llendl; + return; + } + + LLSD payload; + payload["url"] = url; + LLNotificationsUtil::add( "WebLaunchExternalTarget", LLSD(), payload, boost::bind(on_load_url_external_response, _1, _2, async)); +} + +// static +bool on_load_url_external_response(const LLSD& notification, const LLSD& response, bool async ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if ( 0 == option ) + { + LLSD payload = notification["payload"]; + std::string url = payload["url"].asString(); + std::string escaped_url = LLWeb::escapeURL(url); + if (gViewerWindow) + { + gViewerWindow->getWindow()->spawnWebBrowser(escaped_url, async); + } + } + return false; +} // static std::string LLWeb::curlEscape(const std::string& url) @@ -153,6 +227,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, substitution["VERSION_BUILD"] = gVersionBuild; substitution["CHANNEL"] = gVersionChannel; substitution["GRID"] = LLViewerLogin::getInstance()->getGridLabel(); + substitution["GRID_LOWERCASE"] = utf8str_tolower(LLViewerLogin::getInstance()->getGridLabel()); substitution["OS"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); substitution["SESSION_ID"] = gAgent.getSessionID(); substitution["FIRST_LOGIN"] = gAgent.isFirstLogin(); @@ -191,12 +266,3 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, return LLWeb::escapeURL(expanded_url); } - -// virtual -void LLWeb::URLLoader::load(const std::string& url) -{ - loadURL(url); -} - -// static -LLWeb::URLLoader LLWeb::sAlertURLLoader; diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h index 73f5c7ebe..5c88f1843 100644 --- a/indra/newview/llweb.h +++ b/indra/newview/llweb.h @@ -35,21 +35,28 @@ #define LL_LLWEB_H #include -#include "llalertdialog.h" +/// +/// The LLWeb class provides various static methods to display the +/// contents of a Url in a web browser. Variations are provided to +/// let you specifically use the Second Life internal browser, the +/// operating system's default browser, or to respect the user's +/// setting for which of these two they prefer to use with SL. +/// class LLWeb { public: static void initClass(); - // Loads unescaped url in either internal web browser or external - // browser, depending on user settings. - static void loadURL(const std::string& url); - - static void loadURL(const char* url) { loadURL( ll_safe_string(url) ); } + /// Load the given url in the operating system's web browser, async if we want to return immediately + /// before browser has spawned + static void loadURLExternal(const std::string& url) {loadURLExternal(url, LLStringUtil::null);} + static void loadURLExternal(const std::string& url, const std::string& uuid); + static void loadURLExternal(const std::string& url, bool async, const std::string& uuid = LLStringUtil::null); - // Loads unescaped url in external browser. - static void loadURLExternal(const std::string& url); + static void loadURL(const std::string& url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); + // load content using built-in browser + static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); // Behaves like the old curl_escape. static std::string curlEscape(const std::string& url); @@ -60,13 +67,6 @@ public: /// Expands various strings like [LANG], [VERSION], etc. in a URL static std::string expandURLSubstitutions(const std::string &url, const LLSD &default_subs); - - class URLLoader : public LLAlertDialog::URLLoader - { - virtual void load(const std::string& url); - }; - - static URLLoader sAlertURLLoader; }; #endif diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp index b25a3db75..5729bcb11 100644 --- a/indra/newview/llworldmapmessage.cpp +++ b/indra/newview/llworldmapmessage.cpp @@ -227,11 +227,12 @@ void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) gFloaterWorldMap->trackLocation(pos_global); } + U64 handle = to_region_handle(x_world, y_world); // Handle the SLURL callback if any url_callback_t callback = LLWorldMapMessage::getInstance()->mSLURLCallback; if(callback != NULL) { - U64 handle = to_region_handle(x_world, y_world); + // Check if we reached the requested region if ((LLStringUtil::compareInsensitive(LLWorldMapMessage::getInstance()->mSLURLRegionName, name)==0) || (LLWorldMapMessage::getInstance()->mSLURLRegionHandle == handle)) @@ -244,8 +245,7 @@ void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) } } if( gAgent.mPendingLure && - (U16)(gAgent.mPendingLure->mPosGlobal.mdV[0] / REGION_WIDTH_UNITS) == x_regions && - (U16)(gAgent.mPendingLure->mPosGlobal.mdV[1] / REGION_WIDTH_UNITS) == y_regions ) + gAgent.mPendingLure->mRegionHandle == handle) { gAgent.onFoundLureDestination(); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 311d21c0b..88f1ad044 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -82,6 +82,7 @@ #include "lltool.h" #include "lltoolmgr.h" #include "llviewercamera.h" +#include "llviewermediafocus.h" #include "llviewertexturelist.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -295,6 +296,7 @@ S32 LLPipeline::sCompiles = 0; BOOL LLPipeline::sPickAvatar = TRUE; BOOL LLPipeline::sDynamicLOD = TRUE; BOOL LLPipeline::sShowHUDAttachments = TRUE; +BOOL LLPipeline::sRenderMOAPBeacons = FALSE; BOOL LLPipeline::sRenderPhysicalBeacons = TRUE; BOOL LLPipeline::sRenderScriptedBeacons = FALSE; BOOL LLPipeline::sRenderScriptedTouchBeacons = TRUE; @@ -1416,7 +1418,7 @@ U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerTexture* ima bool alpha = te->getColor().mV[3] < 0.999f; if (imagep) { - alpha = alpha || (imagep->getComponents() == 4 && ! imagep->mIsMediaTexture) || (imagep->getComponents() == 2); + alpha = alpha || (imagep->getComponents() == 4 && imagep->getType() != LLViewerTexture::MEDIA_TEXTURE) || (imagep->getComponents() == 2); } if (alpha) @@ -3328,6 +3330,47 @@ void renderPhysicalBeacons(LLDrawable* drawablep) } } +void renderMOAPBeacons(LLDrawable* drawablep) +{ + LLViewerObject *vobj = drawablep->getVObj(); + + if(!vobj || vobj->isAvatar()) + return; + + BOOL beacon=FALSE; + U8 tecount=vobj->getNumTEs(); + for(int x=0;xgetTE(x)->hasMedia()) + { + beacon=TRUE; + break; + } + } + if(beacon==TRUE) + { + if (gPipeline.sRenderBeacons) + { + static const LLCachedControl DebugBeaconLineWidth("DebugBeaconLineWidth",1); + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 1.f, 1.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), DebugBeaconLineWidth); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + LLFace * facep = drawablep->getFace(face_id); + if (facep) + { + gPipeline.mHighlightFaces.push_back(facep); + } + } + } +} +} + void renderParticleBeacons(LLDrawable* drawablep) { // Look for attachments, objects, etc. @@ -3541,6 +3584,11 @@ void LLPipeline::postSort(LLCamera& camera) forAllVisibleDrawables(renderPhysicalBeacons); } + if(sRenderMOAPBeacons) + { + forAllVisibleDrawables(renderMOAPBeacons); + } + if (sRenderParticleBeacons) { forAllVisibleDrawables(renderParticleBeacons); @@ -6007,6 +6055,24 @@ BOOL LLPipeline::getRenderScriptedTouchBeacons(void*) return sRenderScriptedTouchBeacons; } +// static +void LLPipeline::setRenderMOAPBeacons(BOOL val) +{ + sRenderMOAPBeacons = val; +} + +// static +void LLPipeline::toggleRenderMOAPBeacons(void*) +{ + sRenderMOAPBeacons = !sRenderMOAPBeacons; +} + +// static +BOOL LLPipeline::getRenderMOAPBeacons(void*) +{ + return sRenderMOAPBeacons; +} + // static void LLPipeline::setRenderPhysicalBeacons(BOOL val) { @@ -6727,7 +6793,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield, b LLVector3 focus_point; - /*LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); + LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); if (obj && obj->mDrawable && obj->isSelected()) { //focus on selected media object S32 face_idx = LLViewerMediaFocus::getInstance()->getFocusedFace(); @@ -6739,7 +6805,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield, b focus_point = face->getPositionAgent(); } } - }*/ + } if (focus_point.isExactlyZero()) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index b8ede8af7..3bde9fbcd 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -370,6 +370,10 @@ public: static void toggleRenderSoundBeacons(void* data); static BOOL getRenderSoundBeacons(void* data); + static void setRenderMOAPBeacons(BOOL val); + static void toggleRenderMOAPBeacons(void * data); + static BOOL getRenderMOAPBeacons(void * data); + static void setRenderPhysicalBeacons(BOOL val); static void toggleRenderPhysicalBeacons(void* data); static BOOL getRenderPhysicalBeacons(void* data); @@ -778,6 +782,7 @@ protected: S32 mLightingDetail; static BOOL sRenderPhysicalBeacons; + static BOOL sRenderMOAPBeacons; static BOOL sRenderScriptedTouchBeacons; static BOOL sRenderScriptedBeacons; static BOOL sRenderParticleBeacons; diff --git a/indra/newview/skins/default/xui/en-us/floater_directory.xml b/indra/newview/skins/default/xui/en-us/floater_directory.xml index 3a3983bb1..b0a972a5b 100644 --- a/indra/newview/skins/default/xui/en-us/floater_directory.xml +++ b/indra/newview/skins/default/xui/en-us/floater_directory.xml @@ -73,7 +73,7 @@ halign="center" height="18" label="?" label_selected="?" left="-25" mouse_opaque="true" name="?" width="18" /> + name="find_browser" trusted_content="true" right="-10" top="-60" /> @@ -335,6 +335,7 @@ + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_login.xml b/indra/newview/skins/default/xui/en-us/panel_login.xml index 392183aad..aebdcc41a 100644 --- a/indra/newview/skins/default/xui/en-us/panel_login.xml +++ b/indra/newview/skins/default/xui/en-us/panel_login.xml @@ -62,12 +62,12 @@ left="475" mouse_opaque="true" name="start_location_text" v_pad="0" width="120"> Start Location: - - + My Home - + My Last Location diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_audio.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_audio.xml index c46212512..727e05255 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_audio.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_audio.xml @@ -5,7 +5,7 @@ Audio Preferences: - + From 821fd1f7d67577614f7aa1f0457ffb0bf31f46ab Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 4 Jun 2013 03:46:26 -0500 Subject: [PATCH 05/36] Handle fmodex internal channel release by registering channel callbacks. Harden channel<->source associations. Assign console output for audio system "AudioEngine" and "AudioImpl" tags. Added base LLAudioChannel::cleanup() vfunc to handle non-impl-related variable cleanup. Killed some redundant code. --- indra/llaudio/llaudioengine.cpp | 125 +++++++++---------------- indra/llaudio/llaudioengine.h | 2 +- indra/llaudio/llaudioengine_fmod.cpp | 2 +- indra/llaudio/llaudioengine_fmodex.cpp | 61 +++++++----- indra/llaudio/llaudioengine_fmodex.h | 2 +- indra/llaudio/llaudioengine_openal.cpp | 3 +- 6 files changed, 89 insertions(+), 106 deletions(-) diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 558c929e1..2c678346a 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -126,7 +126,7 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata) // Initialize the decode manager gAudioDecodeMgrp = new LLAudioDecodeMgr; - llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; + LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; return true; } @@ -311,8 +311,7 @@ void LLAudioEngine::idle(F32 max_decode_time) LLAudioChannel *channelp = getFreeChannel(max_priority); if (channelp) { - //llinfos << "Replacing source in channel due to priority!" << llendl; - max_sourcep->setChannel(channelp); + LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << llendl; channelp->setSource(max_sourcep); if (max_sourcep->isSyncSlave()) { @@ -348,18 +347,22 @@ void LLAudioEngine::idle(F32 max_decode_time) } LLAudioChannel *channelp = sourcep->getChannel(); - if (!channelp) + bool is_stopped = channelp && channelp->isPlaying(); + if (is_stopped || (sourcep->isLoop() && channelp->mLoopedThisFrame)) { // This sound isn't playing, so we just process move the queue sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; - // Reset the timer so the source doesn't die. - sourcep->mAgeTimer.reset(); - // Make sure we have the buffer set up if we just decoded the data - if (sourcep->mCurrentDatap) + if(is_stopped) { - updateBufferForData(sourcep->mCurrentDatap); + // Reset the timer so the source doesn't die. + sourcep->mAgeTimer.reset(); + // Make sure we have the buffer set up if we just decoded the data + if (sourcep->mCurrentDatap) + { + updateBufferForData(sourcep->mCurrentDatap); + } } // Actually play the associated data. @@ -372,52 +375,6 @@ void LLAudioEngine::idle(F32 max_decode_time) } continue; } - else - { - // Check to see if the current sound is done playing, or looped. - if (!channelp->isPlaying()) - { - sourcep->mCurrentDatap = sourcep->mQueuedDatap; - sourcep->mQueuedDatap = NULL; - - // Reset the timer so the source doesn't die. - sourcep->mAgeTimer.reset(); - - // Make sure we have the buffer set up if we just decoded the data - if (sourcep->mCurrentDatap) - { - updateBufferForData(sourcep->mCurrentDatap); - } - - // Actually play the associated data. - sourcep->setupChannel(); - channelp = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->play(); - } - } - else if (sourcep->isLoop()) - { - // It's a loop, we need to check and see if we're done with it. - if (channelp->mLoopedThisFrame) - { - sourcep->mCurrentDatap = sourcep->mQueuedDatap; - sourcep->mQueuedDatap = NULL; - - // Actually, should do a time sync so if we're a loop master/slave - // we don't drift away. - sourcep->setupChannel(); - channelp = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->play(); - } - } - } - } } // Lame, update the channels AGAIN. @@ -491,7 +448,7 @@ void LLAudioEngine::idle(F32 max_decode_time) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << llendl; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; @@ -603,8 +560,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() if (buffer_id >= 0) { - lldebugs << "Taking over unused buffer " << buffer_id << llendl; - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Taking over unused buffer! max_age=" << max_age << llendl; mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL; for (U32 i = 0; i < MAX_CHANNELS; i++) { @@ -638,6 +594,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) // Channel is allocated but not playing right now, use it. if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting()) { + LL_DEBUGS("AudioEngine") << "Replacing unused channel" << llendl; mChannels[i]->cleanup(); if (mChannels[i]->getSource()) { @@ -670,9 +627,9 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) return NULL; } + LL_DEBUGS("AudioEngine") << "Flushing min channel" << llendl; // Flush the minimum priority channel, and return it. min_channelp->cleanup(); - min_channelp->getSource()->setChannel(NULL); return min_channelp; } @@ -1006,10 +963,13 @@ LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) if( sourcep && sourcep->getCurrentData() && sourcep->getCurrentData()->getID() == audio_uuid ) { LLAudioChannel* chan=sourcep->getChannel(); - delete sourcep; if(chan) + { + LL_DEBUGS("AudioEngine") << "removeAudioData" << llendl; chan->cleanup(); - mAllSources.erase(iter2++); + } + delete sourcep; + mAllSources.erase(iter2++); } else ++iter2; @@ -1043,7 +1003,7 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp) iter = mAllSources.find(asp->getID()); if (iter == mAllSources.end()) { - llwarns << "Cleaning up unknown audio source!" << llendl; + LL_WARNS("AudioEngine") << "Cleaning up unknown audio source!" << llendl; return; } delete asp; @@ -1264,7 +1224,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.notNull()) { - llinfos << "Getting asset data for: " << asset_id << llendl; + LL_DEBUGS("AudioEngine") << "Getting asset data for: " << asset_id << llendl; gAudiop->mCurrentTransfer = asset_id; gAudiop->mCurrentTransferTimer.reset(); gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, @@ -1282,7 +1242,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E { if (result_code) { - llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; + LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; // Need to mark data as bad to avoid constant rerequests. LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) @@ -1299,7 +1259,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E if (!adp) { // Should never happen - llwarns << "Got asset callback without audio data for " << uuid << llendl; + LL_WARNS("AudioEngine") << "Got asset callback without audio data for " << uuid << llendl; } else { @@ -1360,8 +1320,7 @@ LLAudioSource::~LLAudioSource() if (mChannelp) { // Stop playback of this sound - mChannelp->setSource(NULL); - setChannel(NULL); + mChannelp->cleanup(); } } @@ -1404,7 +1363,7 @@ void LLAudioSource::update() } else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done { - llwarns << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; + LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; mCorrupted = true ; } } @@ -1475,7 +1434,6 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) if (getChannel()) { getChannel()->setSource(NULL); - setChannel(NULL); if (!isMuted()) { mCurrentDatap = NULL; @@ -1729,11 +1687,16 @@ LLAudioChannel::~LLAudioChannel() { // Need to disconnect any sources which are using this channel. //llinfos << "Cleaning up audio channel" << llendl; - if (mCurrentSourcep) - { + cleanup(); +} + +void LLAudioChannel::cleanup() +{ + if(mCurrentSourcep) mCurrentSourcep->setChannel(NULL); - } mCurrentBufferp = NULL; + mCurrentSourcep = NULL; + mWaiting = false; } @@ -1744,22 +1707,21 @@ void LLAudioChannel::setSource(LLAudioSource *sourcep) if (!sourcep) { // Clearing the source for this channel, don't need to do anything. - //llinfos << "Clearing source for channel" << llendl; + LL_DEBUGS("AudioEngine") << "Clearing source for channel" << llendl; cleanup(); - mCurrentSourcep = NULL; - mWaiting = false; return; } if (sourcep == mCurrentSourcep) { // Don't reallocate the channel, this will make FMOD goofy. - //llinfos << "Calling setSource with same source!" << llendl; + LL_DEBUGS("AudioEngine") << "Calling setSource with same source!" << llendl; } + cleanup(); mCurrentSourcep = sourcep; - - + mCurrentSourcep->setChannel(this); + updateBuffer(); update3DPosition(); } @@ -1796,7 +1758,10 @@ bool LLAudioChannel::updateBuffer() // The source changed what buffer it's playing. We need to clean up // the existing channel // + LLAudioSource* source = mCurrentSourcep; cleanup(); + mCurrentSourcep = source; + mCurrentSourcep->setChannel(this); mCurrentBufferp = bufferp; if (bufferp) @@ -1856,7 +1821,7 @@ bool LLAudioData::load() if (mBufferp) { // We already have this sound in a buffer, don't do anything. - llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl; + LL_INFOS("AudioEngine") << "Already have a buffer for this sound, don't bother loading!" << llendl; return true; } @@ -1864,7 +1829,7 @@ bool LLAudioData::load() if (!mBufferp) { // No free buffers, abort. - lldebugs << "Not able to allocate a new audio buffer, aborting." << llendl; + LL_DEBUGS("AudioEngine") << "Not able to allocate a new audio buffer, aborting." << llendl; return false; } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index 230168ee9..d58758709 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -447,7 +447,7 @@ public: protected: virtual void play() = 0; virtual void playSynced(LLAudioChannel *channelp) = 0; - virtual void cleanup() = 0; + virtual void cleanup(); void setWaiting(bool waiting) { mWaiting = waiting; } public: diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp index f47a6cac0..4c4f4f48d 100644 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -576,6 +576,7 @@ void LLAudioChannelFMOD::updateLoop() void LLAudioChannelFMOD::cleanup() { + LLAudioChannel::cleanup(); if (!mChannelID) { //llinfos << "Aborting cleanup with no channelID." << llendl; @@ -588,7 +589,6 @@ void LLAudioChannelFMOD::cleanup() LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; } - mCurrentBufferp = NULL; mChannelID = 0; } diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 56997a888..adcdf2b7f 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -93,7 +93,7 @@ inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) { if(result == FMOD_OK) return false; - llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << llendl; return true; } @@ -101,11 +101,11 @@ void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const cha { if(type & FMOD_MEMORY_STREAM_DECODE) { - llinfos << "Decode buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Decode buffer size: " << size << llendl; } else if(type & FMOD_MEMORY_STREAM_FILE) { - llinfos << "Strean buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Stream buffer size: " << size << llendl; } return new char[size]; } @@ -344,7 +344,7 @@ void LLAudioEngine_FMODEX::allocateListener(void) mListenerp = (LLListener *) new LLListener_FMODEX(mSystem); if (!mListenerp) { - llwarns << "Listener creation failed" << llendl; + LL_WARNS("AudioImpl") << "Listener creation failed" << llendl; } } @@ -353,13 +353,13 @@ void LLAudioEngine_FMODEX::shutdown() { stopInternetStream(); - llinfos << "About to LLAudioEngine::shutdown()" << llendl; + LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << llendl; LLAudioEngine::shutdown(); - llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; mSystem->close(); mSystem->release(); - llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; delete mListenerp; mListenerp = NULL; @@ -493,6 +493,27 @@ LLAudioChannelFMODEX::~LLAudioChannelFMODEX() cleanup(); } +static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2) +{ + if(type == FMOD_CHANNEL_CALLBACKTYPE_END) + { + FMOD::Channel* chan = reinterpret_cast(channel); + LLAudioChannelFMODEX* audio_channel = NULL; + chan->getUserData((void**)&audio_channel); + if(audio_channel) + { + audio_channel->onRelease(); + } + } + return FMOD_OK; +} + +void LLAudioChannelFMODEX::onRelease() +{ + mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. + cleanup(); +} + bool LLAudioChannelFMODEX::updateBuffer() { if (LLAudioChannel::updateBuffer()) @@ -508,7 +529,7 @@ bool LLAudioChannelFMODEX::updateBuffer() { // This is bad, there should ALWAYS be a sound associated with a legit // buffer. - llerrs << "No FMOD sound!" << llendl; + LL_ERRS("AudioImpl") << "No FMOD sound!" << llendl; return false; } @@ -519,9 +540,13 @@ bool LLAudioChannelFMODEX::updateBuffer() { FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp); Check_FMOD_Error(result, "FMOD::System::playSound"); - } + if(result == FMOD_OK) + { - //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; + mChannelp->setCallback(&channel_callback); + mChannelp->setUserData(this); + } + } } // If we have a source for the channel, we need to update its gain. @@ -535,13 +560,6 @@ bool LLAudioChannelFMODEX::updateBuffer() Check_FMOD_Error(result, "FMOD::Channel::setVolume"); result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); Check_FMOD_Error(result, "FMOD::Channel::setMode"); - /*if(Check_FMOD_Error(result, "FMOD::Channel::setMode")) - { - S32 index; - mChannelp->getIndex(&index); - llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID() - << " at " << mCurrentSourcep->getPositionGlobal() << llendl; - }*/ } return true; @@ -608,16 +626,17 @@ void LLAudioChannelFMODEX::updateLoop() void LLAudioChannelFMODEX::cleanup() { + LLAudioChannel::cleanup(); + if (!mChannelp) { //llinfos << "Aborting cleanup with no channel handle." << llendl; return; } - //llinfos << "Cleaning up channel: " << mChannelID << llendl; + mChannelp->setCallback(NULL); Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); - mCurrentBufferp = NULL; mChannelp = NULL; } @@ -626,7 +645,7 @@ void LLAudioChannelFMODEX::play() { if (!mChannelp) { - llwarns << "Playing without a channel handle, aborting" << llendl; + LL_WARNS("AudioImpl") << "Playing without a channel handle, aborting" << llendl; return; } @@ -734,7 +753,7 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) if (result != FMOD_OK) { // We failed to load the file for some reason. - llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; // // If we EVER want to load wav files provided by end users, we need diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h index 6b780128f..7f9a28b04 100644 --- a/indra/llaudio/llaudioengine_fmodex.h +++ b/indra/llaudio/llaudioengine_fmodex.h @@ -95,7 +95,7 @@ class LLAudioChannelFMODEX : public LLAudioChannel public: LLAudioChannelFMODEX(FMOD::System *audioengine); virtual ~LLAudioChannelFMODEX(); - + void onRelease(); protected: /*virtual*/ void play(); /*virtual*/ void playSynced(LLAudioChannel *channelp); diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 1b2c8c6ee..10ce5e6ed 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -186,10 +186,9 @@ LLAudioChannelOpenAL::~LLAudioChannelOpenAL() void LLAudioChannelOpenAL::cleanup() { + LLAudioChannel::cleanup(); alSourceStop(mALSource); alSourcei(mALSource, AL_BUFFER, AL_NONE); - - mCurrentBufferp = NULL; } void LLAudioChannelOpenAL::play() From 3a7955192eee480aabe8e098abcfe7c85445c54c Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 4 Jun 2013 04:39:51 -0500 Subject: [PATCH 06/36] Temporary super-obsessive audioengine debugging/diagnostics. --- indra/llaudio/llaudioengine.cpp | 67 +++++++ indra/llaudio/llaudioengine.h | 2 + indra/llaudio/llaudioengine_fmodex.cpp | 215 ++++++++++++++++++++- indra/llaudio/llaudioengine_fmodex.h | 6 +- indra/newview/app_settings/settings_sh.xml | 11 ++ indra/newview/llstartup.cpp | 2 +- 6 files changed, 299 insertions(+), 4 deletions(-) diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 2c678346a..f07965877 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -228,6 +228,28 @@ std::string LLAudioEngine::getInternetStreamURL() else return std::string(); } +void LLAudioEngine::checkStates() +{ +#ifdef SHOW_ASSERT + for (S32 i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i]) + { + bool buf_has_ref = false; + for (S32 j = 0; j < MAX_CHANNELS; j++) + { + if (mChannels[j]) + { + if(mChannels[j]->mCurrentBufferp == mBuffers[i]) + buf_has_ref = true; + } + } + if(buf_has_ref) + llassert(mBuffers[i]->mInUse); + } + } +#endif //SHOW_ASSERT +} void LLAudioEngine::updateChannels() { @@ -239,8 +261,31 @@ void LLAudioEngine::updateChannels() mChannels[i]->updateBuffer(); mChannels[i]->update3DPosition(); mChannels[i]->updateLoop(); +#ifdef SHOW_ASSERT + if(mChannels[i]->getSource()) + llassert(mChannels[i]->mCurrentBufferp == mChannels[i]->getSource()->getCurrentBuffer()); + if(mChannels[i]->mCurrentBufferp) + { + bool found_buffer = false; + for (S32 j = 0; j < MAX_BUFFERS; j++) + { + if (mBuffers[j]) + { + if(mChannels[i]->mCurrentBufferp == mBuffers[j]) + found_buffer = true; + } + } + llassert(found_buffer); + if(!mChannels[i]->mCurrentBufferp->mInUse) + { + llassert(!mChannels[i]->isPlaying()); + llassert(!mChannels[i]->isWaiting()); + } + } +#endif //SHOW_ASSERT } } + checkStates(); } static const F32 default_max_decode_time = .002f; // 2 ms @@ -312,7 +357,14 @@ void LLAudioEngine::idle(F32 max_decode_time) if (channelp) { LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << llendl; + llassert(max_sourcep->getChannel() == NULL); + + llassert(channelp->mCurrentBufferp == NULL); channelp->setSource(max_sourcep); + + llassert(max_sourcep == channelp->getSource()); + llassert(channelp->mCurrentBufferp == max_sourcep->getCurrentBuffer()); + if (max_sourcep->isSyncSlave()) { // A sync slave, it doesn't start playing until it's synced up with the master. @@ -529,6 +581,8 @@ void LLAudioEngine::enableWind(bool enable) LLAudioBuffer * LLAudioEngine::getFreeBuffer() { + //checkStates(); //Fails + S32 i; for (i = 0; i < MAX_BUFFERS; i++) { @@ -539,6 +593,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() } } + //checkStates(); // Fails // Grab the oldest unused buffer F32 max_age = -1.f; @@ -558,6 +613,8 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() } } + //checkStates(); //Fails + if (buffer_id >= 0) { LL_DEBUGS("AudioEngine") << "Taking over unused buffer! max_age=" << max_age << llendl; @@ -568,12 +625,15 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() if(channelp && channelp->mCurrentBufferp == mBuffers[buffer_id]) { channelp->cleanup(); + llassert(channelp->mCurrentBufferp == NULL); } } delete mBuffers[buffer_id]; mBuffers[buffer_id] = createBuffer(); return mBuffers[buffer_id]; } + + //checkStates(); //Fails return NULL; } @@ -1422,6 +1482,8 @@ bool LLAudioSource::setupChannel() } mChannelp->setSource(this); + llassert(this == mChannelp->getSource()); + llassert(mChannelp->mCurrentBufferp == getCurrentBuffer()); return true; } @@ -1433,6 +1495,7 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) { if (getChannel()) { + llassert(this == getChannel()->getSource()); getChannel()->setSource(NULL); if (!isMuted()) { @@ -1685,6 +1748,8 @@ LLAudioChannel::LLAudioChannel() : LLAudioChannel::~LLAudioChannel() { + llassert(mCurrentBufferp == NULL); + // Need to disconnect any sources which are using this channel. //llinfos << "Cleaning up audio channel" << llendl; cleanup(); @@ -1763,6 +1828,8 @@ bool LLAudioChannel::updateBuffer() mCurrentSourcep = source; mCurrentSourcep->setChannel(this); + llassert(mCurrentBufferp == NULL); + mCurrentBufferp = bufferp; if (bufferp) { diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index d58758709..c739e37bf 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -189,6 +189,8 @@ public: static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status); friend class LLPipeline; // For debugging + + void checkStates(); public: F32 mMaxWindGain; // Hack. Public to set before fade in? diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index adcdf2b7f..9bb6ab58f 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -55,6 +55,8 @@ #include #pragma comment(lib, "delayimp.lib") +static bool sVerboseDebugging = false; + bool attemptDelayLoad() { __try @@ -74,8 +76,161 @@ FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; -LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler) +//This class is designed to keep track of all sound<->channel assocations. +//Used to verify validity of sound and channel pointers, as well as catch cases were sounds +//are released with active channels still attached. +class CFMODSoundChecks { + typedef std::map > active_sounds_t; + typedef std::set dead_sounds_t; + typedef std::map active_channels_t; + typedef std::map dead_channels_t; + + active_sounds_t mActiveSounds; + dead_sounds_t mDeadSounds; + active_channels_t mActiveChannels; + dead_channels_t mDeadChannels; +public: + enum STATUS + { + ACTIVE, + DEAD, + UNKNOWN + }; + STATUS getPtrStatus(LLAudioChannelFMODEX* channel) + { + if(!channel) + return UNKNOWN; + return getPtrStatus(channel->mChannelp); + } + STATUS getPtrStatus(LLAudioBufferFMODEX* sound) + { + if(!sound) + return UNKNOWN; + return getPtrStatus(sound->mSoundp); + } + STATUS getPtrStatus(FMOD::Channel* channel) + { + if(!channel) + return UNKNOWN; + else if(mActiveChannels.find(channel) != mActiveChannels.end()) + return ACTIVE; + else if(mDeadChannels.find(channel) != mDeadChannels.end()) + return DEAD; + return UNKNOWN; + } + STATUS getPtrStatus(FMOD::Sound* sound) + { + if(!sound) + return UNKNOWN; + if(mActiveSounds.find(sound) != mActiveSounds.end()) + return ACTIVE; + else if(mDeadSounds.find(sound) != mDeadSounds.end()) + return DEAD; + return UNKNOWN; + } + void addNewSound(FMOD::Sound* sound) + { + STATUS status = getPtrStatus(sound); + llassert(status != ACTIVE); + + mDeadSounds.erase(sound); + mActiveSounds.insert(std::make_pair(sound,std::set())); + } + void removeSound(FMOD::Sound* sound) + { + STATUS status = getPtrStatus(sound); + llassert(status != DEAD); + llassert(status != UNKNOWN); + + active_sounds_t::const_iterator it = mActiveSounds.find(sound); + llassert(it != mActiveSounds.end()); + if(it != mActiveSounds.end()) + { + if(!it->second.empty()) + { +#ifdef LL_RELEASE_FOR_DOWNLOAD + LL_WARNS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; +#else + LL_ERRS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; +#endif + for(std::set::iterator it2 = it->second.begin(); it2 != it->second.end();++it2) + { + switch(getPtrStatus(*it2)) + { + case ACTIVE: + llcont << " Channel " << *it2 << " ACTIVE\n"; + break; + case DEAD: + llcont << " Channel " << *it2 << " DEAD\n"; + break; + default: + llcont << " Channel " << *it2 << " UNKNOWN\n"; + } + } + llcont << llendl; + } + mDeadSounds.insert(sound); + mActiveSounds.erase(it); + } + } + void addNewChannelToSound(FMOD::Sound* sound,FMOD::Channel* channel) + { + STATUS status = getPtrStatus(sound); + llassert(status != DEAD); + llassert(status != UNKNOWN); + status = getPtrStatus(channel); + llassert(status != ACTIVE); + + mActiveSounds[sound].insert(channel); + mActiveChannels.insert(std::make_pair(channel,sound)); + } + void removeChannel(FMOD::Channel* channel) + { + STATUS status = getPtrStatus(channel); + llassert(status != DEAD); + llassert(status != UNKNOWN); + + active_channels_t::const_iterator it = mActiveChannels.find(channel); + llassert(it != mActiveChannels.end()); + if(it != mActiveChannels.end()) + { + STATUS status = getPtrStatus(it->second); + llassert(status != DEAD); + llassert(status != UNKNOWN); + + active_sounds_t::iterator it2 = mActiveSounds.find(it->second); + llassert(it2 != mActiveSounds.end()); + if(it2 != mActiveSounds.end()) + { + it2->second.erase(channel); + } + mDeadChannels.insert(*it); + mActiveChannels.erase(it); + } + } +} gSoundCheck; + +bool isActive(LLAudioChannel* channel) +{ + return gSoundCheck.getPtrStatus(dynamic_cast(channel)) == CFMODSoundChecks::ACTIVE; +} +bool isActive(LLAudioBuffer* sound) +{ + return gSoundCheck.getPtrStatus(dynamic_cast(sound)) == CFMODSoundChecks::ACTIVE; +} + +#define CHECK_PTR(ptr) \ + if(sVerboseDebugging){\ + CFMODSoundChecks::STATUS chan = gSoundCheck.getPtrStatus(ptr); \ + if(chan == CFMODSoundChecks::DEAD) \ + LL_DEBUGS("AudioImpl") << __FUNCTION__ << ": Using dead " << typeid(ptr).name() << " " << ptr << llendl; \ + else if(chan == CFMODSoundChecks::UNKNOWN) \ + LL_DEBUGS("AudioImpl") << __FUNCTION__ << ": Using unknown " << typeid(ptr).name() << " " << ptr << llendl;} \ + +LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler, bool verbose_debugging) +{ + sVerboseDebugging = verbose_debugging; mInited = false; mWindGen = NULL; mWindDSP = NULL; @@ -490,6 +645,9 @@ LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChanne LLAudioChannelFMODEX::~LLAudioChannelFMODEX() { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Destructing Audio Channel. mChannelp = " << mChannelp << llendl; + cleanup(); } @@ -510,6 +668,10 @@ static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNEL *channel, FMOD_CHANN void LLAudioChannelFMODEX::onRelease() { + llassert(mChannelp); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Fmod signaled channel release for channel " << mChannelp << llendl; + gSoundCheck.removeChannel(mChannelp); mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. cleanup(); } @@ -523,6 +685,9 @@ bool LLAudioChannelFMODEX::updateBuffer() LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer(); + llassert(mCurrentBufferp != NULL); + llassert(mCurrentBufferp == bufferp); + // Grab the FMOD sample associated with the buffer FMOD::Sound *soundp = bufferp->getSound(); if (!soundp) @@ -538,13 +703,19 @@ bool LLAudioChannelFMODEX::updateBuffer() // setup. if(!mChannelp) { + llassert(!isActive(this)); + FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp); Check_FMOD_Error(result, "FMOD::System::playSound"); if(result == FMOD_OK) { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Created channel " << mChannelp << " for sound " << soundp << llendl; + gSoundCheck.addNewChannelToSound(soundp,mChannelp); mChannelp->setCallback(&channel_callback); mChannelp->setUserData(this); + llassert(isActive(this)); } } } @@ -555,6 +726,7 @@ bool LLAudioChannelFMODEX::updateBuffer() // SJB: warnings can spam and hurt framerate, disabling FMOD_RESULT result; + CHECK_PTR(mChannelp); result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain()); Check_FMOD_Error(result, "FMOD::Channel::setVolume"); @@ -582,6 +754,8 @@ void LLAudioChannelFMODEX::update3DPosition() return; } + CHECK_PTR(mChannelp); + if (mCurrentSourcep->isAmbient()) { // Ambient sound, don't need to do any positional updates. @@ -608,6 +782,8 @@ void LLAudioChannelFMODEX::updateLoop() return; } + CHECK_PTR(mChannelp); + // // Hack: We keep track of whether we looped or not by seeing when the // sample position looks like it's going backwards. Not reliable; may @@ -630,14 +806,26 @@ void LLAudioChannelFMODEX::cleanup() if (!mChannelp) { + llassert(!isActive(this)); + llassert(mCurrentBufferp == NULL); //llinfos << "Aborting cleanup with no channel handle." << llendl; return; } + CHECK_PTR(mChannelp); + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Stopping channel " << mChannelp << llendl; + mChannelp->setCallback(NULL); - Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); + if(!Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop")) + { + gSoundCheck.removeChannel(mChannelp); + } mChannelp = NULL; + llassert(!isActive(this)); + } @@ -649,8 +837,15 @@ void LLAudioChannelFMODEX::play() return; } + llassert(isActive(this)); + + CHECK_PTR(mChannelp); + Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused"); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Playing channel " << mChannelp << llendl; + getSource()->setPlayedOnce(true); if(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]) @@ -667,6 +862,8 @@ void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp) return; } + CHECK_PTR(mChannelp); + U32 cur_pos; if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position")) return; @@ -688,6 +885,8 @@ bool LLAudioChannelFMODEX::isPlaying() return false; } + CHECK_PTR(mChannelp); + bool paused, playing; Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused"); Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying"); @@ -709,7 +908,12 @@ LLAudioBufferFMODEX::~LLAudioBufferFMODEX() { if(mSoundp) { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Release sound " << mSoundp << llendl; + + CHECK_PTR(mSoundp); Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::Release"); + gSoundCheck.removeSound(mSoundp); mSoundp = NULL; } } @@ -733,8 +937,10 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) if (mSoundp) { + CHECK_PTR(mSoundp); // If there's already something loaded in this buffer, clean it up. Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::release"); + gSoundCheck.removeSound(mSoundp); mSoundp = NULL; } @@ -764,6 +970,8 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) return false; } + gSoundCheck.addNewSound(mSoundp); + // Everything went well, return true return true; } @@ -776,6 +984,7 @@ U32 LLAudioBufferFMODEX::getLength() return 0; } + CHECK_PTR(mSoundp); U32 length; Check_FMOD_Error(mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES),"FMOD::Sound::getLength"); return length; @@ -784,6 +993,8 @@ U32 LLAudioBufferFMODEX::getLength() void LLAudioChannelFMODEX::set3DMode(bool use3d) { + CHECK_PTR(mChannelp); + FMOD_MODE current_mode; if(Check_FMOD_Error(mChannelp->getMode(¤t_mode),"FMOD::Channel::getMode")) return; diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h index 7f9a28b04..18b4d61a8 100644 --- a/indra/llaudio/llaudioengine_fmodex.h +++ b/indra/llaudio/llaudioengine_fmodex.h @@ -53,7 +53,7 @@ namespace FMOD class LLAudioEngine_FMODEX : public LLAudioEngine { public: - LLAudioEngine_FMODEX(bool enable_profiler); + LLAudioEngine_FMODEX(bool enable_profiler, bool verbose_debugging); virtual ~LLAudioEngine_FMODEX(); // initialization/startup/shutdown @@ -112,6 +112,8 @@ protected: FMOD::System *mSystemp; FMOD::Channel *mChannelp; S32 mLastSamplePos; + + friend class CFMODSoundChecks; }; @@ -129,6 +131,8 @@ protected: FMOD::System *mSystemp; FMOD::Sound *getSound() const{ return mSoundp; } FMOD::Sound *mSoundp; + + friend class CFMODSoundChecks; }; diff --git a/indra/newview/app_settings/settings_sh.xml b/indra/newview/app_settings/settings_sh.xml index d0f7516a2..732da64bf 100644 --- a/indra/newview/app_settings/settings_sh.xml +++ b/indra/newview/app_settings/settings_sh.xml @@ -41,6 +41,17 @@ Value 0 + SHEnableFMODEXVerboseDebugging + + Comment + Enable profiler tool if using FMOD Ex + Persist + 1 + Type + Boolean + Value + 0 + SHFMODExStreamBufferSize Comment diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 967e83dd7..c5bd32d21 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -683,7 +683,7 @@ bool idle_startup() #endif // !LL_WINDOWS ) { - gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX(gSavedSettings.getBOOL("SHEnableFMODExProfiler")); + gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX(gSavedSettings.getBOOL("SHEnableFMODExProfiler"),gSavedSettings.getBOOL("SHEnableFMODEXVerboseDebugging")); } #endif From 909fc0d9ce3ca67276fa4a434be7ced17b5b98f0 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 4 Jun 2013 17:08:59 -0500 Subject: [PATCH 07/36] Blah blah fix non-windows builds. --- indra/llaudio/llaudioengine_fmodex.cpp | 8 +++---- indra/newview/llfloaterobjectiminfo.cpp | 24 ------------------- indra/newview/llfloaterobjectiminfo.h | 31 +++++++++++++++++++------ 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 9bb6ab58f..4b5cc3015 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -55,8 +55,6 @@ #include #pragma comment(lib, "delayimp.lib") -static bool sVerboseDebugging = false; - bool attemptDelayLoad() { __try @@ -72,6 +70,8 @@ bool attemptDelayLoad() } #endif +static bool sVerboseDebugging = false; + FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; @@ -171,7 +171,7 @@ public: llcont << llendl; } mDeadSounds.insert(sound); - mActiveSounds.erase(it); + mActiveSounds.erase(sound); } } void addNewChannelToSound(FMOD::Sound* sound,FMOD::Channel* channel) @@ -206,7 +206,7 @@ public: it2->second.erase(channel); } mDeadChannels.insert(*it); - mActiveChannels.erase(it); + mActiveChannels.erase(channel); } } } gSoundCheck; diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index f2b2049a4..101f45bf5 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -56,30 +56,6 @@ //////////////////////////////////////////////////////////////////////////// // LLFloaterObjectIMInfo -class LLFloaterObjectIMInfo : public LLFloater, public LLFloaterSingleton -{ -public: - LLFloaterObjectIMInfo(const LLSD& sd); - virtual ~LLFloaterObjectIMInfo() { }; - - BOOL postBuild(void); - - void update(LLSD& payload); - - // UI Handlers - static void onClickMap(void* data); - static void onClickOwner(void* data); - static void onClickMute(void* data); - - void nameCallback(const LLUUID& id, const std::string& full_name, bool is_group); - -private: - LLUUID mObjectID; - LLUUID mOwnerID; - std::string mSLurl; - std::string mName; - bool mGroupOwned; -}; LLFloaterObjectIMInfo::LLFloaterObjectIMInfo(const LLSD& seed) : mObjectID(), mName(), mSLurl(), mOwnerID(), mGroupOwned(false) diff --git a/indra/newview/llfloaterobjectiminfo.h b/indra/newview/llfloaterobjectiminfo.h index 55f8bba6f..58377c009 100644 --- a/indra/newview/llfloaterobjectiminfo.h +++ b/indra/newview/llfloaterobjectiminfo.h @@ -33,14 +33,31 @@ #ifndef LL_LLFLOATEROBJECTIMINFO_H #define LL_LLFLOATEROBJECTIMINFO_H -namespace LLObjectIMInfo +#include "llfloater.h" + +class LLFloaterObjectIMInfo : public LLFloater, public LLFloaterSingleton { - // Show an LLFloaterObjectIMInfo for this object. - static void show(const LLUUID& object_id, - const std::string& name, - const std::string& location, - const LLUUID& owner_id, - bool owner_is_group); +public: + LLFloaterObjectIMInfo(const LLSD& sd); + virtual ~LLFloaterObjectIMInfo() { }; + + /*virtual*/ BOOL postBuild(void); + + void update(LLSD& payload); + + // UI Handlers + static void onClickMap(void* data); + static void onClickOwner(void* data); + static void onClickMute(void* data); + + void nameCallback(const LLUUID& id, const std::string& full_name, bool is_group); + +private: + LLUUID mObjectID; + LLUUID mOwnerID; + std::string mSLurl; + std::string mName; + bool mGroupOwned; }; #endif // LL_LLFLOATERURLDISPLAY_H From 72c186f0f4ab272bd0aff4e206cc420f2a23f1a9 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 6 Jun 2013 02:12:45 -0500 Subject: [PATCH 08/36] Fixed some unused variable warnings/errors when not compiling with assertions enabled. (eg 'release' configuration). Also snagged a nullcheck from upstream. --- indra/llaudio/llaudioengine_fmodex.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 4b5cc3015..bfdda0957 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -131,17 +131,18 @@ public: } void addNewSound(FMOD::Sound* sound) { - STATUS status = getPtrStatus(sound); - llassert(status != ACTIVE); + llassert(getPtrStatus(sound) != ACTIVE); mDeadSounds.erase(sound); mActiveSounds.insert(std::make_pair(sound,std::set())); } void removeSound(FMOD::Sound* sound) { +#ifdef SHOW_ASSERT STATUS status = getPtrStatus(sound); llassert(status != DEAD); llassert(status != UNKNOWN); +#endif active_sounds_t::const_iterator it = mActiveSounds.find(sound); llassert(it != mActiveSounds.end()); @@ -187,17 +188,21 @@ public: } void removeChannel(FMOD::Channel* channel) { +#ifdef SHOW_ASSERT STATUS status = getPtrStatus(channel); llassert(status != DEAD); llassert(status != UNKNOWN); +#endif active_channels_t::const_iterator it = mActiveChannels.find(channel); llassert(it != mActiveChannels.end()); if(it != mActiveChannels.end()) { +#ifdef SHOW_ASSERT STATUS status = getPtrStatus(it->second); llassert(status != DEAD); llassert(status != UNKNOWN); +#endif active_sounds_t::iterator it2 = mActiveSounds.find(it->second); llassert(it2 != mActiveSounds.end()); @@ -512,8 +517,11 @@ void LLAudioEngine_FMODEX::shutdown() LLAudioEngine::shutdown(); LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + if ( mSystem ) // speculative fix for MAINT-2657 + { mSystem->close(); mSystem->release(); + } LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; delete mListenerp; From d947912bb0d39ed9d6f8710f09190a5dfbcc9cd0 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 11 Jun 2013 15:41:53 -0400 Subject: [PATCH 09/36] [Voice Update] Start/End call functions Adds llvoicecallhandler.cpp Enables the Group and Avatar Actions requiring the voice refactor. Adds autoStartCallOnStartup, processAgentListUpdates, startCall, and endCall to LLIMMgr. Gives llimpanel some LLIMModel functionality to see if a floater was opened with the intent of being a call. Cleans up LLIMPanel with new functions from the voice refactor. --- indra/newview/CMakeLists.txt | 1 + indra/newview/llavataractions.cpp | 6 +- indra/newview/llgroupactions.cpp | 2 - indra/newview/llimpanel.cpp | 76 +++------- indra/newview/llimpanel.h | 13 +- indra/newview/llimview.cpp | 198 +++++++++++++++++---------- indra/newview/llimview.h | 20 ++- indra/newview/llvoicecallhandler.cpp | 69 ++++++++++ 8 files changed, 238 insertions(+), 147 deletions(-) create mode 100644 indra/newview/llvoicecallhandler.cpp diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 5d40fc1fd..5d16b11dc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -521,6 +521,7 @@ set(viewer_SOURCE_FILES llvoclouds.cpp llvograss.cpp llvoground.cpp + llvoicecallhandler.cpp llvoicechannel.cpp llvoiceclient.cpp llvoiceremotectrl.cpp diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index bdb57a10f..9be913ae8 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -204,11 +204,10 @@ void LLAvatarActions::endIM(const LLUUID& id) } } -/* Singu TODO: Voice refactor static void on_avatar_name_cache_start_call(const LLUUID& agent_id, const LLAvatarName& av_name) { - LLUUID session_id = gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id, true); + LLUUID session_id = gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id); if (session_id.notNull()) { gIMMgr->startCall(session_id); @@ -270,7 +269,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true); + ids[0], id_array); if (session_id.isNull()) { return; @@ -280,7 +279,6 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) make_ui_sound("UISndStartIM"); } -*/ /* AD *TODO: Is this function needed any more? I fixed it a bit(added check for canCall), but it appears that it is not used diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index d5d9ecad4..ade7e1d1f 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -128,7 +128,6 @@ void LLGroupActions::search() LLFloaterDirectory::showGroups(); } -/* Singu TODO: Voice refactor // static void LLGroupActions::startCall(const LLUUID& group_id) { @@ -162,7 +161,6 @@ void LLGroupActions::startCall(const LLUUID& group_id) make_ui_sound("UISndStartIM"); } -*/ // static void LLGroupActions::join(const LLUUID& group_id) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index edd499610..b1479ffe9 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -315,7 +315,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mSentTypingState(TRUE), mNumUnreadMessages(0), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -352,7 +352,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mTypingLineStartIndex(0), mSentTypingState(TRUE), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -574,8 +574,8 @@ BOOL LLFloaterIMPanel::postBuild() if (LLUICtrl* ctrl = findChild("rp_mode")) ctrl->setCommitCallback(boost::bind(&LLFloaterIMPanel::onRPMode, this, _2)); - childSetAction("start_call_btn", onClickStartCall, this); - childSetAction("end_call_btn", onClickEndCall, this); + getChild("start_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::startCall, gIMMgr, mOtherParticipantUUID, LLVoiceChannel::OUTGOING_CALL)); + getChild("end_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::endCall, gIMMgr, mOtherParticipantUUID)); getChild("send_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onSendMsg,this)); if (LLButton* btn = findChild("toggle_active_speakers_btn")) btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickToggleActiveSpeakers, this, _2)); @@ -600,8 +600,8 @@ BOOL LLFloaterIMPanel::postBuild() if (mDialog == IM_NOTHING_SPECIAL) { - childSetAction("mute_btn", onClickMuteVoice, this); - childSetCommitCallback("speaker_volume", onVolumeChange, this); + getChild("mute_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickMuteVoice, this)); + getChild("speaker_volume")->setCommitCallback(boost::bind(&LLVoiceClient::setUserVolume, LLVoiceClient::getInstance(), mOtherParticipantUUID, _2)); } setDefaultBtn("send_btn"); @@ -625,33 +625,16 @@ void* LLFloaterIMPanel::createSpeakersPanel(void* data) return floaterp->mSpeakerPanel; } -//static -void LLFloaterIMPanel::onClickMuteVoice(void* user_data) +void LLFloaterIMPanel::onClickMuteVoice() { - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + LLMute mute(mOtherParticipantUUID, getTitle(), LLMute::AGENT); + if (!LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)) { - BOOL is_muted = LLMuteList::getInstance()->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat); - - LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT); - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); - } - else - { - LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); - } + LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); } -} - -//static -void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) -{ - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + else { - LLVoiceClient::getInstance()->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); + LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); } } @@ -670,7 +653,7 @@ void LLFloaterIMPanel::draw() mEndCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); mStartCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); mStartCallBtn->setEnabled(enable_connect); - mSendBtn->setEnabled(!childGetValue("chat_editor").asString().empty()); + mSendBtn->setEnabled(!mInputEditor->getValue().asString().empty()); LLPointer self_speaker = mSpeakers->findSpeaker(gAgent.getID()); if(!mTextIMPossible) @@ -689,12 +672,6 @@ void LLFloaterIMPanel::draw() mInputEditor->setLabel(getString("default_text_label")); } - if (mAutoConnect && enable_connect) - { - onClickStartCall(this); - mAutoConnect = FALSE; - } - // show speakers window when voice first connects if (mShowSpeakersOnConnect && mVoiceChannel->isActive()) { @@ -1111,14 +1088,6 @@ void LLFloaterIMPanel::onClickStartCall(void* userdata) self->mVoiceChannel->activate(); } -// static -void LLFloaterIMPanel::onClickEndCall(void* userdata) -{ - LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; - - self->getVoiceChannel()->deactivate(); -} - void LLFloaterIMPanel::onClickToggleActiveSpeakers(const LLSD& value) { childSetVisible("active_speakers_panel", !value); @@ -1454,11 +1423,6 @@ LL_WARNS("Splitting") << "Pos: " << pos << " next_split: " << next_split << LL_E mSentTypingState = TRUE; } -void LLFloaterIMPanel::updateSpeakersList(const LLSD& speaker_updates) -{ - mSpeakers->updateSpeakers(speaker_updates); -} - void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) { if ( @@ -1482,11 +1446,6 @@ void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) } } -void LLFloaterIMPanel::setSpeakers(const LLSD& speaker_list) -{ - mSpeakers->setSpeakers(speaker_list); -} - void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) { mSessionUUID = session_id; @@ -1512,11 +1471,12 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) mOtherParticipantUUID, mDialog); } -} -void LLFloaterIMPanel::requestAutoConnect() -{ - mAutoConnect = TRUE; + // auto-start the call on session initialization? + if (mStartCallOnInitialize) + { + gIMMgr->startCall(mSessionUUID); + } } void LLFloaterIMPanel::setTyping(BOOL typing) diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 7d4d1fe71..0f659a820 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -121,20 +121,15 @@ public: static void* createSpeakersPanel(void* data); //callbacks for P2P muting and volume control - static void onClickMuteVoice(void* user_data); - static void onVolumeChange(LLUICtrl* source, void* user_data); + void onClickMuteVoice(); const LLUUID& getSessionID() const { return mSessionUUID; } const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; } - void updateSpeakersList(const LLSD& speaker_updates); void processSessionUpdate(const LLSD& update); - void setSpeakers(const LLSD& speaker_list); LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } LLIMSpeakerMgr* getSpeakerManager() const { return mSpeakers; } // Singu TODO: LLIMModel::getSpeakerManager EInstantMessage getDialogType() const { return mDialog; } - void requestAutoConnect(); - void sessionInitReplyReceived(const LLUUID& im_session_id); // Handle other participant in the session typing. @@ -161,6 +156,10 @@ public: bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} SType mSessionType; + // LLIMModel Functionality + bool getSessionInitialized() const { return mSessionInitialized; } + bool mStartCallOnInitialize; + private: // called by constructors void init(const std::string& session_label); @@ -236,8 +235,6 @@ private: BOOL mShowSpeakersOnConnect; - BOOL mAutoConnect; - BOOL mTextIMPossible; BOOL mProfileButtonEnabled; BOOL mCallBackEnabled; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 06ac47b5b..c025bfbf6 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -53,6 +53,7 @@ #include "llfloaterchatterbox.h" #include "llhttpnode.h" #include "llimpanel.h" +#include "llnotificationsutil.h" #include "llsdserialize.h" #include "llspeakers.h" #include "lltabcontainer.h" @@ -60,7 +61,6 @@ #include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerwindow.h" -#include "llvoicechannel.h" #include "llnotify.h" #include "llviewerregion.h" @@ -115,6 +115,7 @@ LLColor4 agent_chat_color(const LLUUID& id, const std::string& name, bool local_ //{ // return (LLStringUtil::compareDict( a->mName, b->mName ) < 0); //} + class LLViewerChatterBoxInvitationAcceptResponder : public LLHTTPClient::ResponderWithResult { public: @@ -130,10 +131,9 @@ public: { if ( gIMMgr) { - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if (floaterp) + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(mSessionID); + LLIMSpeakerMgr* speaker_mgr = floater ? floater->getSpeakerManager() : NULL; + if (speaker_mgr) { //we've accepted our invitation //and received a list of agents that were @@ -147,26 +147,26 @@ public: //but unfortunately, our base that we are receiving here //may not be the most up to date. It was accurate at //some point in time though. - floaterp->setSpeakers(content); + speaker_mgr->setSpeakers(content); //we now have our base of users in the session //that was accurate at some point, but maybe not now //so now we apply all of the udpates we've received //in case of race conditions - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(mSessionID)); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID)); + } - if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE ) - { - floaterp->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(floaterp); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(TRUE); - } - else if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE ) - { - LLFloaterChatterBox::showInstance(TRUE); - } + if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) + { + gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL); + } + + if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE + || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) + && gIMMgr->hasSession(mSessionID)) + { + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(TRUE); } gIMMgr->clearPendingAgentListUpdates(mSessionID); @@ -176,6 +176,8 @@ public: /*virtual*/ void error(U32 statusNum, const std::string& reason) { + llwarns << "LLViewerChatterBoxInvitationAcceptResponder error [status:" + << statusNum << "]: " << reason << llendl; //throw something back to the viewer here? if ( gIMMgr ) { @@ -286,11 +288,14 @@ protected: bool inviteUserResponse(const LLSD& notification, const LLSD& response) { + if (!gIMMgr) + return false; + const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"].asUUID(); EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch(option) { case 0: // accept @@ -304,16 +309,10 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) payload["session_handle"].asString(), payload["session_uri"].asString()); - LLFloaterIMPanel* im_floater = - gIMMgr->findFloaterBySession( - session_id); - if (im_floater) - { - im_floater->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(im_floater); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(session_id); - } + gIMMgr->startCall(session_id); + + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(session_id); gIMMgr->clearPendingAgentListUpdates(session_id); gIMMgr->clearPendingInvitation(session_id); @@ -355,11 +354,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) { if (type == IM_SESSION_P2P_INVITE) { - if(LLVoiceClient::instanceExists()) - { - std::string s = payload["session_handle"].asString(); - LLVoiceClient::getInstance()->declineInvite(s); - } + std::string s = payload["session_handle"].asString(); + LLVoiceClient::getInstance()->declineInvite(s); } else { @@ -688,6 +684,22 @@ BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) return FALSE; } +void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if (!floater) return; + + if (floater->getSessionInitialized()) + { + startCall(session_id); + } + else + { + floater->mStartCallOnInitialize = true; + } +} + LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const std::string& voice_session_handle, @@ -1025,6 +1037,31 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) } } +void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) +{ + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if (!im_floater) return; + LLIMSpeakerMgr* speaker_mgr = im_floater->getSpeakerManager(); + if (speaker_mgr) + { + speaker_mgr->updateSpeakers(body); + + // also the same call is added into LLVoiceClient::participantUpdatedEvent because + // sometimes it is called AFTER LLViewerChatterBoxSessionAgentListUpdates::post() + // when moderation state changed too late. See EXT-3544. + speaker_mgr->update(true); + } + else + { + //we don't have a speaker manager yet..something went wrong + //we are probably receiving an update here before + //a start or an acceptance of an invitation. Race condition. + gIMMgr->addPendingAgentListUpdates( + session_id, + body); + } +} + LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id) { if ( mPendingAgentListUpdates.has(session_id.asString()) ) @@ -1105,6 +1142,39 @@ void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id) } } +bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->setCallDirection(direction); + voice_channel->activate(); + return true; +} + +bool LLIMMgr::endCall(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->deactivate(); + /*LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); + if (im_session)*/ + { + // need to update speakers' state + floater->getSpeakerManager()->update(FALSE); + } + return true; +} + // create a floater and update internal representation for // consistency. Returns the pointer, caller (the class instance since // it is a private method) is not responsible for deleting the @@ -1418,35 +1488,30 @@ public: if ( success ) { session_id = body["session_id"].asUUID(); - gIMMgr->updateFloaterSessionID( - temp_session_id, - session_id); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + // Singu TODO: LLIMModel + gIMMgr->updateFloaterSessionID(temp_session_id, session_id); + + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + LLIMSpeakerMgr* speaker_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; + if (speaker_mgr) { - floaterp->setSpeakers(body); - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); + speaker_mgr->setSpeakers(body); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); + } + if (im_floater) + { if ( body.has("session_info") ) { - floaterp->processSessionUpdate(body["session_info"]); + im_floater->processSessionUpdate(body["session_info"]); } - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); } + gIMMgr->clearPendingAgentListUpdates(session_id); } else { - //throw an error dialog and close the temp session's floater - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) + if (LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id)) { floater->showSessionStartError(body["error"].asString()); } @@ -1526,21 +1591,8 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); - if (floaterp) - { - floaterp->updateSpeakersList( - input["body"]); - } - else - { - //we don't have a floater yet..something went wrong - //we are probably receiving an update here before - //a start or an acceptance of an invitation. Race condition. - gIMMgr->addPendingAgentListUpdates( - input["body"]["session_id"].asUUID(), - input["body"]); - } + const LLUUID& session_id = input["body"]["session_id"].asUUID(); + gIMMgr->processAgentListUpdates(session_id, input["body"]); } }; @@ -1553,12 +1605,12 @@ public: const LLSD& input) const { LLUUID session_id = input["body"]["session_id"].asUUID(); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if ( im_floater ) { - floaterp->processSessionUpdate(input["body"]["info"]); + im_floater->processSessionUpdate(input["body"]["info"]); } - LLIMSpeakerMgr* im_mgr = floaterp ? floaterp->getSpeakerManager() : NULL; //LLIMModel::getInstance()->getSpeakerManager(session_id); + LLIMSpeakerMgr* im_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; //LLIMModel::getInstance()->getSpeakerManager(session_id); if (im_mgr) { im_mgr->processSessionUpdate(input["body"]["info"]); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 4db4b75f9..3e7bc6fff 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -36,12 +36,12 @@ #include "llmultifloater.h" #include "llinstantmessage.h" #include "lluuid.h" +#include "llvoicechannel.h" + class LLFloaterChatterBox; -class LLUUID; class LLFloaterIMPanel; class LLFriendObserver; -class LLFloaterIM; class LLIMMgr : public LLSingleton { @@ -131,6 +131,9 @@ public: void notifyNewIM(); void clearNewIMNotification(); + // automatically start a call once the session has initialized + void autoStartCallOnStartup(const LLUUID& session_id); + // IM received that you haven't seen yet BOOL getIMReceived() const; int getIMUnreadCount(); @@ -163,6 +166,7 @@ public: void clearPendingInvitation(const LLUUID& session_id); + void processAgentListUpdates(const LLUUID& session_id, const LLSD& body); LLSD getPendingAgentListUpdates(const LLUUID& session_id); void addPendingAgentListUpdates( const LLUUID& sessioN_id, @@ -178,6 +182,18 @@ public: // Returns true if group chat is ignored for the UUID, false if not bool getIgnoreGroup(const LLUUID& group_id) const; + /** + * Start call in a session + * @return false if voice channel doesn't exist + **/ + bool startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction = LLVoiceChannel::OUTGOING_CALL); + + /** + * End call in a session + * @return false if voice channel doesn't exist + **/ + bool endCall(const LLUUID& session_id); + private: // create a panel and update internal representation for // consistency. Returns the pointer, caller (the class instance diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp new file mode 100644 index 000000000..0f68f7972 --- /dev/null +++ b/indra/newview/llvoicecallhandler.cpp @@ -0,0 +1,69 @@ + /** + * @file llvoicecallhandler.cpp + * @brief slapp to handle avatar to avatar voice call. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcommandhandler.h" +#include "llavataractions.h" +//#include "llnotificationsutil.h" +//#include "llui.h" + +class LLVoiceCallAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLVoiceCallAvatarHandler() : LLCommandHandler("voicecallavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + /*if (!LLUI::sSettingGroups["config"]->getBOOL("EnableVoiceCall")) + { + LLNotificationsUtil::add("NoVoiceCall", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + }*/ + + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + //instigate call with this avatar + LLAvatarActions::startCall( id ); + return true; + } +}; + +LLVoiceCallAvatarHandler gVoiceCallAvatarHandler; + From 7e9c75ef6b006609722983c464902adaf16a2e71 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 11 Jun 2013 15:43:27 -0400 Subject: [PATCH 10/36] Use the translatable string Saved_message everywhere needed, instead of just one place. --- indra/newview/llimview.cpp | 4 +++- indra/newview/llviewermessage.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index c025bfbf6..837b76ff8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1703,7 +1703,9 @@ public: std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } std::string buffer = separator_string + saved + message.substr(message_offset); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 4f3e2e147..c929c94f2 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2846,7 +2846,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } buffer = separator_string + saved + message.substr(message_offset); gIMMgr->addMessage( From b7136b2b4f6610bcb263788b99e1f0df82f5cb5a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 11 Jun 2013 16:59:32 -0400 Subject: [PATCH 11/36] [Voice Update] startCall and endCall use Session UUID, not Other Participant UUID. --- indra/newview/llimpanel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index b1479ffe9..88d8467df 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -574,8 +574,8 @@ BOOL LLFloaterIMPanel::postBuild() if (LLUICtrl* ctrl = findChild("rp_mode")) ctrl->setCommitCallback(boost::bind(&LLFloaterIMPanel::onRPMode, this, _2)); - getChild("start_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::startCall, gIMMgr, mOtherParticipantUUID, LLVoiceChannel::OUTGOING_CALL)); - getChild("end_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::endCall, gIMMgr, mOtherParticipantUUID)); + getChild("start_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::startCall, gIMMgr, mSessionUUID, LLVoiceChannel::OUTGOING_CALL)); + getChild("end_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::endCall, gIMMgr, mSessionUUID)); getChild("send_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onSendMsg,this)); if (LLButton* btn = findChild("toggle_active_speakers_btn")) btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickToggleActiveSpeakers, this, _2)); From 08ab202a062c2cb465811392d7d4c1791b8dc9e9 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 11 Jun 2013 19:03:15 -0400 Subject: [PATCH 12/36] Fix avatar "inspect" commandhandler --- indra/newview/llpanelprofile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index ad3747811..aa2c5dbd2 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -102,13 +102,13 @@ public: return true; } - /* Singu TODO if (verb == "inspect") { - LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); + LLAvatarActions::showProfile(avatar_id); + //Singu TODO: inspect? + //LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); return true; } - */ if (verb == "im") { From 01ec6905ab283923808e887a1422cea3dca64618 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 13 Jun 2013 05:23:35 -0400 Subject: [PATCH 13/36] [Voice Update] LLFloaterVoiceEffect (Lives in File->Show Voice Morpher for now) --- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 16 + indra/newview/llfloatervoiceeffect.cpp | 318 ++++++++++++++++++ indra/newview/llfloatervoiceeffect.h | 74 ++++ indra/newview/llviewermenu.cpp | 2 + .../xui/en-us/floater_voice_effect.xml | 180 ++++++++++ .../skins/default/xui/en-us/menu_viewer.xml | 4 + .../default/xui/es/floater_voice_effect.xml | 138 ++++++++ .../default/xui/fr/floater_voice_effect.xml | 159 +++++++++ .../default/xui/pt/floater_voice_effect.xml | 159 +++++++++ 10 files changed, 1052 insertions(+) create mode 100644 indra/newview/llfloatervoiceeffect.cpp create mode 100644 indra/newview/llfloatervoiceeffect.h create mode 100644 indra/newview/skins/default/xui/en-us/floater_voice_effect.xml create mode 100644 indra/newview/skins/default/xui/es/floater_voice_effect.xml create mode 100644 indra/newview/skins/default/xui/fr/floater_voice_effect.xml create mode 100644 indra/newview/skins/default/xui/pt/floater_voice_effect.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 5d16b11dc..d876b66f6 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -260,6 +260,7 @@ set(viewer_SOURCE_FILES llfloatertos.cpp llfloaterurldisplay.cpp llfloaterurlentry.cpp + llfloatervoiceeffect.cpp llfloaterwater.cpp llfloaterwebcontent.cpp llfloaterwindlight.cpp @@ -765,6 +766,7 @@ set(viewer_HEADER_FILES llfloatertos.h llfloaterurldisplay.h llfloaterurlentry.h + llfloatervoiceeffect.h llfloaterwater.h llfloaterwebcontent.h llfloaterwindlight.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 309252bef..44dea9cd1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7427,6 +7427,22 @@ This should be as low as possible, but too low may break functionality Value -1 + FloaterVoiceEffectRect + + Comment + Rectangle for voice morpher floater + Persist + 1 + Type + Rect + Value + + 0 + 300 + 360 + 0 + + FloaterWorldMapRect2 Comment diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp new file mode 100644 index 000000000..415798086 --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -0,0 +1,318 @@ +/** + * @file llfloatervoiceeffect.cpp + * @author Aimee + * @brief Selection and preview of voice effect. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoiceeffect.h" + +#include "llscrolllistctrl.h" +#include "lltexteditor.h" // For linked text hack +#include "lltrans.h" +#include "lluictrlfactory.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)); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_voice_effect.xml"); +} + +// 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("record_btn")->setFocus(true); + + // Singu Note: Here we must hack together a linked piece of text + if (LLTextEditor* editor = getChild("voice_morphing_link")) + { + editor->setParseHTML(true); // For some reason, adding style doesn't work unless this is true. + const std::string text = editor->getValue(); + editor->clear(); + LLStyleSP link(new LLStyle); + link->setLinkHREF(LLTrans::getString("voice_morphing_url")); + link->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + editor->appendStyledText(text, false, false, link); + } + + mVoiceEffectList = getChild("voice_effect_list"); + if (mVoiceEffectList) + { + mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); +// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + } + + return TRUE; +} + +// virtual +void LLFloaterVoiceEffect::onOpen() +{ + 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(); +} + +// virtual +void LLFloaterVoiceEffect::onClose(bool app_quitting) +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->enablePreviewBuffer(false); + } + setVisible(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 items = mVoiceEffectList->getAllSelected(); + for(std::vector::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"] = "SANSSERIF"; + element["columns"][NAME_COLUMN]["font-style"] = "BOLD"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *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 localized_effect = "effect_" + it->first; + std::string effect_name = hasString(localized_effect) ? getString(localized_effect) : it->first; // XML contains localized effects names + + 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"] = "SANSSERIF"; + 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"] = "SANSSERIF"; + element["columns"][DATE_COLUMN]["font-style"] = "NORMAL"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *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; + LLScrollListText* slt = dynamic_cast(sl_item->getColumn(0)); + llassert(slt); + if (slt) + { + slt->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("record_btn")->setVisible(!recording); + getChild("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() +{ + LL_DEBUGS("Voice") << "Activate clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface && mVoiceEffectList) + { + //effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); // Singu Note: This would return early, since we're disconnected from voice, just set the string and refresh list + gSavedPerAccountSettings.setString("VoiceEffectDefault", mVoiceEffectList->getCurrentID().asString()); + refreshEffectList(); + } +} + diff --git a/indra/newview/llfloatervoiceeffect.h b/indra/newview/llfloatervoiceeffect.h new file mode 100644 index 000000000..9bee8937c --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.h @@ -0,0 +1,74 @@ +/** + * @file llfloatervoiceeffect.h + * @author Aimee + * @brief Selection and preview of voice effects. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEEFFECT_H +#define LL_LLFLOATERVOICEEFFECT_H + +#include "llfloater.h" +#include "llvoiceclient.h" + +class LLButton; +class LLScrollListCtrl; + +class LLFloaterVoiceEffect + : public LLFloater + , public LLVoiceEffectObserver + , public LLFloaterSingleton +{ +public: + LOG_CLASS(LLFloaterVoiceEffect); + + LLFloaterVoiceEffect(const LLSD& key); + virtual ~LLFloaterVoiceEffect(); + + virtual BOOL postBuild(); + virtual void onOpen(); + 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/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2f6c73c0e..bdd452de3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -109,6 +109,7 @@ #include "llfloaterteleporthistory.h" #include "llfloatertest.h" #include "llfloatertools.h" +#include "llfloatervoiceeffect.h" #include "llfloaterwater.h" #include "llfloaterwebcontent.h" #include "llfloaterwindlight.h" @@ -6383,6 +6384,7 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("script info"); registerFloater ("stat bar"); registerFloater ("teleport history"); + registerFloater ("voice effect"); registerFloater ("pathfinding_characters"); registerFloater ("pathfinding_linksets"); diff --git a/indra/newview/skins/default/xui/en-us/floater_voice_effect.xml b/indra/newview/skins/default/xui/en-us/floater_voice_effect.xml new file mode 100644 index 000000000..d625ffcc8 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_voice_effect.xml @@ -0,0 +1,180 @@ + + + + (No Voice Morph) + + + (Active) + + + (Unsubscribed) + + + (New!) + + + + Arena + Beast + Buff + Buzz + Camille + Creepy + CreepyBot + Cyber + DeepBot + Demon + Female Elf + Flirty + Foxy + Halloween_2010_Bonus + Helium + Husky + Husky Whisper + Intercom + Julia + Lo Lilt + Macho + Micro + Mini + Model + Nano + Nightmare + PopBot + Rachel + Radio + Robot + Roxanne + Rumble + Sabrina + Samantha + Sexy + Shorty + Smaller + Sneaky + Stallion + Sultry + Thunder + Vixen + WhinyBot + + + To Preview + + +Record a sample, then click on a voice to hear how it will sound. + + + + + + Subscribe Now + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/indra/newview/skins/default/xui/en-us/menu_viewer.xml index 43fd1f857..f32b74a43 100644 --- a/indra/newview/skins/default/xui/en-us/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en-us/menu_viewer.xml @@ -45,6 +45,10 @@ mouse_opaque="true" name="perm prefs" > + + + + diff --git a/indra/newview/skins/default/xui/es/floater_voice_effect.xml b/indra/newview/skins/default/xui/es/floater_voice_effect.xml new file mode 100644 index 000000000..54b1b5f80 --- /dev/null +++ b/indra/newview/skins/default/xui/es/floater_voice_effect.xml @@ -0,0 +1,138 @@ + + + + (Sin transformación de voz) + + + (Activo) + + + (Suscripción cancelada) + + + (¡Nuevo!) + + + Campo + + + Bestia + + + Musculoso + + + Murmullo + + + Camila + + + Aterrador + + + Robot aterrador + + + Cyber + + + Robot profundo + + + Diablo + + + Coqueta + + + Astuto + + + Halloween_2010_Bonus + + + Helio + + + Corpulento + + + Intercom + + + Macho + + + Micro + + + Mini + + + Nano + + + Pesadilla + + + Robot pop + + + Raquel + + + Radio + + + Robot + + + Roxana + + + Sabrina + + + Samanta + + + Sexy + + + Bajito + + + Furtivo + + + Mujeriego + + + Sensual + + + Trueno + + + Tigresa + + + Robot llorica + + + Para probarla + + + Graba una muestra y pulsa en una voz para escuchar cómo suena. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml index 38178813f..4c6b39881 100644 --- a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml @@ -2,19 +2,45 @@ Redock Windows - - - + name="radio zoom" radio_style="true" width="114" > + + + + mouse_opaque="true" name="slider zoom" width="134" > + + + name="radio orbit" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio pan" radio_style="true" width="114" > + + + name="radio move" radio_style="true" width="114" > + + + name="radio lift" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio spin" radio_style="true" width="114" > + + + name="radio position" radio_style="true" width="114" > + + + name="radio rotate" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio stretch" radio_style="true" width="123" > + + + name="radio select face" radio_style="true" width="114" > + + + name="radio align" radio_style="true" width="114" > + + + name="checkbox edit linked parts" width="114"> + + Reference + @@ -249,29 +314,53 @@ + name="radio select land" radio_style="true" width="114" > + + + name="radio flatten" radio_style="true" width="114" > + + + name="radio raise" radio_style="true" width="114" > + + + name="radio lower" radio_style="true" width="114" > + + + name="radio smooth" radio_style="true" width="114" > + + + name="radio noise" radio_style="true" width="114" > + + + name="radio revert" radio_style="true" width="114" > + + Date: Sat, 15 Jun 2013 05:14:27 -0500 Subject: [PATCH 20/36] Added media support to prim face editor. Todo: Translations for all these floaters and panels. --- indra/newview/CMakeLists.txt | 10 + indra/newview/llfloatermediasettings.cpp | 319 +++++++ indra/newview/llfloatermediasettings.h | 89 ++ indra/newview/llfloatertools.cpp | 874 +++++++++++++++++- indra/newview/llfloatertools.h | 20 +- indra/newview/llfloaterwhitelistentry.cpp | 91 ++ indra/newview/llfloaterwhitelistentry.h | 50 + indra/newview/llmediactrl.cpp | 2 +- indra/newview/llpanelcontents.cpp | 7 + indra/newview/llpanelcontents.h | 11 + indra/newview/llpanelface.cpp | 211 ++--- indra/newview/llpanelface.h | 15 +- indra/newview/llpanelmediasettingsgeneral.cpp | 515 +++++++++++ indra/newview/llpanelmediasettingsgeneral.h | 100 ++ .../llpanelmediasettingspermissions.cpp | 285 ++++++ .../newview/llpanelmediasettingspermissions.h | 73 ++ .../newview/llpanelmediasettingssecurity.cpp | 366 ++++++++ indra/newview/llpanelmediasettingssecurity.h | 82 ++ indra/newview/llselectmgr.h | 47 + indra/newview/skins/default/textures/Flag.png | Bin 0 -> 3238 bytes .../skins/default/textures/textures.xml | 2 + .../xui/en-us/floater_media_settings.xml | 73 ++ .../skins/default/xui/en-us/floater_tools.xml | 123 ++- .../xui/en-us/floater_whitelist_entry.xml | 29 + .../skins/default/xui/en-us/notifications.xml | 46 + .../en-us/panel_media_settings_general.xml | 226 +++++ .../panel_media_settings_permissions.xml | 172 ++++ .../en-us/panel_media_settings_security.xml | 91 ++ .../xui/en-us/panel_prim_media_controls.xml | 9 +- .../skins/default/xui/en-us/strings.xml | 4 + 30 files changed, 3794 insertions(+), 148 deletions(-) create mode 100644 indra/newview/llfloatermediasettings.cpp create mode 100644 indra/newview/llfloatermediasettings.h create mode 100644 indra/newview/llfloaterwhitelistentry.cpp create mode 100644 indra/newview/llfloaterwhitelistentry.h create mode 100644 indra/newview/llpanelmediasettingsgeneral.cpp create mode 100644 indra/newview/llpanelmediasettingsgeneral.h create mode 100644 indra/newview/llpanelmediasettingspermissions.cpp create mode 100644 indra/newview/llpanelmediasettingspermissions.h create mode 100644 indra/newview/llpanelmediasettingssecurity.cpp create mode 100644 indra/newview/llpanelmediasettingssecurity.h create mode 100644 indra/newview/skins/default/textures/Flag.png create mode 100644 indra/newview/skins/default/xui/en-us/floater_media_settings.xml create mode 100644 indra/newview/skins/default/xui/en-us/floater_whitelist_entry.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_media_settings_general.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 682c43939..878141ad0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -219,6 +219,7 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlandmark.cpp llfloatermap.cpp + llfloatermediasettings.cpp llfloatermemleak.cpp llfloatermessagelog.cpp llfloatermodelpreview.cpp @@ -260,6 +261,7 @@ set(viewer_SOURCE_FILES llfloatervoicedevicesettings.cpp llfloaterwater.cpp llfloaterwebcontent.cpp + llfloaterwhitelistentry.cpp llfloaterwindlight.cpp llfloaterworldmap.cpp llfolderview.cpp @@ -365,6 +367,9 @@ set(viewer_SOURCE_FILES llpanellogin.cpp llpanelmaininventory.cpp llpanelmarketplaceoutboxinventory.cpp + llpanelmediasettingsgeneral.cpp + llpanelmediasettingspermissions.cpp + llpanelmediasettingssecurity.cpp llpanelmorph.cpp llpanelmsgs.cpp llpanelnetwork.cpp @@ -715,6 +720,7 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlandmark.h llfloatermap.h + llfloatermediasettings.h llfloatermemleak.h llfloatermessagelog.h llfloatermodelpreview.h @@ -756,6 +762,7 @@ set(viewer_HEADER_FILES llfloatervoicedevicesettings.h llfloaterwater.h llfloaterwebcontent.h + llfloaterwhitelistentry.h llfloaterwindlight.h llfloaterworldmap.h llfolderview.h @@ -861,6 +868,9 @@ set(viewer_HEADER_FILES llpanellogin.h llpanelmaininventory.h llpanelmarketplaceoutboxinventory.h + llpanelmediasettingsgeneral.h + llpanelmediasettingspermissions.h + llpanelmediasettingssecurity.h llpanelmorph.h llpanelmsgs.h llpanelnetwork.h diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp new file mode 100644 index 000000000..151ed53b6 --- /dev/null +++ b/indra/newview/llfloatermediasettings.cpp @@ -0,0 +1,319 @@ +/** + * @file llfloatermediasettings.cpp + * @brief Tabbed dialog for media settings - class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermediasettings.h" +#include "llfloaterwhitelistentry.h" +#include "llpanelmediasettingsgeneral.h" +#include "llpanelmediasettingssecurity.h" +#include "llpanelmediasettingspermissions.h" +#include "llviewercontrol.h" +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llselectmgr.h" +#include "llsdutil.h" + +LLFloaterMediaSettings* LLFloaterMediaSettings::sInstance = NULL; + +//////////////////////////////////////////////////////////////////////////////// +// +LLFloaterMediaSettings::LLFloaterMediaSettings(const LLSD& key) + : LLFloater(key), + mTabContainer(NULL), + mPanelMediaSettingsGeneral(NULL), + mPanelMediaSettingsSecurity(NULL), + mPanelMediaSettingsPermissions(NULL), + mWaitingToClose( false ), + mIdenticalHasMediaInfo( true ), + mMultipleMedia(false), + mMultipleValidMedia(false) +{ + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_media_settings.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFloaterMediaSettings::~LLFloaterMediaSettings() +{ + if ( mPanelMediaSettingsGeneral ) + { + delete mPanelMediaSettingsGeneral; + mPanelMediaSettingsGeneral = NULL; + } + + if ( mPanelMediaSettingsSecurity ) + { + delete mPanelMediaSettingsSecurity; + mPanelMediaSettingsSecurity = NULL; + } + + if ( mPanelMediaSettingsPermissions ) + { + delete mPanelMediaSettingsPermissions; + mPanelMediaSettingsPermissions = NULL; + } + + sInstance = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLFloaterMediaSettings::postBuild() +{ + mApplyBtn = getChild("Apply"); + mApplyBtn->setClickedCallback(onBtnApply, this); + + mCancelBtn = getChild("Cancel"); + mCancelBtn->setClickedCallback(onBtnCancel, this); + + mOKBtn = getChild("OK"); + mOKBtn->setClickedCallback(onBtnOK, this); + + mTabContainer = getChild( "tab_container" ); + + mPanelMediaSettingsGeneral = new LLPanelMediaSettingsGeneral(); + mTabContainer->addTabPanel( mPanelMediaSettingsGeneral, mPanelMediaSettingsGeneral->getLabel() ); + mPanelMediaSettingsGeneral->setParent( this ); + + // note that "permissions" tab is really "Controls" tab - refs to 'perms' and + // 'permissions' not changed to 'controls' since we don't want to change + // shared files in server code and keeping everything the same seemed best. + mPanelMediaSettingsPermissions = new LLPanelMediaSettingsPermissions(); + mTabContainer->addTabPanel( mPanelMediaSettingsPermissions, mPanelMediaSettingsPermissions->getLabel() ); + + mPanelMediaSettingsSecurity = new LLPanelMediaSettingsSecurity(); + mTabContainer->addTabPanel( mPanelMediaSettingsSecurity, mPanelMediaSettingsSecurity->getLabel() ); + mPanelMediaSettingsSecurity->setParent( this ); + + // restore the last tab viewed from persistance variable storage + if (!mTabContainer->selectTab(gSavedSettings.getS32("LastMediaSettingsTab"))) + { + mTabContainer->selectFirstTab(); + }; + + sInstance = this; + + return TRUE; +} + +//static +LLFloaterMediaSettings* LLFloaterMediaSettings::getInstance() +{ + if ( !sInstance ) + { + sInstance = new LLFloaterMediaSettings(LLSD()); + sInstance->setVisible(FALSE); + } + + return sInstance; +} + +//static +void LLFloaterMediaSettings::apply() +{ + if (sInstance->haveValuesChanged()) + { + LLSD settings; + sInstance->mPanelMediaSettingsGeneral->preApply(); + sInstance->mPanelMediaSettingsGeneral->getValues( settings, false ); + sInstance->mPanelMediaSettingsSecurity->preApply(); + sInstance->mPanelMediaSettingsSecurity->getValues( settings, false ); + sInstance->mPanelMediaSettingsPermissions->preApply(); + sInstance->mPanelMediaSettingsPermissions->getValues( settings, false ); + + LLSelectMgr::getInstance()->selectionSetMedia( LLTextureEntry::MF_HAS_MEDIA, settings ); + + sInstance->mPanelMediaSettingsGeneral->postApply(); + sInstance->mPanelMediaSettingsSecurity->postApply(); + sInstance->mPanelMediaSettingsPermissions->postApply(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void LLFloaterMediaSettings::onClose(bool app_quitting) +{ + if(mPanelMediaSettingsGeneral) + { + mPanelMediaSettingsGeneral->onClose(app_quitting); + } + if(LLFloaterWhiteListEntry::instanceExists()) + LLFloaterWhiteListEntry::getInstance()->close(app_quitting); + + LLFloater::onClose(app_quitting); +} + +//////////////////////////////////////////////////////////////////////////////// +//static +void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable ) +{ + if (sInstance->hasFocus()) return; + + sInstance->clearValues(editable); + // update all panels with values from simulator + sInstance->mPanelMediaSettingsGeneral-> + initValues( sInstance->mPanelMediaSettingsGeneral, media_settings, editable ); + + sInstance->mPanelMediaSettingsSecurity-> + initValues( sInstance->mPanelMediaSettingsSecurity, media_settings, editable ); + + sInstance->mPanelMediaSettingsPermissions-> + initValues( sInstance->mPanelMediaSettingsPermissions, media_settings, editable ); + + // Squirrel away initial values + sInstance->mInitialValues.clear(); + sInstance->mPanelMediaSettingsGeneral->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsSecurity->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsPermissions->getValues( sInstance->mInitialValues ); + + sInstance->mApplyBtn->setEnabled(editable); + sInstance->mOKBtn->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterMediaSettings::commitFields() +{ + if (hasFocus()) + { + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (cur_focus && cur_focus->acceptsTextInput()) + { + cur_focus->onCommit(); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +//static +void LLFloaterMediaSettings::clearValues( bool editable) +{ + if (sInstance) + { + // clean up all panels before updating + sInstance->mPanelMediaSettingsGeneral ->clearValues(sInstance->mPanelMediaSettingsGeneral, editable); + sInstance->mPanelMediaSettingsSecurity ->clearValues(sInstance->mPanelMediaSettingsSecurity, editable); + sInstance->mPanelMediaSettingsPermissions->clearValues(sInstance->mPanelMediaSettingsPermissions, editable); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnOK( void* userdata ) +{ + sInstance->commitFields(); + + sInstance->apply(); + + sInstance->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnApply( void* userdata ) +{ + sInstance->commitFields(); + + sInstance->apply(); + + sInstance->mInitialValues.clear(); + sInstance->mPanelMediaSettingsGeneral->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsSecurity->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsPermissions->getValues( sInstance->mInitialValues ); + +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnCancel( void* userdata ) +{ + sInstance->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onTabChanged(void* user_data, bool from_click) +{ + LLTabContainer* self = (LLTabContainer*)user_data; + gSavedSettings.setS32("LastMediaSettingsTab", self->getCurrentPanelIndex()); +} +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLFloaterMediaSettings::getHomeUrl() +{ + if ( mPanelMediaSettingsGeneral ) + return mPanelMediaSettingsGeneral->getHomeUrl(); + else + return std::string( "" ); +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void LLFloaterMediaSettings::draw() +{ + if (NULL != mApplyBtn) + { + // Set the enabled state of the "Apply" button if values changed + mApplyBtn->setEnabled( haveValuesChanged() ); + } + + LLFloater::draw(); +} + + +//private +bool LLFloaterMediaSettings::haveValuesChanged() const +{ + bool values_changed = false; + // *NOTE: The code below is very inefficient. Better to do this + // only when data change. + // Every frame, check to see what the values are. If they are not + // the same as the initial media data, enable the OK/Apply buttons + LLSD settings; + sInstance->mPanelMediaSettingsGeneral->getValues( settings ); + sInstance->mPanelMediaSettingsSecurity->getValues( settings ); + sInstance->mPanelMediaSettingsPermissions->getValues( settings ); + LLSD::map_const_iterator iter = settings.beginMap(); + LLSD::map_const_iterator end = settings.endMap(); + for ( ; iter != end; ++iter ) + { + const std::string ¤t_key = iter->first; + const LLSD ¤t_value = iter->second; + if ( ! llsd_equals(current_value, mInitialValues[current_key])) + { + values_changed = true; + break; + } + } + return values_changed; +} + +bool LLFloaterMediaSettings::instanceExists() +{ + return sInstance; +} + + diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h new file mode 100644 index 000000000..1d2553098 --- /dev/null +++ b/indra/newview/llfloatermediasettings.h @@ -0,0 +1,89 @@ +/** + * @file llfloatermediasettings.cpp + * @brief Tabbed dialog for media settings - class definition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERMEDIASETTINGS_H +#define LL_LLFLOATERMEDIASETTINGS_H + +#include "llfloater.h" +#include "lltabcontainer.h" + +class LLPanelMediaSettingsGeneral; +class LLPanelMediaSettingsSecurity; +class LLPanelMediaSettingsPermissions; + +class LLFloaterMediaSettings : + public LLFloater +{ +public: + LLFloaterMediaSettings(const LLSD& key); + ~LLFloaterMediaSettings(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onClose(bool app_quitting); + + static LLFloaterMediaSettings* getInstance(); + static bool instanceExists(); + static void apply(); + static void initValues( const LLSD& media_settings , bool editable); + static void clearValues( bool editable); + + LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;}; + const std::string getHomeUrl(); + //bool passesWhiteList( const std::string& test_url ); + + virtual void draw(); + + bool mIdenticalHasMediaInfo; + bool mMultipleMedia; + bool mMultipleValidMedia; + +protected: + LLButton *mOKBtn; + LLButton *mCancelBtn; + LLButton *mApplyBtn; + + LLTabContainer *mTabContainer; + LLPanelMediaSettingsGeneral* mPanelMediaSettingsGeneral; + LLPanelMediaSettingsSecurity* mPanelMediaSettingsSecurity; + LLPanelMediaSettingsPermissions* mPanelMediaSettingsPermissions; + + static void onBtnOK(void*); + static void onBtnCancel(void*); + static void onBtnApply(void*); + static void onTabChanged(void* user_data, bool from_click); + void commitFields(); + + static LLFloaterMediaSettings* sInstance; + +private: + + bool haveValuesChanged() const; + + LLSD mInitialValues; + bool mWaitingToClose; +}; + +#endif // LL_LLFLOATERMEDIASETTINGS_H diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 5b7d59c28..d63fde073 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -45,9 +45,13 @@ #include "llcombobox.h" #include "lldraghandle.h" #include "llfloaterbuildoptions.h" +#include "llfloatermediasettings.h" #include "llfloateropenobject.h" #include "llfocusmgr.h" +#include "llmediaentry.h" +#include "llmediactrl.h" #include "llmenugl.h" +#include "llnotificationsutil.h" #include "llpanelcontents.h" #include "llpanelface.h" #include "llpanelland.h" @@ -55,6 +59,7 @@ #include "llpanelobject.h" #include "llpanelvolume.h" #include "llpanelpermissions.h" +#include "llparcel.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llslider.h" @@ -74,12 +79,15 @@ #include "lltoolpipette.h" #include "lltoolplacer.h" #include "lltoolselectland.h" +#include "lltrans.h" #include "llui.h" #include "llviewercontrol.h" #include "llviewerjoystick.h" +#include "llviewerregion.h" #include "llviewermenu.h" #include "llviewerparcelmgr.h" #include "llviewerwindow.h" +#include "llvovolume.h" #include "lluictrlfactory.h" #include "llmeshrepository.h" @@ -241,6 +249,7 @@ BOOL LLFloaterTools::postBuild() mRadioStretch = getChild("radio stretch"); mRadioSelectFace = getChild("radio select face"); mRadioAlign = getChild("radio align"); + mTitleMedia = getChild("title_media"); mCheckSelectIndividual = getChild("checkbox edit linked parts"); getChild("checkbox edit linked parts")->setValue((BOOL)gSavedSettings.getBOOL("EditLinkedParts")); @@ -331,14 +340,17 @@ LLFloaterTools::LLFloaterTools() mBtnLand(NULL), mTextStatus(NULL), + //Camera Focus mRadioOrbit(NULL), mRadioZoom(NULL), mRadioPan(NULL), + //Move via physics mRadioMove(NULL), mRadioLift(NULL), mRadioSpin(NULL), + //Edit prim mRadioPosition(NULL), mRadioRotate(NULL), mRadioStretch(NULL), @@ -349,6 +361,7 @@ LLFloaterTools::LLFloaterTools() mCheckSnapToGrid(NULL), mBtnGridOptions(NULL), mTextGridMode(NULL), + mTitleMedia(NULL), mComboGridMode(NULL), mCheckStretchUniform(NULL), mCheckStretchTexture(NULL), @@ -369,6 +382,8 @@ LLFloaterTools::LLFloaterTools() mCheckCopySelection(NULL), mCheckCopyCenters(NULL), mCheckCopyRotates(NULL), + + //Edit land mRadioSelectLand(NULL), mRadioDozerFlatten(NULL), mRadioDozerRaise(NULL), @@ -376,6 +391,7 @@ LLFloaterTools::LLFloaterTools() mRadioDozerSmooth(NULL), mRadioDozerNoise(NULL), mRadioDozerRevert(NULL), + mSliderDozerSize(NULL), mSliderDozerForce(NULL), mBtnApplyToSelection(NULL), @@ -388,7 +404,8 @@ LLFloaterTools::LLFloaterTools() mPanelFace(NULL), mPanelLandInfo(NULL), - mDirty(TRUE) + mDirty(TRUE), + mNeedMediaTitle(TRUE) { setAutoFocus(FALSE); LLCallbackMap::map_t factory_map; @@ -413,6 +430,10 @@ LLFloaterTools::LLFloaterTools() mCommitCallbackRegistrar.add("BuildTool.applyToSelection", boost::bind(&click_apply_to_selection, this)); mCommitCallbackRegistrar.add("BuildTool.commitRadioLand", boost::bind(&commit_radio_group_land,_1)); mCommitCallbackRegistrar.add("BuildTool.LandBrushForce", boost::bind(&commit_slider_dozer_force,_1)); + mCommitCallbackRegistrar.add("BuildTool.AddMedia", boost::bind(&LLFloaterTools::onClickBtnAddMedia,this)); + mCommitCallbackRegistrar.add("BuildTool.DeleteMedia", boost::bind(&LLFloaterTools::onClickBtnDeleteMedia,this)); + mCommitCallbackRegistrar.add("BuildTool.EditMedia", boost::bind(&LLFloaterTools::onClickBtnEditMedia,this)); + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_tools.xml",&factory_map,FALSE); } @@ -521,6 +542,8 @@ void LLFloaterTools::refresh() mPanelObject->refresh(); mPanelVolume->refresh(); mPanelFace->refresh(); + if(mTitleMedia) + refreshMedia(); mPanelContents->refresh(); mPanelLandInfo->refresh(); } @@ -533,6 +556,10 @@ void LLFloaterTools::draw() mDirty = FALSE; } + // grab media name/title and update the UI widget + if(mTitleMedia) + updateMediaTitle(); + // mCheckSelectIndividual->set(gSavedSettings.getBOOL("EditLinkedParts")); LLFloater::draw(); } @@ -737,8 +764,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) if (mCheckCopyCenters) mCheckCopyCenters ->setVisible( create_visible ); if (mCheckCopyRotates) mCheckCopyRotates ->setVisible( create_visible ); - if (mCheckCopyCenters) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() ); - if (mCheckCopyRotates) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() ); + if (mCheckCopyCenters && mCheckCopySelection) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() ); + if (mCheckCopyRotates && mCheckCopySelection) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() ); // Land buttons BOOL land_visible = (tool == LLToolBrushLand::getInstance() || tool == LLToolSelectLand::getInstance() ); @@ -833,6 +860,10 @@ void LLFloaterTools::onClose(bool app_quitting) LLViewerJoystick::getInstance()->moveAvatar(false); + // destroy media source used to grab media title + if( mTitleMedia ) + mTitleMedia->unloadMediaSource(); + // Different from handle_reset_view in that it doesn't actually // move the camera if EditCameraMovement is not set. gAgentCamera.resetView(gSavedSettings.getBOOL("EditCameraMovement")); @@ -867,6 +898,9 @@ void LLFloaterTools::onClose(bool app_quitting) // gMenuBarView->setItemVisible(std::string("Tools"), FALSE); // gMenuBarView->arrange(); + + if(LLFloaterMediaSettings::instanceExists()) + LLFloaterMediaSettings::getInstance()->close(); } void LLFloaterTools::showPanel(EInfoPanel panel) @@ -1047,8 +1081,7 @@ void commit_grid_mode(LLUICtrl *ctrl) LLSelectMgr::getInstance()->setGridMode((EGridMode)combo->getCurrentIndex()); } -// static -void LLFloaterTools::onClickGridOptions(void* data) +void LLFloaterTools::onClickGridOptions() { //LLFloaterTools* floaterp = (LLFloaterTools*)data; LLFloaterBuildOptions::show(NULL); @@ -1086,6 +1119,836 @@ void LLFloaterTools::onFocusReceived() LLFloater::onFocusReceived(); } +// Media stuff +void LLFloaterTools::refreshMedia() +{ + if(!mTitleMedia) + return; + getMediaState(); +} + +bool LLFloaterTools::selectedMediaEditable() +{ + U32 owner_mask_on; + U32 owner_mask_off; + U32 valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_OWNER, + &owner_mask_on, &owner_mask_off ); + U32 group_mask_on; + U32 group_mask_off; + U32 valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_GROUP, + &group_mask_on, &group_mask_off ); + U32 everyone_mask_on; + U32 everyone_mask_off; + S32 valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_EVERYONE, + &everyone_mask_on, &everyone_mask_off ); + + bool selected_Media_editable = false; + + // if perms we got back are valid + if ( valid_owner_perms && + valid_group_perms && + valid_everyone_perms ) + { + + if ( ( owner_mask_on & PERM_MODIFY ) || + ( group_mask_on & PERM_MODIFY ) || + ( group_mask_on & PERM_MODIFY ) ) + { + selected_Media_editable = true; + } + else + // user is NOT allowed to press the RESET button + { + selected_Media_editable = false; + }; + }; + + return selected_Media_editable; +} +void LLFloaterTools::getMediaState() +{ + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* first_object = selected_objects->getFirstObject(); + LLTextBox* media_info = getChild("media_info"); + + if( !(first_object + && first_object->getPCode() == LL_PCODE_VOLUME + &&first_object->permModify() + )) + { + getChildView("Add_Media")->setEnabled(FALSE); + media_info->setValue(""); + clearMediaSettings(); + return; + } + + std::string url = first_object->getRegion()->getCapability("ObjectMedia"); + bool has_media_capability = (!url.empty()); + + if(!has_media_capability) + { + getChildView("Add_Media")->setEnabled(FALSE); + LL_WARNS("LLFloaterTools: media") << "Media not enabled (no capability) in this region!" << LL_ENDL; + clearMediaSettings(); + return; + } + + BOOL is_nonpermanent_enforced = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode() + && LLSelectMgr::getInstance()->selectGetRootsNonPermanentEnforced()) + || LLSelectMgr::getInstance()->selectGetNonPermanentEnforced(); + bool editable = is_nonpermanent_enforced && (first_object->permModify() || selectedMediaEditable()); + + // Check modify permissions and whether any selected objects are in + // the process of being fetched. If they are, then we're not editable + if (editable) + { + LLObjectSelection::iterator iter = selected_objects->begin(); + LLObjectSelection::iterator end = selected_objects->end(); + for ( ; iter != end; ++iter) + { + LLSelectNode* node = *iter; + LLVOVolume* object = dynamic_cast(node->getObject()); + if (NULL != object) + { + if (!object->permModify()) + { + LL_INFOS("LLFloaterTools: media") + << "Selection not editable due to lack of modify permissions on object id " + << object->getID() << LL_ENDL; + + editable = false; + break; + } + // XXX DISABLE this for now, because when the fetch finally + // does come in, the state of this floater doesn't properly + // update. Re-selecting fixes the problem, but there is + // contention as to whether this is a sufficient solution. +// if (object->isMediaDataBeingFetched()) +// { +// LL_INFOS("LLFloaterTools: media") +// << "Selection not editable due to media data being fetched for object id " +// << object->getID() << LL_ENDL; +// +// editable = false; +// break; +// } + } + } + } + + // Media settings + bool bool_has_media = false; + struct media_functor : public LLSelectedTEGetFunctor + { + bool get(LLViewerObject* object, S32 face) + { + LLTextureEntry *te = object->getTE(face); + if (te) + { + return te->hasMedia(); + } + return false; + } + } func; + + + // check if all faces have media(or, all dont have media) + LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue( &func, bool_has_media ); + + const LLMediaEntry default_media_data; + + struct functor_getter_media_data : public LLSelectedTEGetFunctor< LLMediaEntry> + { + functor_getter_media_data(const LLMediaEntry& entry): mMediaEntry(entry) {} + + LLMediaEntry get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return *(object->getTE(face)->getMediaData()); + return mMediaEntry; + }; + + const LLMediaEntry& mMediaEntry; + + } func_media_data(default_media_data); + + LLMediaEntry media_data_get; + LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue( &func_media_data, media_data_get )); + + std::string multi_media_info_str = LLTrans::getString("Multiple Media"); + std::string media_title = ""; + mNeedMediaTitle = false; + // update UI depending on whether "object" (prim or face) has media + // and whether or not you are allowed to edit it. + + getChildView("Add_Media")->setEnabled(editable); + // IF all the faces have media (or all dont have media) + if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) + { + // TODO: get media title and set it. + media_info->setValue(""); + // if identical is set, all faces are same (whether all empty or has the same media) + if(!(LLFloaterMediaSettings::getInstance()->mMultipleMedia) ) + { + // Media data is valid + if(media_data_get!=default_media_data) + { + // initial media title is the media URL (until we get the name) + media_title = media_data_get.getHomeURL(); + + // kick off a navigate and flag that we need to update the title + navigateToTitleMedia( media_data_get.getHomeURL() ); + mNeedMediaTitle = true; + } + // else all faces might be empty. + } + else // there' re Different Medias' been set on on the faces. + { + media_title = multi_media_info_str; + mNeedMediaTitle = false; + } + + getChildView("media_tex")->setEnabled(bool_has_media && editable); + getChildView("edit_media")->setEnabled(bool_has_media && LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo && editable ); + getChildView("delete_media")->setEnabled(bool_has_media && editable ); + getChildView("add_media")->setEnabled(( ! bool_has_media ) && editable ); + // TODO: display a list of all media on the face - use 'identical' flag + } + else // not all face has media but at least one does. + { + // seleted faces have not identical value + LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data ); + + if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) + { + media_title = multi_media_info_str; + mNeedMediaTitle = false; + } + else + { + // Media data is valid + if(media_data_get!=default_media_data) + { + // initial media title is the media URL (until we get the name) + media_title = media_data_get.getHomeURL(); + + // kick off a navigate and flag that we need to update the title + navigateToTitleMedia( media_data_get.getHomeURL() ); + mNeedMediaTitle = true; + } + } + + getChildView("media_tex")->setEnabled(TRUE); + getChildView("edit_media")->setEnabled(LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo); + getChildView("delete_media")->setEnabled(TRUE); + getChildView("add_media")->setEnabled(FALSE ); + } + media_info->setText(media_title); + + // load values for media settings + updateMediaSettings(); + + if(mTitleMedia) + LLFloaterMediaSettings::initValues(mMediaSettings, editable ); +} +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to add media to a prim or prim face +void LLFloaterTools::onClickBtnAddMedia() +{ + // check if multiple faces are selected + if(LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + { + LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm); + } + else + { + onClickBtnEditMedia(); + } +} + +// static +bool LLFloaterTools::multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch( option ) + { + case 0: // "Yes" + gFloaterTools->onClickBtnEditMedia(); + break; + case 1: // "No" + default: + break; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to edit existing media settings on a prim or prim face +// TODO: test if there is media on the item and only allow editing if present +void LLFloaterTools::onClickBtnEditMedia() +{ + refreshMedia(); + LLFloaterMediaSettings::getInstance()->open(); + LLFloaterMediaSettings::getInstance()->setVisible(TRUE); + const LLRect& rect = getRect(); + U32 height_offset = rect.getHeight() - LLFloaterMediaSettings::getInstance()->getRect().getHeight(); + LLFloaterMediaSettings::getInstance()->setOrigin(rect.mRight, rect.mBottom + height_offset); +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to delete media from a prim or prim face +void LLFloaterTools::onClickBtnDeleteMedia() +{ + LLNotificationsUtil::add("DeleteMedia", LLSD(), LLSD(), deleteMediaConfirm); +} + + +// static +bool LLFloaterTools::deleteMediaConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch( option ) + { + case 0: // "Yes" + LLSelectMgr::getInstance()->selectionSetMedia( 0, LLSD() ); + if(LLFloaterMediaSettings::instanceExists()) + { + LLFloaterMediaSettings::getInstance()->close(); + } + break; + + case 1: // "No" + default: + break; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::clearMediaSettings() +{ + LLFloaterMediaSettings::clearValues(false); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::navigateToTitleMedia( const std::string url ) +{ + if ( mTitleMedia ) + { + LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); + if ( media_plugin ) + { + // if it's a movie, we don't want to hear it + media_plugin->setVolume( 0 ); + }; + mTitleMedia->navigateTo( url ); + }; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::updateMediaTitle() +{ + // only get the media name if we need it + if ( ! mNeedMediaTitle || !mTitleMedia ) + return; + + // get plugin impl + LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); + if ( media_plugin ) + { + // get the media name (asynchronous - must call repeatedly) + std::string media_title = media_plugin->getMediaName(); + + // only replace the title if what we get contains something + if ( ! media_title.empty() ) + { + // update the UI widget + LLTextBox* media_title_field = getChild("media_info"); + if ( media_title_field ) + { + media_title_field->setText( media_title ); + + // stop looking for a title when we get one + // FIXME: check this is the right approach + mNeedMediaTitle = false; + }; + }; + }; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::updateMediaSettings() +{ + bool identical( false ); + std::string base_key( "" ); + std::string value_str( "" ); + int value_int = 0; + bool value_bool = false; + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + // TODO: (CP) refactor this using something clever or boost or both !! + + const LLMediaEntry default_media_data; + + // controls + U8 value_u8 = default_media_data.getControls(); + struct functor_getter_controls : public LLSelectedTEGetFunctor< U8 > + { + functor_getter_controls(const LLMediaEntry &entry) : mMediaEntry(entry) {} + + U8 get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getControls(); + return mMediaEntry.getControls(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_controls(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_controls, value_u8 ); + base_key = std::string( LLMediaEntry::CONTROLS_KEY ); + mMediaSettings[ base_key ] = value_u8; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // First click (formerly left click) + value_bool = default_media_data.getFirstClickInteract(); + struct functor_getter_first_click : public LLSelectedTEGetFunctor< bool > + { + functor_getter_first_click(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getFirstClickInteract(); + return mMediaEntry.getFirstClickInteract(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_first_click(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_first_click, value_bool ); + base_key = std::string( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Home URL + value_str = default_media_data.getHomeURL(); + struct functor_getter_home_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_home_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getHomeURL(); + return mMediaEntry.getHomeURL(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_home_url(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_home_url, value_str ); + base_key = std::string( LLMediaEntry::HOME_URL_KEY ); + mMediaSettings[ base_key ] = value_str; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Current URL + value_str = default_media_data.getCurrentURL(); + struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getCurrentURL(); + return mMediaEntry.getCurrentURL(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_current_url(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_current_url, value_str ); + base_key = std::string( LLMediaEntry::CURRENT_URL_KEY ); + mMediaSettings[ base_key ] = value_str; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto zoom + value_bool = default_media_data.getAutoZoom(); + struct functor_getter_auto_zoom : public LLSelectedTEGetFunctor< bool > + { + + functor_getter_auto_zoom(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoZoom(); + return mMediaEntry.getAutoZoom(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_zoom(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_zoom, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_ZOOM_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto play + //value_bool = default_media_data.getAutoPlay(); + // set default to auto play TRUE -- angela EXT-5172 + value_bool = true; + struct functor_getter_auto_play : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_play(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoPlay(); + //return mMediaEntry.getAutoPlay(); set default to auto play TRUE -- angela EXT-5172 + return true; + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_play(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_play, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_PLAY_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + + // Auto scale + // set default to auto scale TRUE -- angela EXT-5172 + //value_bool = default_media_data.getAutoScale(); + value_bool = true; + struct functor_getter_auto_scale : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_scale(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoScale(); + // return mMediaEntry.getAutoScale(); set default to auto scale TRUE -- angela EXT-5172 + return true; + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_scale(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_scale, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_SCALE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto loop + value_bool = default_media_data.getAutoLoop(); + struct functor_getter_auto_loop : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_loop(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoLoop(); + return mMediaEntry.getAutoLoop(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_loop(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_loop, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_LOOP_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // width pixels (if not auto scaled) + value_int = default_media_data.getWidthPixels(); + struct functor_getter_width_pixels : public LLSelectedTEGetFunctor< int > + { + functor_getter_width_pixels(const LLMediaEntry& entry): mMediaEntry(entry) {} + + int get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWidthPixels(); + return mMediaEntry.getWidthPixels(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_width_pixels(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_width_pixels, value_int ); + base_key = std::string( LLMediaEntry::WIDTH_PIXELS_KEY ); + mMediaSettings[ base_key ] = value_int; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // height pixels (if not auto scaled) + value_int = default_media_data.getHeightPixels(); + struct functor_getter_height_pixels : public LLSelectedTEGetFunctor< int > + { + functor_getter_height_pixels(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + int get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getHeightPixels(); + return mMediaEntry.getHeightPixels(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_height_pixels(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_height_pixels, value_int ); + base_key = std::string( LLMediaEntry::HEIGHT_PIXELS_KEY ); + mMediaSettings[ base_key ] = value_int; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Enable Alt image + value_bool = default_media_data.getAltImageEnable(); + struct functor_getter_enable_alt_image : public LLSelectedTEGetFunctor< bool > + { + functor_getter_enable_alt_image(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAltImageEnable(); + return mMediaEntry.getAltImageEnable(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_enable_alt_image(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_enable_alt_image, value_bool ); + base_key = std::string( LLMediaEntry::ALT_IMAGE_ENABLE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - owner interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_OWNER ); + struct functor_getter_perms_owner_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_owner_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_OWNER)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_OWNER ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_owner_interact(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_owner_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_OWNER_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - owner control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_OWNER ); + struct functor_getter_perms_owner_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_owner_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_OWNER)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_OWNER ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_owner_control(default_media_data); + identical = selected_objects ->getSelectedTEValue( &func_perms_owner_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_OWNER_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - group interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_GROUP ); + struct functor_getter_perms_group_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_group_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_GROUP)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_GROUP ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_group_interact(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_group_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_GROUP_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - group control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_GROUP ); + struct functor_getter_perms_group_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_group_control(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_GROUP)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_GROUP ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_group_control(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_group_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_GROUP_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - anyone interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); + struct functor_getter_perms_anyone_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_anyone_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_ANYONE)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_anyone_interact(default_media_data); + identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_perms_anyone_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - anyone control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_ANYONE ); + struct functor_getter_perms_anyone_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_anyone_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_ANYONE)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_ANYONE ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_anyone_control(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_anyone_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // security - whitelist enable + value_bool = default_media_data.getWhiteListEnable(); + struct functor_getter_whitelist_enable : public LLSelectedTEGetFunctor< bool > + { + functor_getter_whitelist_enable(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWhiteListEnable(); + return mMediaEntry.getWhiteListEnable(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_whitelist_enable(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_whitelist_enable, value_bool ); + base_key = std::string( LLMediaEntry::WHITELIST_ENABLE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // security - whitelist URLs + std::vector value_vector_str = default_media_data.getWhiteList(); + struct functor_getter_whitelist_urls : public LLSelectedTEGetFunctor< std::vector > + { + functor_getter_whitelist_urls(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::vector get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWhiteList(); + return mMediaEntry.getWhiteList(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_whitelist_urls(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_whitelist_urls, value_vector_str ); + base_key = std::string( LLMediaEntry::WHITELIST_KEY ); + mMediaSettings[ base_key ].clear(); + std::vector< std::string >::iterator iter = value_vector_str.begin(); + while( iter != value_vector_str.end() ) + { + std::string white_list_url = *iter; + mMediaSettings[ base_key ].append( white_list_url ); + ++iter; + }; + + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; +} + // static void LLFloaterTools::onSelectTreesGrass(LLUICtrl*, void*) { @@ -1159,3 +2022,4 @@ void LLFloaterTools::updateTreeGrassCombo(bool visible) mComboTreesGrass->setVisible(visible); tree_grass_label->setVisible(visible); } + diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h index 84e04df69..fec736819 100644 --- a/indra/newview/llfloatertools.h +++ b/indra/newview/llfloatertools.h @@ -49,6 +49,7 @@ class LLPanelLandInfo; class LLSlider; class LLTabContainer; class LLTextBox; +class LLMediaCtrl; class LLTool; class LLParcelSelection; class LLObjectSelection; @@ -102,12 +103,23 @@ public: static void setEditTool(void* data); void setTool(const LLSD& user_data); void saveLastTool(); + void onClickBtnDeleteMedia(); + void onClickBtnAddMedia(); + void onClickBtnEditMedia(); + void clearMediaSettings(); + void updateMediaTitle(); + void navigateToTitleMedia( const std::string url ); + bool selectedMediaEditable(); private: void refresh(); - + void refreshMedia(); + void getMediaState(); + void updateMediaSettings(); + static bool deleteMediaConfirm(const LLSD& notification, const LLSD& response); + static bool multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response); static void setObjectType( LLPCode pcode ); - static void onClickGridOptions(void* data); + void onClickGridOptions(); public: LLButton *mBtnFocus; @@ -191,6 +203,8 @@ public: LLParcelSelectionHandle mParcelSelection; LLObjectSelectionHandle mObjectSelection; + LLMediaCtrl *mTitleMedia; + bool mNeedMediaTitle; private: BOOL mDirty; @@ -198,6 +212,8 @@ private: void updateTreeGrassCombo(bool visible); static void onSelectTreesGrass(LLUICtrl*, void*); +protected: + LLSD mMediaSettings; }; extern LLFloaterTools *gFloaterTools; diff --git a/indra/newview/llfloaterwhitelistentry.cpp b/indra/newview/llfloaterwhitelistentry.cpp new file mode 100644 index 000000000..f930e0940 --- /dev/null +++ b/indra/newview/llfloaterwhitelistentry.cpp @@ -0,0 +1,91 @@ +/** + * @file llfloaterwhitelistentry.cpp + * @brief LLFloaterWhistListEntry class implementation + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermediasettings.h" +#include "llfloaterwhitelistentry.h" +#include "llpanelmediasettingssecurity.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "lllineeditor.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +LLFloaterWhiteListEntry::LLFloaterWhiteListEntry() : + LLFloater() +{ + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_whitelist_entry.xml"); +} + +/////////////////////////////////////////////////////////////////////////////// +// +LLFloaterWhiteListEntry::~LLFloaterWhiteListEntry() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// +BOOL LLFloaterWhiteListEntry::postBuild() +{ + mWhiteListEdit = getChild("whitelist_entry"); + + childSetAction("cancel_btn", onBtnCancel, this); + childSetAction("ok_btn", onBtnOK, this); + + setDefaultBtn("ok_btn"); + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterWhiteListEntry::onBtnOK( void* userdata ) +{ + LLFloaterWhiteListEntry *self =(LLFloaterWhiteListEntry *)userdata; + + LLPanelMediaSettingsSecurity* panel = LLFloaterMediaSettings::instanceExists() ? LLFloaterMediaSettings::getInstance()->getPanelSecurity() : NULL; + if ( panel ) + { + std::string white_list_item = self->mWhiteListEdit->getText(); + + panel->addWhiteListEntry( white_list_item ); + panel->updateWhitelistEnableStatus(); + }; + + self->close(); +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterWhiteListEntry::onBtnCancel( void* userdata ) +{ + LLFloaterWhiteListEntry *self =(LLFloaterWhiteListEntry *)userdata; + + self->close(); +} diff --git a/indra/newview/llfloaterwhitelistentry.h b/indra/newview/llfloaterwhitelistentry.h new file mode 100644 index 000000000..da23b1ebf --- /dev/null +++ b/indra/newview/llfloaterwhitelistentry.h @@ -0,0 +1,50 @@ +/** + * @file llfloaterwhitelistentry.h + * @brief LLFloaterWhiteListEntry class definition + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERWHITELISTENTRY_H +#define LL_LLFLOATERWHITELISTENTRY_H + +#include "llfloater.h" + +class LLLineEditor; + +class LLFloaterWhiteListEntry : + public LLFloater, public LLSingleton +{ + public: + LLFloaterWhiteListEntry(); + ~LLFloaterWhiteListEntry(); + + BOOL postBuild(); + + private: + LLLineEditor* mWhiteListEdit; + + static void onBtnOK(void*); + static void onBtnCancel(void*); +}; + +#endif // LL_LLFLOATERWHITELISTENTRY_H diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index af7c6d131..eeaa6aacd 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -1161,7 +1161,7 @@ LLView* LLMediaCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory if(node->getAttributeBOOL("focus_on_click", bval)) p.focus_on_click = bval; if(node->getAttributeBOOL("decouple_texture_size", bval)) - p.focus_on_click = bval; + p.decouple_texture_size = bval; if(node->getAttributeBOOL("trusted_content", bval)) p.trusted_content = bval; if(node->getAttributeS32("texture_width", ival)) diff --git a/indra/newview/llpanelcontents.cpp b/indra/newview/llpanelcontents.cpp index 5eda11310..c44a855b7 100644 --- a/indra/newview/llpanelcontents.cpp +++ b/indra/newview/llpanelcontents.cpp @@ -82,6 +82,13 @@ // // Globals // +const char* LLPanelContents::TENTATIVE_SUFFIX = "_tentative"; +const char* LLPanelContents::PERMS_OWNER_INTERACT_KEY = "perms_owner_interact"; +const char* LLPanelContents::PERMS_OWNER_CONTROL_KEY = "perms_owner_control"; +const char* LLPanelContents::PERMS_GROUP_INTERACT_KEY = "perms_group_interact"; +const char* LLPanelContents::PERMS_GROUP_CONTROL_KEY = "perms_group_control"; +const char* LLPanelContents::PERMS_ANYONE_INTERACT_KEY = "perms_anyone_interact"; +const char* LLPanelContents::PERMS_ANYONE_CONTROL_KEY = "perms_anyone_control"; BOOL LLPanelContents::postBuild() { diff --git a/indra/newview/llpanelcontents.h b/indra/newview/llpanelcontents.h index b7b103d5b..8e4aa7c4e 100644 --- a/indra/newview/llpanelcontents.h +++ b/indra/newview/llpanelcontents.h @@ -54,6 +54,17 @@ public: static void onClickNewScript( void* userdata); static void onClickPermissions( void* userdata); + // Key suffix for "tentative" fields + static const char* TENTATIVE_SUFFIX; + + // These aren't fields in LLMediaEntry, so we have to define them ourselves for checkbox control + static const char* PERMS_OWNER_INTERACT_KEY; + static const char* PERMS_OWNER_CONTROL_KEY; + static const char* PERMS_GROUP_INTERACT_KEY; + static const char* PERMS_GROUP_CONTROL_KEY; + static const char* PERMS_ANYONE_INTERACT_KEY; + static const char* PERMS_ANYONE_CONTROL_KEY; + protected: void getState(LLViewerObject *object); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 2d7da3bc7..1e6269fa0 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -79,6 +79,22 @@ BOOL LLPanelFace::postBuild() { + childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this); + childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this); + + childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this); + childSetAction("button apply",&LLPanelFace::onClickApply,this); + childSetCommitCallback("checkbox planar align",&LLPanelFace::onCommitPlanarAlign, this); + childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this); + childSetAction("button align",&LLPanelFace::onClickAutoFix,this); + childSetAction("copytextures",&LLPanelFace::onClickCopy,this); + childSetAction("pastetextures",&LLPanelFace::onClickPaste,this); + LLTextureCtrl* mTextureCtrl; LLColorSwatchCtrl* mColorSwatch; @@ -96,7 +112,7 @@ BOOL LLPanelFace::postBuild() if(mTextureCtrl) { mTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectTexture" ))); - mTextureCtrl->setCommitCallback( LLPanelFace::onCommitTexture ); + mTextureCtrl->setCommitCallback( boost::bind(&LLPanelFace::onCommitTexture, this, _2) ); mTextureCtrl->setOnCancelCallback( LLPanelFace::onCancelTexture ); mTextureCtrl->setOnSelectCallback( LLPanelFace::onSelectTexture ); mTextureCtrl->setDragCallback(LLPanelFace::onDragTexture); @@ -127,7 +143,7 @@ BOOL LLPanelFace::postBuild() mColorSwatch = getChild("colorswatch"); if(mColorSwatch) { - mColorSwatch->setCommitCallback(LLPanelFace::onCommitColor); + mColorSwatch->setCommitCallback(boost::bind(&LLPanelFace::onCommitColor, this, _2)); mColorSwatch->setOnCancelCallback(LLPanelFace::onCancelColor); mColorSwatch->setOnSelectCallback(LLPanelFace::onSelectColor); mColorSwatch->setCallbackUserData( this ); @@ -146,8 +162,7 @@ BOOL LLPanelFace::postBuild() mCtrlColorTransp = getChild("ColorTrans"); if(mCtrlColorTransp) { - mCtrlColorTransp->setCommitCallback(LLPanelFace::onCommitAlpha); - mCtrlColorTransp->setCallbackUserData(this); + mCtrlColorTransp->setCommitCallback(boost::bind(&LLPanelFace::onCommitAlpha, this, _2)); mCtrlColorTransp->setPrecision(0); mCtrlColorTransp->setFollowsTop(); mCtrlColorTransp->setFollowsLeft(); @@ -156,39 +171,22 @@ BOOL LLPanelFace::postBuild() mCheckFullbright = getChild("checkbox fullbright"); if (mCheckFullbright) { - mCheckFullbright->setCommitCallback(LLPanelFace::onCommitFullbright); - mCheckFullbright->setCallbackUserData( this ); + mCheckFullbright->setCommitCallback(LLPanelFace::onCommitFullbright, this); } mComboTexGen = getChild("combobox texgen"); if(mComboTexGen) { - mComboTexGen->setCommitCallback(LLPanelFace::onCommitTexGen); + mComboTexGen->setCommitCallback(LLPanelFace::onCommitTexGen, this); mComboTexGen->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP); - mComboTexGen->setCallbackUserData( this ); } mCtrlGlow = getChild("glow"); if(mCtrlGlow) { - mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow); - mCtrlGlow->setCallbackUserData(this); + mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow, this); } - childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this); - childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this); - childSetCommitCallback("checkbox planar align",&LLPanelFace::onCommitPlanarAlign, this); - childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this); - childSetAction("button apply",&onClickApply,this); - childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this); - childSetAction("button align",onClickAutoFix,this); - childSetAction("copytextures",onClickCopy,this); - childSetAction("pastetextures",onClickPaste,this); clearCtrls(); @@ -521,12 +519,9 @@ void LLPanelFace::getState() { BOOL editable = objectp->permModify() && !objectp->isPermanentEnforced(); - // only turn on auto-adjust button if there is a media renderer and the media is loaded - childSetEnabled("textbox autofix",FALSE); - //mLabelTexAutoFix->setEnabled ( FALSE ); - childSetEnabled("button align",FALSE); - //mBtnAutoFix->setEnabled ( FALSE ); + getChildView("textbox autofix")->setEnabled(editable); + getChildView("button align")->setEnabled(editable); //if ( LLMediaEngine::getInstance()->getMediaRenderer () ) // if ( LLMediaEngine::getInstance()->getMediaRenderer ()->isLoaded () ) @@ -543,7 +538,7 @@ void LLPanelFace::getState() childSetEnabled("copytextures", single_volume && editable); childSetEnabled("pastetextures", single_volume && editable); childSetEnabled("textbox params", single_volume && editable); - childSetEnabled("button apply",editable); + getChildView("button apply")->setEnabled(editable); bool identical; LLTextureCtrl* texture_ctrl = getChild("texture control"); @@ -684,7 +679,7 @@ void LLPanelFace::getState() // Texture scale { - childSetEnabled("tex scale",editable); + getChildView("tex scale")->setEnabled(editable); //mLabelTexScale->setEnabled( editable ); F32 scale_s = 1.f; struct f2 : public LLSelectedTEGetFunctor @@ -696,12 +691,12 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_s ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexScaleU",editable ? llabs(scale_s) : 0); - childSetTentative("TexScaleU",LLSD((BOOL)(!identical))); - childSetEnabled("TexScaleU",editable); - childSetValue("checkbox flip s",LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE ))); - childSetTentative("checkbox flip s",LLSD((BOOL)((!identical) ? TRUE : FALSE ))); - childSetEnabled("checkbox flip s",editable); + getChild("TexScaleU")->setValue(editable ? llabs(scale_s) : 0); + getChild("TexScaleU")->setTentative(LLSD((BOOL)(!identical))); + getChildView("TexScaleU")->setEnabled(editable); + getChild("checkbox flip s")->setValue(LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE ))); + getChild("checkbox flip s")->setTentative(LLSD((BOOL)((!identical) ? TRUE : FALSE ))); + getChildView("checkbox flip s")->setEnabled(editable); } { @@ -716,17 +711,17 @@ void LLPanelFace::getState() identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_t ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexScaleV",llabs(editable ? llabs(scale_t) : 0)); - childSetTentative("TexScaleV",LLSD((BOOL)(!identical))); - childSetEnabled("TexScaleV",editable); - childSetValue("checkbox flip t",LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE ))); - childSetTentative("checkbox flip t",LLSD((BOOL)((!identical) ? TRUE : FALSE ))); - childSetEnabled("checkbox flip t",editable); + getChild("TexScaleV")->setValue(llabs(editable ? llabs(scale_t) : 0)); + getChild("TexScaleV")->setTentative(LLSD((BOOL)(!identical))); + getChildView("TexScaleV")->setEnabled(editable); + getChild("checkbox flip t")->setValue(LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE ))); + getChild("checkbox flip t")->setTentative(LLSD((BOOL)((!identical) ? TRUE : FALSE ))); + getChildView("checkbox flip t")->setEnabled(editable); } // Texture offset { - childSetEnabled("tex offset",editable); + getChildView("tex offset")->setEnabled(editable); F32 offset_s = 0.f; struct f4 : public LLSelectedTEGetFunctor { @@ -737,9 +732,9 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_s ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexOffsetU", editable ? offset_s : 0); - childSetTentative("TexOffsetU",!identical); - childSetEnabled("TexOffsetU",editable); + getChild("TexOffsetU")->setValue(editable ? offset_s : 0); + getChild("TexOffsetU")->setTentative(!identical); + getChildView("TexOffsetU")->setEnabled(editable); } { @@ -753,14 +748,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_t ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexOffsetV", editable ? offset_t : 0); - childSetTentative("TexOffsetV",!identical); - childSetEnabled("TexOffsetV",editable); + getChild("TexOffsetV")->setValue(editable ? offset_t : 0); + getChild("TexOffsetV")->setTentative(!identical); + getChildView("TexOffsetV")->setEnabled(editable); } // Texture rotation { - childSetEnabled("tex rotate",editable); + getChildView("tex rotate")->setEnabled(editable); F32 rotation = 0.f; struct f6 : public LLSelectedTEGetFunctor { @@ -771,9 +766,9 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, rotation ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexRot", editable ? rotation * RAD_TO_DEG : 0); - childSetTentative("TexRot",!identical); - childSetEnabled("TexRot",editable); + getChild("TexRot")->setValue(editable ? rotation * RAD_TO_DEG : 0); + getChild("TexRot")->setTentative(!identical); + getChildView("TexRot")->setEnabled(editable); } // Color swatch @@ -799,13 +794,13 @@ void LLPanelFace::getState() } // Color transparency { - childSetEnabled("color trans",editable); + getChildView("color trans")->setEnabled(editable); } F32 transparency = (1.f - color.mV[VALPHA]) * 100.f; { - childSetValue("ColorTrans", editable ? transparency : 0); - childSetEnabled("ColorTrans",editable); + getChild("ColorTrans")->setValue(editable ? transparency : 0); + getChildView("ColorTrans")->setEnabled(editable); } { @@ -819,10 +814,10 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, glow ); - childSetValue("glow",glow); - childSetEnabled("glow",editable); - childSetTentative("glow",!identical); - childSetEnabled("glow label",editable); + getChild("glow")->setValue(glow); + getChildView("glow")->setEnabled(editable); + getChild("glow")->setTentative(!identical); + getChildView("glow label")->setEnabled(editable); } @@ -847,9 +842,9 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox shininess'" << llendl; } - childSetEnabled("combobox shininess",editable); - childSetTentative("combobox shininess",!identical); - childSetEnabled("label shininess",editable); + getChildView("combobox shininess")->setEnabled(editable); + getChild("combobox shininess")->setTentative(!identical); + getChildView("label shininess")->setEnabled(editable); } { @@ -872,9 +867,9 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox bumpiness'" << llendl; } - childSetEnabled("combobox bumpiness",editable); - childSetTentative("combobox bumpiness",!identical); - childSetEnabled("label bumpiness",editable); + getChildView("combobox bumpiness")->setEnabled(editable); + getChild("combobox bumpiness")->setTentative(!identical); + getChildView("label bumpiness")->setEnabled(editable); } { @@ -898,19 +893,21 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox texgen'" << llendl; } - childSetEnabled("combobox texgen",editable); - childSetTentative("combobox texgen",!identical); - childSetEnabled("tex gen",editable); + getChildView("combobox texgen")->setEnabled(editable); + getChild("combobox texgen")->setTentative(!identical); + getChildView("tex gen")->setEnabled(editable); if (selected_texgen == 1) { - childSetText("tex scale",getString("string repeats per meter")); - childSetValue("TexScaleU", 2.0f * childGetValue("TexScaleU").asReal() ); - childSetValue("TexScaleV", 2.0f * childGetValue("TexScaleV").asReal() ); + getChild("TexScaleU")->setValue(2.0f * getChild("TexScaleU")->getValue().asReal() ); + getChild("TexScaleV")->setValue(2.0f * getChild("TexScaleV")->getValue().asReal() ); + + // EXP-1507 (change label based on the mapping mode) + getChild("tex scale")->setValue(getString("string repeats per meter")); } else { - childSetText("tex scale",getString("string repeats per face")); + getChild("tex scale")->setValue(getString("string repeats per face")); } } @@ -926,14 +923,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, fullbrightf ); - childSetValue("checkbox fullbright",(S32)fullbrightf); - childSetEnabled("checkbox fullbright",editable); - childSetTentative("checkbox fullbright",!identical); + getChild("checkbox fullbright")->setValue((S32)fullbrightf); + getChildView("checkbox fullbright")->setEnabled(editable); + getChild("checkbox fullbright")->setTentative(!identical); } // Repeats per meter label { - childSetEnabled("rpt",editable); + getChildView("rpt")->setEnabled(editable); } // Repeats per meter @@ -953,14 +950,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, repeats ); - childSetValue("rptctrl", editable ? repeats : 0); - childSetTentative("rptctrl",!identical); + getChild("rptctrl")->setValue(editable ? repeats : 0); + getChild("rptctrl")->setTentative(!identical); LLComboBox* mComboTexGen = getChild("combobox texgen"); if (mComboTexGen) { BOOL enabled = editable && (!mComboTexGen || mComboTexGen->getCurrentIndex() != 1); - childSetEnabled("rptctrl",enabled); - childSetEnabled("button apply",enabled); + getChildView("rptctrl")->setEnabled(enabled); + getChildView("button apply")->setEnabled(enabled); } } @@ -994,19 +991,21 @@ void LLPanelFace::getState() mColorSwatch->setFallbackImageName("locked_image.j2c" ); mColorSwatch->setValid(FALSE); } - childSetEnabled("color trans",FALSE); - childSetEnabled("rpt",FALSE); - childSetEnabled("tex scale",FALSE); - childSetEnabled("tex offset",FALSE); - childSetEnabled("tex rotate",FALSE); - childSetEnabled("tex gen",FALSE); - childSetEnabled("label shininess",FALSE); - childSetEnabled("label bumpiness",FALSE); + getChildView("color trans")->setEnabled(FALSE); + getChildView("rpt")->setEnabled(FALSE); + getChildView("tex scale")->setEnabled(FALSE); + getChildView("tex offset")->setEnabled(FALSE); + getChildView("tex rotate")->setEnabled(FALSE); + getChildView("tex gen")->setEnabled(FALSE); + getChildView("label shininess")->setEnabled(FALSE); + getChildView("label bumpiness")->setEnabled(FALSE); - childSetEnabled("textbox autofix",FALSE); + getChildView("textbox autofix")->setEnabled(FALSE); - childSetEnabled("button align",FALSE); - childSetEnabled("button apply",FALSE); + getChildView("button align")->setEnabled(FALSE); + getChildView("button apply")->setEnabled(FALSE); + //getChildView("has media")->setEnabled(FALSE); + //getChildView("media info set")->setEnabled(FALSE); // Set variable values for numeric expressions @@ -1037,18 +1036,14 @@ F32 LLPanelFace::valueGlow(LLViewerObject* object, S32 face) } -// static -void LLPanelFace::onCommitColor(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onCommitColor(const LLSD& data) { - LLPanelFace* self = (LLPanelFace*) userdata; - self->sendColor(); + sendColor(); } -// static -void LLPanelFace::onCommitAlpha(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onCommitAlpha(const LLSD& data) { - LLPanelFace* self = (LLPanelFace*) userdata; - self->sendAlpha(); + sendAlpha(); } // static @@ -1118,14 +1113,10 @@ BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item, void*) return accept; } -// static -void LLPanelFace::onCommitTexture( LLUICtrl* ctrl, void* userdata ) +void LLPanelFace::onCommitTexture( const LLSD& data ) { - LLPanelFace* self = (LLPanelFace*) userdata; - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT ); - - self->sendTexture(); + sendTexture(); } // static @@ -1138,7 +1129,7 @@ void LLPanelFace::onCancelTexture(LLUICtrl* ctrl, void* userdata) void LLPanelFace::onSelectTexture(LLUICtrl* ctrl, void* userdata) { LLPanelFace* self = (LLPanelFace*) userdata; - LLSelectMgr::getInstance()->saveSelectedObjectTextures(); + LLSelectMgr::getInstance()->saveSelectedObjectTextures(); self->sendTexture(); } @@ -1159,12 +1150,10 @@ void LLPanelFace::onClickApply(void* userdata) gFocusMgr.setKeyboardFocus( NULL ); //F32 repeats_per_meter = self->mCtrlRepeatsPerMeter->get(); - F32 repeats_per_meter = (F32)self->childGetValue( "rptctrl" ).asReal();//self->mCtrlRepeatsPerMeter->get(); + F32 repeats_per_meter = (F32)self->getChild("rptctrl")->getValue().asReal();//self->mCtrlRepeatsPerMeter->get(); LLSelectMgr::getInstance()->selectionTexScaleAutofit( repeats_per_meter ); } -// commit the fit media texture to prim button - struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor { virtual bool apply(LLViewerObject* object, S32 te) diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 2f9a7887a..e9700a21e 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -73,17 +73,18 @@ protected: void sendGlow(); void sendMedia(); - // this function is to return TRUE if the dra should succeed. + // this function is to return TRUE if the drag should succeed. static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item, void* ud); - static void onCommitTexture( LLUICtrl* ctrl, void* userdata); - static void onCancelTexture( LLUICtrl* ctrl, void* userdata); - static void onSelectTexture( LLUICtrl* ctrl, void* userdata); - static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata); - static void onCommitColor( LLUICtrl* ctrl, void* userdata); - static void onCommitAlpha( LLUICtrl* ctrl, void* userdata); + void onCommitTexture(const LLSD& data); + static void onCancelTexture( LLUICtrl* ctrl, void* userdata); + static void onSelectTexture( LLUICtrl* ctrl, void* userdata); + void onCommitColor(const LLSD& data); + void onCommitAlpha(const LLSD& data); static void onCancelColor( LLUICtrl* ctrl, void* userdata); static void onSelectColor( LLUICtrl* ctrl, void* userdata); + + static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata); static void onCommitBump( LLUICtrl* ctrl, void* userdata); static void onCommitTexGen( LLUICtrl* ctrl, void* userdata); static void onCommitShiny( LLUICtrl* ctrl, void* userdata); diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp new file mode 100644 index 000000000..d6fb6b2cc --- /dev/null +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -0,0 +1,515 @@ +/** + * @file llpanelmediasettingsgeneral.cpp + * @brief LLPanelMediaSettingsGeneral class implementation + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelmediasettingsgeneral.h" + +// library includes +#include "llcombobox.h" +#include "llcheckboxctrl.h" +#include "llnotificationsutil.h" +#include "llspinctrl.h" +#include "lluictrlfactory.h" + +// project includes +#include "llagent.h" +#include "llviewerwindow.h" +#include "llviewermedia.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llbutton.h" +#include "lltexturectrl.h" +#include "llurl.h" +#include "llwindow.h" +#include "llmediaentry.h" +#include "llmediactrl.h" +#include "llpanelcontents.h" +#include "llpermissions.h" +#include "llpluginclassmedia.h" +#include "llfloatermediasettings.h" +#include "llfloatertools.h" +#include "lltrans.h" +#include "lltextbox.h" +#include "llpanelmediasettingssecurity.h" + +const char *CHECKERBOARD_DATA_URL = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E"; + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() : + mAutoLoop( NULL ), + mFirstClick( NULL ), + mAutoZoom( NULL ), + mAutoPlay( NULL ), + mAutoScale( NULL ), + mWidthPixels( NULL ), + mHeightPixels( NULL ), + mHomeURL( NULL ), + mCurrentURL( NULL ), + mParent( NULL ), + mMediaEditable(false) +{ + // build dialog from XML + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_general.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsGeneral::postBuild() +{ + // connect member vars with UI widgets + mAutoLoop = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_LOOP_KEY ); + mAutoPlay = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_PLAY_KEY ); + mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY ); + mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY ); + mCurrentURL = getChild< LLTextBox >( LLMediaEntry::CURRENT_URL_KEY ); + mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); + mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY ); + mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY ); + mWidthPixels = getChild< LLSpinCtrl >( LLMediaEntry::WIDTH_PIXELS_KEY ); + mPreviewMedia = getChild("preview_media"); + mFailWhiteListText = getChild( "home_fails_whitelist_label" ); + + // watch commit action for HOME URL + childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this); + childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this); + + // interrogates controls and updates widgets as required + updateMediaPreview(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsGeneral::~LLPanelMediaSettingsGeneral() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::draw() +{ + // housekeeping + LLPanel::draw(); + + // TODO: we need to call this repeatedly until the floater panels are fully + // created but once we have a valid answer, we should stop looking here - the + // commit callback will handle it + checkHomeUrlPassesWhitelist(); + + // enable/disable pixel values image entry based on auto scale checkbox + if ( mAutoScale->getValue().asBoolean() == false ) + { + getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( true ); + getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( true ); + } + else + { + getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( false ); + getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( false ); + }; + + // enable/disable UI based on type of media + bool reset_button_is_active = true; + if( mPreviewMedia ) + { + LLPluginClassMedia* media_plugin = mPreviewMedia->getMediaPlugin(); + if( media_plugin ) + { + // turn off volume (if we can) for preview. Note: this really only + // works for QuickTime movies right now - no way to control the + // volume of a flash app embedded in a page for example + media_plugin->setVolume( 0 ); + + // some controls are only appropriate for time or browser type plugins + // so we selectively enable/disable them - need to do it in draw + // because the information from plugins arrives assynchronously + bool show_time_controls = media_plugin->pluginSupportsMediaTime(); + if ( show_time_controls ) + { + getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( false ); + reset_button_is_active = false; + getChildView("current_url_label")->setEnabled(false ); + getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( true ); + } + else + { + getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( true ); + reset_button_is_active = true; + getChildView("current_url_label")->setEnabled(true ); + getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( false ); + }; + }; + }; + + // current URL can change over time, update it here + updateCurrentUrl(); + + LLPermissions perm; + bool user_can_press_reset = mMediaEditable; + + // several places modify this widget so we must collect states in one place + if ( reset_button_is_active ) + { + // user has perms to press reset button and it is active + if ( user_can_press_reset ) + { + getChildView("current_url_reset_btn")->setEnabled(true ); + } + // user does not has perms to press reset button and it is active + else + { + getChildView("current_url_reset_btn")->setEnabled(false ); + }; + } + else + // reset button is inactive so we just slam it to off - other states don't matter + { + getChildView("current_url_reset_btn")->setEnabled(false ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable) +{ + LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; + self->mAutoLoop->clear(); + self->mAutoPlay->clear(); + self->mAutoScale->clear(); + self->mAutoZoom ->clear(); + self->mCurrentURL->clear(); + self->mFirstClick->clear(); + self->mHeightPixels->clear(); + self->mHomeURL->clear(); + self->mWidthPixels->clear(); + self->mAutoLoop ->setEnabled(editable); + self->mAutoPlay ->setEnabled(editable); + self->mAutoScale ->setEnabled(editable); + self->mAutoZoom ->setEnabled(editable); + self->mCurrentURL ->setEnabled(editable); + self->mFirstClick ->setEnabled(editable); + self->mHeightPixels ->setEnabled(editable); + self->mHomeURL ->setEnabled(editable); + self->mWidthPixels ->setEnabled(editable); + self->updateMediaPreview(); +} + +// static +bool LLPanelMediaSettingsGeneral::isMultiple() +{ + // IF all the faces have media (or all dont have media) + if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) + { + if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) + { + return true; + } + + } + else + { + if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) + { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& _media_settings, bool editable) +{ + LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; + self->mMediaEditable = editable; + + LLSD media_settings = _media_settings; + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + // *HACK: "edit" the incoming media_settings + media_settings[LLMediaEntry::CURRENT_URL_KEY] = LLTrans::getString("Multiple Media"); + media_settings[LLMediaEntry::HOME_URL_KEY] = LLTrans::getString("Multiple Media"); + } + + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::AUTO_LOOP_KEY, self->mAutoLoop, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_PLAY_KEY, self->mAutoPlay, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_SCALE_KEY, self->mAutoScale, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_ZOOM_KEY, self->mAutoZoom, "LLCheckBoxCtrl" }, + { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLTextBox" }, + { LLMediaEntry::HEIGHT_PIXELS_KEY, self->mHeightPixels, "LLSpinCtrl" }, + { LLMediaEntry::HOME_URL_KEY, self->mHomeURL, "LLLineEditor" }, + { LLMediaEntry::FIRST_CLICK_INTERACT_KEY, self->mFirstClick, "LLCheckBoxCtrl" }, + { LLMediaEntry::WIDTH_PIXELS_KEY, self->mWidthPixels, "LLSpinCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLLineEditor" ) + { + static_cast< LLLineEditor* >( data_set[ i ].ctrl_ptr )-> + setText( media_settings[ base_key ].asString() ); + } + else + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + else + if ( data_set[ i ].ctrl_type == "LLComboBox" ) + static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )-> + setCurrentByIndex( media_settings[ base_key ].asInteger() ); + else + if ( data_set[ i ].ctrl_type == "LLSpinCtrl" ) + static_cast< LLSpinCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asInteger() ); + + data_set[ i ].ctrl_ptr->setEnabled(self->mMediaEditable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // interrogates controls and updates widgets as required + self->updateMediaPreview(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Helper to set media control to media URL as required +void LLPanelMediaSettingsGeneral::updateMediaPreview() +{ + if ( mHomeURL->getValue().asString().length() > 0 ) + { + if(mPreviewMedia->getCurrentNavUrl() != mHomeURL->getValue().asString()) + { + mPreviewMedia->navigateTo( mHomeURL->getValue().asString() ); + } + } + else + // new home URL will be empty if media is deleted so display a + // "preview goes here" data url page + { + if(mPreviewMedia->getCurrentNavUrl() != CHECKERBOARD_DATA_URL) + { + mPreviewMedia->navigateTo( CHECKERBOARD_DATA_URL ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// + +// virtual +void LLPanelMediaSettingsGeneral::onClose(bool app_quitting) +{ + if(mPreviewMedia) + { + mPreviewMedia->unloadMediaSource(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::checkHomeUrlPassesWhitelist() +{ + // parent floater has not constructed the security panel yet + if ( mParent->getPanelSecurity() == 0 ) + return; + + std::string home_url = getHomeUrl(); + if ( home_url.empty() || mParent->getPanelSecurity()->urlPassesWhiteList( home_url ) ) + { + // Home URL is empty or passes the white list so hide the warning message + mFailWhiteListText->setVisible( false ); + } + else + { + // Home URL does not pass the white list so show the warning message + mFailWhiteListText->setVisible( true ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata ) +{ + LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; + + // check home url passes whitelist and display warning if not + self->checkHomeUrlPassesWhitelist(); + + self->updateMediaPreview(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata) +{ + LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; + self->navigateHomeSelectedFace(false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::preApply() +{ + // Make sure the home URL entry is committed + mHomeURL->onCommit(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + if (include_tentative || !mAutoLoop->getTentative()) fill_me_in[LLMediaEntry::AUTO_LOOP_KEY] = (LLSD::Boolean)mAutoLoop->getValue(); + if (include_tentative || !mAutoPlay->getTentative()) fill_me_in[LLMediaEntry::AUTO_PLAY_KEY] = (LLSD::Boolean)mAutoPlay->getValue(); + if (include_tentative || !mAutoScale->getTentative()) fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = (LLSD::Boolean)mAutoScale->getValue(); + if (include_tentative || !mAutoZoom->getTentative()) fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = (LLSD::Boolean)mAutoZoom->getValue(); + //Don't fill in current URL: this is only supposed to get changed via navigate + // if (include_tentative || !mCurrentURL->getTentative()) fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue(); + if (include_tentative || !mHeightPixels->getTentative()) fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = (LLSD::Integer)mHeightPixels->getValue(); + // Don't fill in the home URL if it is the special "Multiple Media" string! + if ((include_tentative || !mHomeURL->getTentative()) + && LLTrans::getString("Multiple Media") != mHomeURL->getValue()) + fill_me_in[LLMediaEntry::HOME_URL_KEY] = (LLSD::String)mHomeURL->getValue(); + if (include_tentative || !mFirstClick->getTentative()) fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = (LLSD::Boolean)mFirstClick->getValue(); + if (include_tentative || !mWidthPixels->getTentative()) fill_me_in[LLMediaEntry::WIDTH_PIXELS_KEY] = (LLSD::Integer)mWidthPixels->getValue(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::postApply() +{ + // Make sure to navigate to the home URL if the current URL is empty and + // autoplay is on + navigateHomeSelectedFace(true); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent ) +{ + mParent = parent; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace(bool only_if_current_is_empty) +{ + struct functor_navigate_media : public LLSelectedTEGetFunctor< bool> + { + functor_navigate_media(bool flag) : only_if_current_is_empty(flag) {} + bool get( LLViewerObject* object, S32 face ) + { + if ( object && object->getTE(face) && object->permModify() ) + { + const LLMediaEntry *media_data = object->getTE(face)->getMediaData(); + if ( media_data ) + { + if (!only_if_current_is_empty || (media_data->getCurrentURL().empty() && media_data->getAutoPlay())) + { + viewer_media_t media_impl = + LLViewerMedia::getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID()); + if(media_impl) + { + media_impl->navigateHome(); + return true; + } + } + } + } + return false; + }; + bool only_if_current_is_empty; + + } functor_navigate_media(only_if_current_is_empty); + + bool all_face_media_navigated = false; + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated ); + + // Note: we don't update the 'current URL' field until the media data itself changes + + return all_face_media_navigated; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLPanelMediaSettingsGeneral::getHomeUrl() +{ + return mHomeURL->getValue().asString(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::updateCurrentUrl() +{ + // Get the current URL from the selection + const LLMediaEntry default_media_data; + std::string value_str = default_media_data.getCurrentURL(); + struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getCurrentURL(); + return mMediaEntry.getCurrentURL(); + }; + + const LLMediaEntry & mMediaEntry; + + } func_current_url(default_media_data); + bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str ); + mCurrentURL->setText(value_str); + mCurrentURL->setTentative(identical); + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + mCurrentURL->setText(LLTrans::getString("Multiple Media")); + } +} diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h new file mode 100644 index 000000000..0ae1401ab --- /dev/null +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -0,0 +1,100 @@ +/** + * @file llpanelmediasettingsgeneral.h + * @brief LLPanelMediaSettingsGeneral class definition + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H +#define LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H + +#include "llpanel.h" + +class LLButton; +class LLCheckBoxCtrl; +class LLLineEditor; +class LLSpinCtrl; +class LLTextureCtrl; +class LLMediaCtrl; +class LLTextBox; +class LLFloaterMediaSettings; + +class LLPanelMediaSettingsGeneral : public LLPanel +{ +public: + LLPanelMediaSettingsGeneral(); + ~LLPanelMediaSettingsGeneral(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onClose(bool app_quitting); + + void setParent( LLFloaterMediaSettings* parent ); + static void initValues( void* userdata, const LLSD& media_settings ,bool editable); + static void clearValues( void* userdata, bool editable); + + // Navigates the current selected face to the Home URL. + // If 'only_if_current_is_empty' is "true", it only performs + // the operation if: 1) the current URL is empty, and 2) auto play is true. + bool navigateHomeSelectedFace(bool only_if_current_is_empty); + + void updateMediaPreview(); + + const std::string getHomeUrl(); + +protected: + LLFloaterMediaSettings* mParent; + bool mMediaEditable; + +private: + void updateCurrentUrl(); + + static void onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata); + static void onCommitHomeURL(LLUICtrl* ctrl, void *userdata ); + + static bool isMultiple(); + + void checkHomeUrlPassesWhitelist(); + + LLCheckBoxCtrl* mAutoLoop; + LLCheckBoxCtrl* mFirstClick; + LLCheckBoxCtrl* mAutoZoom; + LLCheckBoxCtrl* mAutoPlay; + LLCheckBoxCtrl* mAutoScale; + LLSpinCtrl* mWidthPixels; + LLSpinCtrl* mHeightPixels; + LLLineEditor* mHomeURL; + LLTextBox* mCurrentURL; + LLMediaCtrl* mPreviewMedia; + LLTextBox* mFailWhiteListText; +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H diff --git a/indra/newview/llpanelmediasettingspermissions.cpp b/indra/newview/llpanelmediasettingspermissions.cpp new file mode 100644 index 000000000..5f7042a3c --- /dev/null +++ b/indra/newview/llpanelmediasettingspermissions.cpp @@ -0,0 +1,285 @@ +/** + * @file llpanelmediasettingspermissions.cpp + * @brief LLPanelMediaSettingsPermissions class implementation + * + * note that "permissions" tab is really "Controls" tab - refs to 'perms' and + * 'permissions' not changed to 'controls' since we don't want to change + * shared files in server code and keeping everything the same seemed best. + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelmediasettingspermissions.h" +#include "llpanelcontents.h" +#include "llcombobox.h" +#include "llcheckboxctrl.h" +#include "llspinctrl.h" +#include "llurlhistory.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llnamebox.h" +#include "lltrans.h" +#include "llfloatermediasettings.h" + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsPermissions::LLPanelMediaSettingsPermissions() : + mControls( NULL ), + mPermsOwnerInteract( 0 ), + mPermsOwnerControl( 0 ), + mPermsGroupName( 0 ), + mPermsGroupInteract( 0 ), + mPermsGroupControl( 0 ), + mPermsWorldInteract( 0 ), + mPermsWorldControl( 0 ) +{ + // build dialog from XML + //buildFromFile( "panel_media_settings_permissions.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_permissions.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsPermissions::postBuild() +{ + // connect member vars with UI widgets + mControls = getChild< LLComboBox >( LLMediaEntry::CONTROLS_KEY ); + mPermsOwnerInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_OWNER_INTERACT_KEY ); + mPermsOwnerControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_OWNER_CONTROL_KEY ); + mPermsGroupInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_GROUP_INTERACT_KEY ); + mPermsGroupControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_GROUP_CONTROL_KEY ); + mPermsWorldInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); + mPermsWorldControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); + + //mPermsGroupName = getChild< LLNameBox >( "perms_group_name" ); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsPermissions::~LLPanelMediaSettingsPermissions() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void LLPanelMediaSettingsPermissions::draw() +{ + // housekeeping + LLPanel::draw(); + + //getChild("perms_group_name")->setValue(LLStringUtil::null); + LLUUID group_id; + BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id); + if (groups_identical) + { + if(mPermsGroupName) + { + mPermsGroupName->setNameID(group_id, true); + } + } + else + { + if(mPermsGroupName) + { + mPermsGroupName->setNameID(LLUUID::null, TRUE); + mPermsGroupName->refresh(LLUUID::null, std::string(), true); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsPermissions::clearValues( void* userdata, bool editable) +{ + LLPanelMediaSettingsPermissions *self =(LLPanelMediaSettingsPermissions *)userdata; + + self->mControls->clear(); + self->mPermsOwnerInteract->clear(); + self->mPermsOwnerControl->clear(); + self->mPermsGroupInteract->clear(); + self->mPermsGroupControl->clear(); + self->mPermsWorldInteract->clear(); + self->mPermsWorldControl->clear(); + + self->mControls->setEnabled(editable); + self->mPermsOwnerInteract->setEnabled(editable); + self->mPermsOwnerControl->setEnabled(editable); + self->mPermsGroupInteract->setEnabled(editable); + self->mPermsGroupControl->setEnabled(editable); + self->mPermsWorldInteract->setEnabled(editable); + self->mPermsWorldControl->setEnabled(editable); + + self->getChild< LLTextBox >("controls_label")->setEnabled(editable); + self->getChild< LLTextBox >("owner_label")->setEnabled(editable); + self->getChild< LLTextBox >("group_label")->setEnabled(editable); + //self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsPermissions::initValues( void* userdata, const LLSD& media_settings , bool editable) +{ + LLPanelMediaSettingsPermissions *self =(LLPanelMediaSettingsPermissions *)userdata; + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::CONTROLS_KEY, self->mControls, "LLComboBox" }, + { LLPanelContents::PERMS_OWNER_INTERACT_KEY, self->mPermsOwnerInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_OWNER_CONTROL_KEY, self->mPermsOwnerControl, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_GROUP_INTERACT_KEY, self->mPermsGroupInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_GROUP_CONTROL_KEY, self->mPermsGroupControl, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_ANYONE_INTERACT_KEY, self->mPermsWorldInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_ANYONE_CONTROL_KEY, self->mPermsWorldControl, "LLCheckBoxCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + { + // Most recent change to the "sense" of these checkboxes + // means the value in the checkbox matches that on the server + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + } + else + if ( data_set[ i ].ctrl_type == "LLComboBox" ) + static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )-> + setCurrentByIndex( media_settings[ base_key ].asInteger() ); + + data_set[ i ].ctrl_ptr->setEnabled(editable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // *NOTE: If any of a particular flavor is tentative, we have to disable + // them all because of an architectural issue: namely that we represent + // these as a bit field, and we can't selectively apply only one bit to all selected + // faces if they don't match. Also see the *NOTE below. + if ( self->mPermsOwnerInteract->getTentative() || + self->mPermsGroupInteract->getTentative() || + self->mPermsWorldInteract->getTentative()) + { + self->mPermsOwnerInteract->setEnabled(false); + self->mPermsGroupInteract->setEnabled(false); + self->mPermsWorldInteract->setEnabled(false); + } + if ( self->mPermsOwnerControl->getTentative() || + self->mPermsGroupControl->getTentative() || + self->mPermsWorldControl->getTentative()) + { + self->mPermsOwnerControl->setEnabled(false); + self->mPermsGroupControl->setEnabled(false); + self->mPermsWorldControl->setEnabled(false); + } + + self->getChild< LLTextBox >("controls_label")->setEnabled(editable); + self->getChild< LLTextBox >("owner_label")->setEnabled(editable); + self->getChild< LLTextBox >("group_label")->setEnabled(editable); + //self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::preApply() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + // moved over from the 'General settings' tab + if (include_tentative || !mControls->getTentative()) fill_me_in[LLMediaEntry::CONTROLS_KEY] = (LLSD::Integer)mControls->getCurrentIndex(); + + // *NOTE: For some reason, gcc does not like these symbol references in the + // expressions below (inside the static_casts). I have NO idea why :(. + // For some reason, assigning them to const temp vars here fixes the link + // error. Bizarre. + const U8 none = LLMediaEntry::PERM_NONE; + const U8 owner = LLMediaEntry::PERM_OWNER; + const U8 group = LLMediaEntry::PERM_GROUP; + const U8 anyone = LLMediaEntry::PERM_ANYONE; + const LLSD::Integer control = static_cast( + (mPermsOwnerControl->getValue() ? owner : none ) | + (mPermsGroupControl->getValue() ? group: none ) | + (mPermsWorldControl->getValue() ? anyone : none )); + const LLSD::Integer interact = static_cast( + (mPermsOwnerInteract->getValue() ? owner: none ) | + (mPermsGroupInteract->getValue() ? group : none ) | + (mPermsWorldInteract->getValue() ? anyone : none )); + + // *TODO: This will fill in the values of all permissions values, even if + // one or more is tentative. This is not quite the user expectation...what + // it should do is only change the bit that was made "untentative", but in + // a multiple-selection situation, this isn't possible given the architecture + // for how settings are applied. + if (include_tentative || + !mPermsOwnerControl->getTentative() || + !mPermsGroupControl->getTentative() || + !mPermsWorldControl->getTentative()) + { + fill_me_in[LLMediaEntry::PERMS_CONTROL_KEY] = control; + } + if (include_tentative || + !mPermsOwnerInteract->getTentative() || + !mPermsGroupInteract->getTentative() || + !mPermsWorldInteract->getTentative()) + { + fill_me_in[LLMediaEntry::PERMS_INTERACT_KEY] = interact; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::postApply() +{ + // no-op +} + + diff --git a/indra/newview/llpanelmediasettingspermissions.h b/indra/newview/llpanelmediasettingspermissions.h new file mode 100644 index 000000000..f97672018 --- /dev/null +++ b/indra/newview/llpanelmediasettingspermissions.h @@ -0,0 +1,73 @@ +/** + * @file llpanelmediasettingspermissions.h + * @brief LLPanelMediaSettingsPermissions class definition + * + * note that "permissions" tab is really "Controls" tab - refs to 'perms' and + * 'permissions' not changed to 'controls' since we don't want to change + * shared files in server code and keeping everything the same seemed best. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H +#define LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H + +#include "llpanel.h" +#include "lluuid.h" + +class LLComboBox; +class LLCheckBoxCtrl; +class LLNameBox; + +class LLPanelMediaSettingsPermissions : public LLPanel +{ +public: + LLPanelMediaSettingsPermissions(); + ~LLPanelMediaSettingsPermissions(); + + BOOL postBuild(); + virtual void draw(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + static void initValues( void* userdata, const LLSD& media_settings, bool editable ); + static void clearValues( void* userdata, bool editable); + +private: + LLComboBox* mControls; + LLCheckBoxCtrl* mPermsOwnerInteract; + LLCheckBoxCtrl* mPermsOwnerControl; + LLNameBox* mPermsGroupName; + LLCheckBoxCtrl* mPermsGroupInteract; + LLCheckBoxCtrl* mPermsGroupControl; + LLCheckBoxCtrl* mPermsWorldInteract; + LLCheckBoxCtrl* mPermsWorldControl; +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp new file mode 100644 index 000000000..509560b37 --- /dev/null +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -0,0 +1,366 @@ +/** + * @file llpanelmediasettingssecurity.cpp + * @brief LLPanelMediaSettingsSecurity class implementation + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelmediasettingssecurity.h" + +#include "llpanelcontents.h" +#include "llcheckboxctrl.h" +#include "llnotificationsutil.h" +#include "llscrolllistctrl.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "lltextbox.h" +#include "llfloaterwhitelistentry.h" +#include "llfloatermediasettings.h" + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() : + mParent( NULL ) +{ + mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this)); + mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this)); + + // build dialog from XML + //buildFromFile( "panel_media_settings_security.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_security.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsSecurity::postBuild() +{ + mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY ); + mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY ); + mHomeUrlFailsWhiteListText = getChild( "home_url_fails_whitelist" ); + + setDefaultBtn("whitelist_add"); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::draw() +{ + // housekeeping + LLPanel::draw(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" }, + { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + + bool enabled_overridden = false; + + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + { + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + } + else + if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" ) + { + // get control + LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr ); + list->deleteAllItems(); + + // points to list of white list URLs + LLSD url_list = media_settings[ base_key ]; + + // better be the whitelist + llassert(data_set[ i ].ctrl_ptr == self->mWhiteListList); + + // If tentative, don't add entries + if (media_settings[ tentative_key ].asBoolean()) + { + self->mWhiteListList->setEnabled(false); + enabled_overridden = true; + } + else { + // iterate over them and add to scroll list + LLSD::array_iterator iter = url_list.beginArray(); + while( iter != url_list.endArray() ) + { + std::string entry = *iter; + self->addWhiteListEntry( entry ); + ++iter; + } + } + }; + if ( ! enabled_overridden) data_set[ i ].ctrl_ptr->setEnabled(editable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // initial update - hides/shows status messages etc. + self->updateWhitelistEnableStatus(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + self->mEnableWhiteList->clear(); + self->mWhiteListList->deleteAllItems(); + self->mEnableWhiteList->setEnabled(editable); + self->mWhiteListList->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::preApply() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + if (include_tentative || !mEnableWhiteList->getTentative()) + fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = (LLSD::Boolean)mEnableWhiteList->getValue(); + + if (include_tentative || !mWhiteListList->getTentative()) + { + // iterate over white list and extract items + std::vector< LLScrollListItem* > whitelist_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = whitelist_items.begin(); + + // *NOTE: need actually set the key to be an emptyArray(), or the merge + // we do with this LLSD will think there's nothing to change. + fill_me_in[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray(); + while( iter != whitelist_items.end() ) + { + LLScrollListCell* cell = (*iter)->getColumn( ENTRY_COLUMN ); + std::string whitelist_url = cell->getValue().asString(); + + fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( whitelist_url ); + ++iter; + }; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::postApply() +{ + // no-op +} + +/////////////////////////////////////////////////////////////////////////////// +// Try to make a valid URL if a fragment ( +// white list list box widget and build a list to test against. Can also +const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& src_url ) +{ + // use LLURI to determine if we have a valid scheme + LLURI candidate_url( src_url ); + if ( candidate_url.scheme().empty() ) + { + // build a URL comprised of default scheme and the original fragment + const std::string default_scheme( "http://" ); + return default_scheme + src_url; + }; + + // we *could* test the "default scheme" + "original fragment" URL again + // using LLURI to see if it's valid but I think the outcome is the same + // in either case - our only option is to return the original URL + + // we *think* the original url passed in was valid + return src_url; +} + +/////////////////////////////////////////////////////////////////////////////// +// wrapper for testing a URL against the whitelist. We grab entries from +// white list list box widget and build a list to test against. +bool LLPanelMediaSettingsSecurity::urlPassesWhiteList( const std::string& test_url ) +{ + // If the whitlelist list is tentative, it means we have multiple settings. + // In that case, we have no choice but to return true + if ( mWhiteListList->getTentative() ) return true; + + // the checkUrlAgainstWhitelist(..) function works on a vector + // of strings for the white list entries - in this panel, the white list + // is stored in the widgets themselves so we need to build something compatible. + std::vector< std::string > whitelist_strings; + whitelist_strings.clear(); // may not be required - I forget what the spec says. + + // step through whitelist widget entries and grab them as strings + std::vector< LLScrollListItem* > whitelist_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = whitelist_items.begin(); + while( iter != whitelist_items.end() ) + { + LLScrollListCell* cell = (*iter)->getColumn( ENTRY_COLUMN ); + std::string whitelist_url = cell->getValue().asString(); + + whitelist_strings.push_back( whitelist_url ); + + ++iter; + }; + + // possible the URL is just a fragment so we validize it + const std::string valid_url = makeValidUrl( test_url ); + + // indicate if the URL passes whitelist + return LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_strings ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::updateWhitelistEnableStatus() +{ + // get the value for home URL and make it a valid URL + const std::string valid_url = makeValidUrl( mParent->getHomeUrl() ); + + // now check to see if the home url passes the whitelist in its entirity + if ( urlPassesWhiteList( valid_url ) ) + { + mEnableWhiteList->setEnabled( true ); + mHomeUrlFailsWhiteListText->setVisible( false ); + } + else + { + mEnableWhiteList->set( false ); + mEnableWhiteList->setEnabled( false ); + mHomeUrlFailsWhiteListText->setVisible( true ); + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// Add an entry to the whitelist scrollbox and indicate if the current +// home URL passes this entry or not using an icon +void LLPanelMediaSettingsSecurity::addWhiteListEntry( const std::string& entry ) +{ + // grab the home url + std::string home_url( "" ); + if ( mParent ) + home_url = mParent->getHomeUrl(); + + // try to make a valid URL based on what the user entered - missing scheme for example + const std::string valid_url = makeValidUrl( home_url ); + + // check the home url against this single whitelist entry + std::vector< std::string > whitelist_entries; + whitelist_entries.push_back( entry ); + bool home_url_passes_entry = LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_entries ); + + // build an icon cell based on whether or not the home url pases it or not + LLSD row; + if ( home_url_passes_entry || home_url.empty() ) + { + row[ "columns" ][ ICON_COLUMN ][ "type" ] = "icon"; + row[ "columns" ][ ICON_COLUMN ][ "value" ] = ""; + row[ "columns" ][ ICON_COLUMN ][ "width" ] = 20; + } + else + { + row[ "columns" ][ ICON_COLUMN ][ "type" ] = "icon"; + row[ "columns" ][ ICON_COLUMN ][ "value" ] = "Parcel_Exp_Color.png"; + row[ "columns" ][ ICON_COLUMN ][ "width" ] = 20; + }; + + // always add in the entry itself + row[ "columns" ][ ENTRY_COLUMN ][ "type" ] = "text"; + row[ "columns" ][ ENTRY_COLUMN ][ "value" ] = entry; + + // add to the white list scroll box + mWhiteListList->addElement( row ); +}; + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata ) +{ + + LLPanelMediaSettingsSecurity* self = (LLPanelMediaSettingsSecurity*)userdata; + LLFloaterWhiteListEntry::getInstance()->open(); + + for(LLView* parent = self->getParent(); parent !=NULL; parent = parent->getParent()) + { + if(dynamic_cast(parent)) + { + LLFloaterWhiteListEntry::getInstance()->centerWithin(parent->getRect()); + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata ) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + + self->mWhiteListList->deleteSelectedItems(); + + // contents of whitelist changed so recheck it against home url + self->updateWhitelistEnableStatus(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::setParent( LLFloaterMediaSettings* parent ) +{ + mParent = parent; +}; diff --git a/indra/newview/llpanelmediasettingssecurity.h b/indra/newview/llpanelmediasettingssecurity.h new file mode 100644 index 000000000..fe8e84357 --- /dev/null +++ b/indra/newview/llpanelmediasettingssecurity.h @@ -0,0 +1,82 @@ +/** + * @file llpanelmediasettingssecurity.h + * @brief LLPanelMediaSettingsSecurity class definition + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H +#define LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H + +#include "llpanel.h" + +class LLCheckBoxCtrl; +class LLScrollListCtrl; +class LLTextBox; +class LLFloaterMediaSettings; + +class LLPanelMediaSettingsSecurity : public LLPanel +{ +public: + LLPanelMediaSettingsSecurity(); + ~LLPanelMediaSettingsSecurity(); + + BOOL postBuild(); + virtual void draw(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + static void initValues( void* userdata, const LLSD& media_settings, bool editable); + static void clearValues( void* userdata, bool editable); + void addWhiteListEntry( const std::string& url ); + void setParent( LLFloaterMediaSettings* parent ); + bool urlPassesWhiteList( const std::string& test_url ); + const std::string makeValidUrl( const std::string& src_url ); + + void updateWhitelistEnableStatus(); + +protected: + LLFloaterMediaSettings* mParent; + +private: + enum ColumnIndex + { + ICON_COLUMN = 0, + ENTRY_COLUMN = 1, + }; + + LLCheckBoxCtrl* mEnableWhiteList; + LLScrollListCtrl* mWhiteListList; + LLTextBox* mHomeUrlFailsWhiteListText; + + static void onBtnAdd(void*); + static void onBtnDel(void*); +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 3526d93d6..22175932f 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -845,5 +845,52 @@ template bool LLObjectSelection::getSelectedTEValue(LLSelectedTEGet return identical; } +// Templates +//----------------------------------------------------------------------------- +// isMultipleTEValue iterate through all TEs and test for uniqueness +// with certain return value ignored when performing the test. +// e.g. when testing if the selection has a unique non-empty homeurl : +// you can set ignore_value = "" and it will only compare among the non-empty +// homeUrls and ignore the empty ones. +//----------------------------------------------------------------------------- +template bool LLObjectSelection::isMultipleTEValue(LLSelectedTEGetFunctor* func, const T& ignore_value) +{ + bool have_first = false; + T selected_value = T(); + + // Now iterate through all TEs to test for sameness + bool unique = TRUE; + for (iterator iter = begin(); iter != end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + for (S32 te = 0; te < object->getNumTEs(); ++te) + { + if (!node->isTESelected(te)) + { + continue; + } + T value = func->get(object, te); + if(value == ignore_value) + { + continue; + } + if (!have_first) + { + have_first = true; + } + else + { + if (value !=selected_value ) + { + unique = false; + return !unique; + } + } + } + } + return !unique; +} + #endif diff --git a/indra/newview/skins/default/textures/Flag.png b/indra/newview/skins/default/textures/Flag.png new file mode 100644 index 0000000000000000000000000000000000000000..66739e9bf39cf5325796c0d878ba51854e93700f GIT binary patch literal 3238 zcmV;X3|aGuP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005eNkl<{LENu;hX~z1!r52 zg{SrDbozM!#+a8~+<6m~iTI;VteF&h}>miO~)OGzPFN8o8MM#naS(br_5JeG$ z5Lm5Nn9XL;T7SI*)>`~{e8l+S;cA_f5<&<_DbekAA*DnZhM3RiR{`F&U^1BmKcAmb zRn>hoEQ$ign2YWID;gf7X&L}PS(YaYgb;}17*Q167QkAI&1QqLEKddiK&R6|mSs>% zAxRRXX$mRjZ2+F0o&eyL=S|air4)`sYB(HXzu&*Iz*_r#fZuscM1j^CT5B-IuvjcS z=NxBq;Ln_a4?sd|9lTKP%_wef!xo?gXaQP)fAd-X=`UgooO2(BA&3ahIZs3Z5#1hu zF$O86AB{%PT4TH2Vz=9&u50hT4{i>?Irn*j Y05^C0By~#@LjV8(07*qoM6N<$f+VZ^%m4rY literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 49333fd78..92938cd5c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -403,6 +403,8 @@ + + diff --git a/indra/newview/skins/default/xui/en-us/floater_media_settings.xml b/indra/newview/skins/default/xui/en-us/floater_media_settings.xml new file mode 100644 index 000000000..8a5bae62f --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_media_settings.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + Note: Residents can override this setting + + + + + + Size: + + + + + + X + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml new file mode 100644 index 000000000..3773ff361 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml @@ -0,0 +1,172 @@ + + + + + Controls: + + + + Standard + + + Mini + + + + + Owner + + + + + + + + Group: + + + + + + + + + + Anyone + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml new file mode 100644 index 000000000..828635262 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml @@ -0,0 +1,91 @@ + + + + + + + Entries that the home page fails against are marked: + + + + +Warning: the home page specified in the General tab fails to +pass this whitelist. It has been disabled until a valid entry +has been added. + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en-us/panel_prim_media_controls.xml index 444c990f5..003c4e8cc 100644 --- a/indra/newview/skins/default/xui/en-us/panel_prim_media_controls.xml +++ b/indra/newview/skins/default/xui/en-us/panel_prim_media_controls.xml @@ -268,7 +268,8 @@ follows="top|right" height="20" width="38" - right="-1" + left="140" + bottom="0" border_size="0" mouse_opaque="false" orientation="horizontal"> @@ -281,9 +282,9 @@ name="media_whitelist_flag" follows="top|right" height="16" - image_name="Flag" + image_name="Flag.png" layout="topleft" - bottom="-22" + bottom="-16" width="16" /> diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index e8bacfa91..29363d81c 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -3380,6 +3380,10 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh ' --- + + Multiple Media + Play/Pause Media + An error was found parsing the command line. From be94087bee418347664b6c4d84fe5933598eca0c Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 15 Jun 2013 20:08:05 -0500 Subject: [PATCH 21/36] Fixed a focus issue that prevented face media floater from updating on face change if said floater had current focus. --- indra/newview/llpanelmediasettingsgeneral.cpp | 2 +- indra/newview/lltool.cpp | 15 +++++++++++++++ indra/newview/lltool.h | 1 + indra/newview/lltoolpie.cpp | 9 +++++++++ indra/newview/lltoolpie.h | 1 + 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index d6fb6b2cc..f8ede13ba 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -203,7 +203,7 @@ void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable) self->mAutoPlay->clear(); self->mAutoScale->clear(); self->mAutoZoom ->clear(); - self->mCurrentURL->clear(); + self->mCurrentURL->setValue(""); self->mFirstClick->clear(); self->mHeightPixels->clear(); self->mHomeURL->clear(); diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 6ebeb870a..0740fc38b 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -66,6 +66,21 @@ LLTool::~LLTool() } } +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +{ + BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); + + // This behavior was moved here from LLViewerWindow::handleAnyMouseClick, so it can be selectively overridden by LLTool subclasses. + if(down && result) + { + // This is necessary to force clicks in the world to cause edit + // boxes that might have keyboard focus to relinquish it, and hence + // cause a commit to update their value. JC + gFocusMgr.setKeyboardFocus(NULL); + } + + return result; +} BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index f954a8c24..3fec2c8e8 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -55,6 +55,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 8133066c9..e3dce440d 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -92,6 +92,15 @@ LLToolPie::LLToolPie() { } +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +{ + BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); + + // This override DISABLES the keyboard focus reset that LLTool::handleAnyMouseClick adds. + // LLToolPie will do the right thing in its pick callback. + + return result; +} BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index f811c53c2..5f3c75c3b 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -46,6 +46,7 @@ public: LLToolPie( ); // Virtual functions inherited from LLMouseHandler + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); From d4f5c25612b74a1ce6490df4c909c003f2ca7c2f Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 16 Jun 2013 21:47:55 -0500 Subject: [PATCH 22/36] Fix up some of the broken SLURL<->HippoGridManager logic. --- indra/newview/hippogridmanager.cpp | 47 ++++++++++++++++++--------- indra/newview/hippogridmanager.h | 10 +++--- indra/newview/llpanellogin.cpp | 46 +++++++++++++++----------- indra/newview/llslurl.cpp | 52 +++++++++++++++++++++++++----- indra/newview/llstartup.cpp | 6 ++-- indra/newview/llurldispatcher.cpp | 9 +++--- 6 files changed, 116 insertions(+), 54 deletions(-) diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 93168da88..d7c02be7f 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -506,7 +506,7 @@ void HippoGridInfo::formatFee(std::string &fee, int cost, bool showFree) const } //static -std::string HippoGridInfo::sanitizeGridNick(std::string &gridnick) +std::string HippoGridInfo::sanitizeGridNick(const std::string &gridnick) { std::string tmp; int size = gridnick.size(); @@ -529,7 +529,7 @@ std::string HippoGridInfo::sanitizeGridNick(std::string &gridnick) } -std::string HippoGridInfo::getGridNick() +std::string HippoGridInfo::getGridNick() const { if(!mGridNick.empty()) { @@ -684,42 +684,57 @@ void HippoGridManager::discardAndReload() HippoGridInfo* HippoGridManager::getGrid(const std::string& grid) const { + if(grid.empty()) + return NULL; + std::map::const_iterator it; it = mGridInfo.find(grid); + + //The grids are keyed by 'name' which equates to something like "Second Life" + //Try to match such first. if (it != mGridInfo.end()) { return it->second; } - else + else //Fall back to nick short names. (so something like "secondlife" will work) { - return 0; + for(it = mGridInfo.begin(); it != mGridInfo.end(); ++it) + { + if(it->second && LLStringUtil::compareInsensitive(it->second->getGridNick(), grid)==0) + return it->second; + } } + return NULL; } HippoGridInfo* HippoGridManager::getCurrentGrid() const { HippoGridInfo* grid = getGrid(mCurrentGrid); - if (grid) + if(!grid) { - return grid; - } - else - { - return &HippoGridInfo::FALLBACK_GRIDINFO; + grid = getGrid(mDefaultGrid); } + return grid ? grid : &HippoGridInfo::FALLBACK_GRIDINFO; } -const std::string& HippoGridManager::getDefaultGridNick() const +std::string HippoGridManager::getDefaultGridNick() const +{ + HippoGridInfo* grid = getGrid(mDefaultGrid); + return grid ? grid->getGridNick() : HippoGridInfo::FALLBACK_GRIDINFO.getGridNick(); +} + +std::string HippoGridManager::getCurrentGridNick() const +{ + return getCurrentGrid()->getGridNick(); +} + +const std::string& HippoGridManager::getDefaultGridName() const { return mDefaultGrid; } -const std::string& HippoGridManager::getCurrentGridNick() const +const std::string& HippoGridManager::getCurrentGridName() const { - if (mCurrentGrid.empty()) - { - return mDefaultGrid; - } return mCurrentGrid; } diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 9562d563f..e5600dfa9 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -57,7 +57,7 @@ public: const std::string& getVoiceConnector() const { return mVoiceConnector; } std::string getSearchUrl(SearchType ty, bool is_web) const; bool isRenderCompat() const { return mRenderCompat; } - std::string getGridNick(); + std::string getGridNick() const; int getMaxAgentGroups() const { return mMaxAgentGroups; } const std::string& getCurrencySymbol() const { return mCurrencySymbol; } @@ -99,7 +99,7 @@ public: bool retrieveGridInfo(); static const char* getPlatformString(Platform platform); - static std::string sanitizeGridNick(std::string &gridnick); + static std::string sanitizeGridNick(const std::string &gridnick); static HippoGridInfo FALLBACK_GRIDINFO; static void initFallback(); @@ -163,8 +163,10 @@ public: HippoGridInfo* getConnectedGrid() const { return mConnectedGrid ? mConnectedGrid : getCurrentGrid(); } HippoGridInfo* getCurrentGrid() const; - const std::string& getDefaultGridNick() const; - const std::string& getCurrentGridNick() const; + std::string getDefaultGridNick() const; + std::string getCurrentGridNick() const; + const std::string& getDefaultGridName() const; + const std::string& getCurrentGridName() const; void setDefaultGrid(const std::string& grid); void setCurrentGrid(const std::string& grid); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 0dc6f669f..1bbb0416b 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -130,7 +130,8 @@ static std::string nameJoin(const std::string& first,const std::string& last, bo } static std::string getDisplayString(const std::string& first, const std::string& last, const std::string& grid, bool is_secondlife) { - if(grid == gHippoGridManager->getDefaultGridNick()) + //grid comes via LLSavedLoginEntry, which uses full grid names, not nicks + if(grid == gHippoGridManager->getDefaultGridName()) return nameJoin(first, last, is_secondlife); else return nameJoin(first, last, is_secondlife) + " (" + grid + ")"; @@ -633,7 +634,9 @@ void LLPanelLogin::setFields(const LLSavedLoginEntry& entry, bool takeFocus) //sInstance->childSetText("name_combo", fullname); std::string grid = entry.getGrid(); - if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridNick()) { + //grid comes via LLSavedLoginEntry, which uses full grid names, not nicks + if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridName()) + { gHippoGridManager->setCurrentGrid(grid); LLPanelLogin::refreshLoginPage(); } @@ -791,27 +794,34 @@ void LLPanelLogin::setAlwaysRefresh(bool refresh) void LLPanelLogin::updateGridCombo() { - const std::string &defaultGrid = gHippoGridManager->getDefaultGridNick(); - const std::string ¤tGrid = gHippoGridManager->getCurrentGridNick(); + const std::string &defaultGrid = gHippoGridManager->getDefaultGridName(); + LLComboBox *grids = getChild("grids_combo"); - S32 selectIndex = -1, i = 0; + std::string top_entry; + grids->removeall(); - if (defaultGrid != "") { - grids->add(defaultGrid); - selectIndex = i++; - } + + const HippoGridInfo *curGrid = gHippoGridManager->getCurrentGrid(); + const HippoGridInfo *defGrid = gHippoGridManager->getGrid(defaultGrid); + HippoGridManager::GridIterator it, end = gHippoGridManager->endGrid(); - for (it = gHippoGridManager->beginGrid(); it != end; ++it) { + for (it = gHippoGridManager->beginGrid(); it != end; ++it) + { std::string grid = it->second->getGridName(); - if (grid != defaultGrid) { - grids->add(grid); - if (grid == currentGrid) selectIndex = i; - i++; - } + if(grid.empty() || it->second == defGrid || it->second == curGrid) + continue; + grids->add(grid,it->second->getGridNick()); } - if (selectIndex >= 0) { - grids->setCurrentByIndex(selectIndex); - } else { + if(curGrid || defGrid) + { + if(defGrid) + grids->add(defGrid->getGridName(),defGrid->getGridNick(),ADD_TOP); + if(curGrid && defGrid != curGrid) + grids->add(curGrid->getGridName(),curGrid->getGridNick(),ADD_TOP); + grids->setCurrentByIndex(0); + } + else + { grids->setLabel(LLStringExplicit("")); // LLComboBox::removeall() does not clear the label } } diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 69f731467..5bfba42a8 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -53,11 +53,14 @@ const char* LLSLURL::SLURL_REGION_PATH = "region"; const char* LLSLURL::SIM_LOCATION_HOME = "home"; const char* LLSLURL::SIM_LOCATION_LAST = "last"; - const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; -#define MAINGRID "util.agni.lindenlab.com" +const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; +const char* DEFAULT_SLURL_BASE = "https://%s/region/"; +const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; + +#define MAINGRID "secondlife" // resolve a simstring from a slurl LLSLURL::LLSLURL(const std::string& slurl) { @@ -87,7 +90,19 @@ LLSLURL::LLSLURL(const std::string& slurl) // where the user can type in /// //std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. - std::string fixed_slurl = MAIN_GRID_SLURL_BASE; + std::string fixed_slurl; + + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + { + if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) + fixed_slurl = MAIN_GRID_SLURL_BASE; + else + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + } + else + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + + //std::string fixed_slurl = MAIN_GRID_SLURL_BASE; // the slurl that was passed in might have a prepended /, or not. So, // we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife//// @@ -147,7 +162,15 @@ LLSLURL::LLSLURL(const std::string& slurl) // so parse the grid name to derive the grid ID if (!slurl_uri.hostName().empty()) { - mGrid = gHippoGridManager->getGrid(slurl_uri.hostName())->getGridNick(); + if(slurl_uri.hostName() == "util.agni.lindenlab.com") + mGrid = MAINGRID; + else if(slurl_uri.hostName() == "util.aditi.lindenlab.com") + mGrid = "secondlife_beta"; + else + { + HippoGridInfo* grid = gHippoGridManager->getGrid(slurl_uri.hostName()); + mGrid = grid ? grid->getGridNick() : gHippoGridManager->getDefaultGridNick(); + } } else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) { @@ -353,8 +376,8 @@ LLSLURL::LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position) { - HippoGridInfo* target_grid = gHippoGridManager->getGrid(grid); - *this = LLSLURL((target_grid ? target_grid->getGridNick() : ""), + HippoGridInfo* gridp = gHippoGridManager->getGrid(grid); + *this = LLSLURL(gridp ? gridp->getGridNick() : gHippoGridManager->getDefaultGridNick(), region, LLVector3(global_position.mdV[VX], global_position.mdV[VY], global_position.mdV[VZ])); @@ -394,7 +417,17 @@ std::string LLSLURL::getSLURLString() const S32 z = llround( (F32)mPosition[VZ] ); //return LLGridManager::getInstance()->getSLURLBase(mGrid) + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. - return MAIN_GRID_SLURL_BASE + + std::string fixed_slurl; + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + { + if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) + fixed_slurl = MAIN_GRID_SLURL_BASE; + else + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + } + else + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + return fixed_slurl + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); } case APP: @@ -402,7 +435,10 @@ std::string LLSLURL::getSLURLString() const std::ostringstream app_url; //app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; //Singu TODO: Implement LLHippoGridMgr::getAppSLURLBase some day. For now it's hardcoded. - app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; + else + app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick()) << "/" << mAppCmd; for(LLSD::array_const_iterator i = mAppPath.beginArray(); i != mAppPath.endArray(); i++) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index ee7673022..44ba523b8 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3918,11 +3918,11 @@ bool process_login_success_response(std::string& password) std::string history_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "saved_logins_sg2.xml"); LLSavedLogins history_data = LLSavedLogins::loadFile(history_file); - std::string grid_nick = gHippoGridManager->getConnectedGrid()->getGridName(); - history_data.deleteEntry(firstname, lastname, grid_nick); + std::string grid_name = gHippoGridManager->getConnectedGrid()->getGridName(); + history_data.deleteEntry(firstname, lastname, grid_name); if (gSavedSettings.getBOOL("RememberLogin")) { - LLSavedLoginEntry login_entry(firstname, lastname, password, grid_nick); + LLSavedLoginEntry login_entry(firstname, lastname, password, grid_name); history_data.addEntry(login_entry); } else diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 2a614f4c7..dcf955cef 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -216,9 +216,9 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& { LLSD args; args["SLURL"] = slurl.getLocationString(); - args["CURRENT_GRID"] = gHippoGridManager->getCurrentGridNick(); + args["CURRENT_GRID"] = gHippoGridManager->getCurrentGrid()->getGridName(); - std::string grid_label = new_grid ? new_grid->getGridNick() : ""; + std::string grid_label = new_grid ? new_grid->getGridName() : ""; if(!grid_label.empty()) { @@ -226,7 +226,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& } else { - args["GRID"] = slurl.getGrid(); + args["GRID"] = slurl.getGrid() + " (Unrecognized)"; } LLNotificationsUtil::add("CantTeleportToGrid", args); return; @@ -255,8 +255,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& { url_displayp->setSnapshotDisplay(snapshot_id); } - LLVector3 pos = slurl.getPosition(); - std::string locationString = llformat("%s %d, %d, %d", slurl.getRegion().c_str(), local_pos.mV[VX],local_pos.mV[VY],local_pos.mV[VZ]); + std::string locationString = llformat("%s %i, %i, %i", slurl.getRegion().c_str(), (S32)local_pos.mV[VX],(S32)local_pos.mV[VY],(S32)local_pos.mV[VZ]); url_displayp->setLocationString(locationString); } } From 10f61f6f00e4c0ce6dd5fbfb9ff065238e2930e2 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 16 Jun 2013 22:45:48 -0500 Subject: [PATCH 23/36] Old habits die hard. Fix gcc compile. --- indra/newview/llslurl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 5bfba42a8..b1a30f78b 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -97,10 +97,10 @@ LLSLURL::LLSLURL(const std::string& slurl) if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) fixed_slurl = MAIN_GRID_SLURL_BASE; else - fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); } else - fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); //std::string fixed_slurl = MAIN_GRID_SLURL_BASE; @@ -423,10 +423,10 @@ std::string LLSLURL::getSLURLString() const if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) fixed_slurl = MAIN_GRID_SLURL_BASE; else - fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); } else - fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick()); + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); return fixed_slurl + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); } @@ -438,7 +438,7 @@ std::string LLSLURL::getSLURLString() const if(gHippoGridManager->getCurrentGrid()->isSecondLife()) app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; else - app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick()) << "/" << mAppCmd; + app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()) << "/" << mAppCmd; for(LLSD::array_const_iterator i = mAppPath.beginArray(); i != mAppPath.endArray(); i++) From 884c67e6411f00ccf3186322110cb3dc0d7dbdc6 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 16 Jun 2013 22:48:50 -0500 Subject: [PATCH 24/36] Added LLViewDrawContext. Implemented in a few select ui elements. Just enough to support the panel prim media controller fading out all fancy-like. --- indra/llui/llbutton.cpp | 2 +- indra/llui/lliconctrl.cpp | 3 +- indra/llui/lllineeditor.cpp | 20 ++++++++------ indra/llui/lltextbox.cpp | 11 ++++---- indra/llui/llview.cpp | 17 ++++++++++++ indra/llui/llview.h | 32 ++++++++++++++++++++++ indra/llui/llviewborder.cpp | 5 ++++ indra/newview/llpanelprimmediacontrols.cpp | 4 +-- 8 files changed, 77 insertions(+), 17 deletions(-) diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 888dc2e6e..daa7de213 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -544,7 +544,7 @@ void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height) // virtual void LLButton::draw() { - F32 alpha = mAlpha; + F32 alpha = mAlpha * getDrawContext().mAlpha; bool flash = FALSE; if( mFlashing ) { diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 1e4a5dcec..889878af1 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -106,7 +106,8 @@ void LLIconCtrl::draw() { if( mImagep.notNull() ) { - mImagep->draw(getLocalRect(), mColor ); + const F32 alpha = getDrawContext().mAlpha; + mImagep->draw(getLocalRect(), mColor % alpha ); } LLUICtrl::draw(); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 811de99a9..0e6a22e3e 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1702,6 +1702,7 @@ void LLLineEditor::drawMisspelled(LLRect background) if (glggHunSpell->getSpellCheckHighlight()) { + F32 alpha = getDrawContext().mAlpha; for (int i =0; i<(int)misspellLocations.size(); i++) { S32 wstart =findPixelNearestPos( misspellLocations[i]-getCursor()); @@ -1716,7 +1717,7 @@ void LLLineEditor::drawMisspelled(LLRect background) { wstart = maxw; } - gGL.color4ub(255,0,0,200); + gGL.color4ub(255,0,0,200*alpha); //3 line zig zags.. while (wstart < wend) { @@ -1733,6 +1734,7 @@ void LLLineEditor::drawMisspelled(LLRect background) void LLLineEditor::draw() { + F32 alpha = getDrawContext().mAlpha; S32 text_len = mText.length(); std::string saved_text; @@ -1776,7 +1778,7 @@ void LLLineEditor::draw() bg_color = mWriteableBgColor; } } - gl_rect_2d(background, bg_color); + gl_rect_2d(background, bg_color % alpha); } #endif @@ -1801,7 +1803,9 @@ void LLLineEditor::draw() { text_color = mReadOnlyFgColor; } - LLColor4 label_color = mTentativeFgColor; + + text_color %= alpha; + LLColor4 label_color = mTentativeFgColor % alpha; if (hasPreeditString()) { @@ -1824,7 +1828,7 @@ void LLLineEditor::draw() background.mBottom + PREEDIT_STANDOUT_POSITION, preedit_pixels_right - PREEDIT_STANDOUT_GAP - 1, background.mBottom + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS, - (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f)); + (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(alpha)); } else { @@ -1832,7 +1836,7 @@ void LLLineEditor::draw() background.mBottom + PREEDIT_MARKER_POSITION, preedit_pixels_right - PREEDIT_MARKER_GAP - 1, background.mBottom + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS, - (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f)); + (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(alpha)); } } } @@ -1874,7 +1878,7 @@ void LLLineEditor::draw() if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) ) { - LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f); + LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], alpha ); // selected middle S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); width = llmin(width, mMaxHPixels - llround(rendered_pixels_right)); @@ -1883,7 +1887,7 @@ void LLLineEditor::draw() rendered_text += mGLFont->render( mText, mScrollHPos + rendered_text, rendered_pixels_right, text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, @@ -1953,7 +1957,7 @@ void LLLineEditor::draw() if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) { mGLFont->render(mText, getCursor(), (F32)(cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS / 2), text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 938676225..799441a99 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -286,6 +286,7 @@ BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text void LLTextBox::draw() { + F32 alpha = getDrawContext().mAlpha; if (mBorderVisible) { gl_rect_2d_offset_local(getLocalRect(), 2, FALSE); @@ -296,12 +297,12 @@ void LLTextBox::draw() static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow"); static S32 drop_shadow_tooltip = LLUI::sConfigGroup->getS32("DropShadowTooltip"); gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow, drop_shadow_tooltip); + color_drop_shadow % alpha, drop_shadow_tooltip); } if (mBackgroundVisible) { - gl_rect_2d( getLocalRect(), mBackgroundColor ); + gl_rect_2d( getLocalRect(), mBackgroundColor % alpha ); } S32 text_x = 0; @@ -324,16 +325,16 @@ void LLTextBox::draw() { if(mHasHover) { - drawText( text_x, text_y, mHoverColor ); + drawText( text_x, text_y, mHoverColor % alpha ); } else { - drawText( text_x, text_y, mTextColor ); + drawText( text_x, text_y, mTextColor % alpha ); } } else { - drawText( text_x, text_y, mDisabledColor ); + drawText( text_x, text_y, mDisabledColor % alpha ); } if (sDebugRects) diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 9f7cfa092..6d2ae643a 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -74,6 +74,7 @@ BOOL LLView::sForceReshape = FALSE; LLView* LLView::sEditingUIView = NULL; S32 LLView::sLastLeftXML = S32_MIN; S32 LLView::sLastBottomXML = S32_MIN; +std::vector LLViewDrawContext::sDrawContextStack; LLView::DrilldownFunc LLView::sDrilldown = boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); @@ -3014,6 +3015,22 @@ bool LLView::notifyChildren(const LLSD& info) return ret; } +// convenient accessor for draw context +const LLViewDrawContext& LLView::getDrawContext() +{ + return LLViewDrawContext::getCurrentContext(); +} + +const LLViewDrawContext& LLViewDrawContext::getCurrentContext() +{ + static LLViewDrawContext default_context; + + if (sDrawContextStack.empty()) + return default_context; + + return *sDrawContextStack.back(); +} + LLView* LLView::createWidget(LLXMLNodePtr xml_node) const { // forward requests to ui ctrl factory diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 9bc969ea6..b009bab41 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -131,6 +131,35 @@ public: } }; +// maintains render state during traversal of UI tree +class LLViewDrawContext +{ +public: + F32 mAlpha; + + LLViewDrawContext(F32 alpha = 1.f) + : mAlpha(alpha) + { + if (!sDrawContextStack.empty()) + { + LLViewDrawContext* context_top = sDrawContextStack.back(); + // merge with top of stack + mAlpha *= context_top->mAlpha; + } + sDrawContextStack.push_back(this); + } + + ~LLViewDrawContext() + { + sDrawContextStack.pop_back(); + } + + static const LLViewDrawContext& getCurrentContext(); + +private: + static std::vector sDrawContextStack; +}; + class LLView : public LLMouseHandler, // handles mouse events public LLFocusableElement, // handles keyboard events @@ -635,6 +664,9 @@ public: //send custom notification to current view virtual S32 notify(const LLSD& info) { return 0;}; + + static const LLViewDrawContext& getDrawContext(); + protected: void drawDebugRect(); void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE); diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index cd2987fe8..2fdd7dac0 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -104,6 +104,8 @@ void LLViewBorder::draw() void LLViewBorder::drawOnePixelLines() { + F32 alpha = getDrawContext().mAlpha; + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLColor4 top_color = mHighlightLight; @@ -138,6 +140,9 @@ void LLViewBorder::drawOnePixelLines() S32 right = getRect().getWidth(); S32 bottom = 0; + top_color %= alpha; + bottom_color %= alpha; + gGL.color4fv( top_color.mV ); gl_line_2d(left, bottom, left, top); gl_line_2d(left, top, right, top); diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index b57c3fdcb..3ebc38710 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -730,7 +730,7 @@ void LLPanelPrimMediaControls::draw() } } - F32 alpha = 1.f; + F32 alpha = getDrawContext().mAlpha; if(mHideImmediately) { //hide this panel @@ -781,7 +781,7 @@ void LLPanelPrimMediaControls::draw() } { - //LLViewDrawContext context(alpha); + LLViewDrawContext context(alpha); LLPanel::draw(); } } From 49cbc80ee0304ba63bb86490a962ea3b62edf9f4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 18 Jun 2013 08:09:17 -0500 Subject: [PATCH 25/36] Added 'NearbyMedia' floater (accessable via new button in Media Remote popup) -Floater utilizes a slightly modified LLPanelNearbyMedia, which has fading and the 'minimal' mode disabled when its parent is a floater. -Due to how LL designed LLPanelNearbyMedia, only one instance should ever be allowed at a time. Added LLNameBox. To avoid having dupilcate code, logic in LLTextBox::fromXML had to be moved to LLTextBox::initFromXML. -Perfomed some additional cleanup. 'name' attribute now parsed in LLUICtrl::initFromXML, or as-needed for elements not based off of LLUICtrl. LLSlider now respects current DrawContext Fixed the AO toolbar widget reserving too much width. Made the "wlfAdvSettingsPopup" setting persist, and un-inverted its logic. Altered ui initilization order. Toolbar/overlay panels now constructed further into login process. --- indra/llui/llcheckboxctrl.cpp | 5 +- indra/llui/llcombobox.cpp | 10 +- indra/llui/llfiltereditor.cpp | 5 +- indra/llui/lliconctrl.cpp | 5 +- indra/llui/lllayoutstack.cpp | 8 - indra/llui/lllineeditor.cpp | 5 +- indra/llui/llmenugl.cpp | 5 +- indra/llui/llmultislider.cpp | 5 +- indra/llui/llmultisliderctrl.cpp | 5 +- indra/llui/llradiogroup.cpp | 9 +- indra/llui/llscrollingpanellist.cpp | 5 +- indra/llui/llscrolllistctrl.cpp | 5 +- indra/llui/llsearcheditor.cpp | 5 +- indra/llui/llslider.cpp | 47 +- indra/llui/llslider.h | 6 +- indra/llui/llsliderctrl.cpp | 5 +- indra/llui/llspinctrl.cpp | 5 +- indra/llui/lltextbox.cpp | 125 +- indra/llui/lltextbox.h | 1 + indra/llui/lltexteditor.cpp | 5 +- indra/llui/lluictrl.cpp | 4 + indra/llui/lluictrlfactory.cpp | 5 +- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 32 +- indra/newview/llcolorswatch.cpp | 5 +- indra/newview/llfloaterpreference.h | 1 + indra/newview/lljoystickbutton.cpp | 10 +- indra/newview/llnamebox.cpp | 71 +- indra/newview/llnamebox.h | 15 +- indra/newview/llnameeditor.cpp | 5 +- indra/newview/llnamelistctrl.cpp | 5 +- indra/newview/lloverlaybar.cpp | 26 +- indra/newview/lloverlaybar.h | 2 +- indra/newview/llpanelgroupgeneral.cpp | 9 +- .../llpanelmediasettingspermissions.cpp | 8 +- indra/newview/llpanelnearbymedia.cpp | 1313 +++++++++++++++++ indra/newview/llpanelnearbymedia.h | 198 +++ indra/newview/llstartup.cpp | 2 - indra/newview/lltexturectrl.cpp | 5 +- .../newview/llviewermedia_streamingaudio.cpp | 32 +- indra/newview/llviewerparcelmediaautoplay.cpp | 37 +- indra/newview/llviewerparcelmediaautoplay.h | 2 + indra/newview/llviewertexteditor.cpp | 5 +- indra/newview/llviewerwindow.cpp | 40 +- .../xui/en-us/floater_nearby_media.xml | 20 + .../skins/default/xui/en-us/floater_tools.xml | 15 +- .../default/xui/en-us/floater_web_content.xml | 21 +- .../skins/default/xui/en-us/panel_audio.xml | 3 +- .../default/xui/en-us/panel_group_general.xml | 7 +- .../xui/en-us/panel_media_remote_expanded.xml | 2 +- .../panel_media_settings_permissions.xml | 4 +- .../default/xui/en-us/panel_nearby_media.xml | 382 +++++ .../default/xui/en-us/panel_overlaybar.xml | 2 +- .../xui/en-us/panel_prim_media_controls.xml | 23 +- .../skins/default/xui/es/floater_tools.xml | 4 +- .../skins/default/xui/es/panel_audio.xml | 1 + .../xui/es/panel_media_settings_general.xml | 33 + .../es/panel_media_settings_permissions.xml | 29 + .../xui/es/panel_media_settings_security.xml | 15 + .../default/xui/es/panel_nearby_media.xml | 71 + .../skins/default/xui/fr/floater_tools.xml | 4 +- .../skins/default/xui/fr/panel_audio.xml | 1 + .../xui/fr/panel_media_settings_general.xml | 32 + .../fr/panel_media_settings_permissions.xml | 29 + .../xui/fr/panel_media_settings_security.xml | 16 + .../default/xui/fr/panel_nearby_media.xml | 71 + .../skins/default/xui/pt/floater_tools.xml | 4 +- .../skins/default/xui/pt/panel_audio.xml | 17 +- .../xui/pt/panel_media_settings_general.xml | 32 + .../pt/panel_media_settings_permissions.xml | 29 + .../xui/pt/panel_media_settings_security.xml | 15 + .../default/xui/pt/panel_nearby_media.xml | 71 + indra/newview/wlfPanel_AdvSettings.cpp | 6 +- 73 files changed, 2750 insertions(+), 314 deletions(-) create mode 100644 indra/newview/llpanelnearbymedia.cpp create mode 100644 indra/newview/llpanelnearbymedia.h create mode 100644 indra/newview/skins/default/xui/en-us/floater_nearby_media.xml create mode 100644 indra/newview/skins/default/xui/en-us/panel_nearby_media.xml create mode 100644 indra/newview/skins/default/xui/es/panel_media_settings_general.xml create mode 100644 indra/newview/skins/default/xui/es/panel_media_settings_permissions.xml create mode 100644 indra/newview/skins/default/xui/es/panel_media_settings_security.xml create mode 100644 indra/newview/skins/default/xui/es/panel_nearby_media.xml create mode 100644 indra/newview/skins/default/xui/fr/panel_media_settings_general.xml create mode 100644 indra/newview/skins/default/xui/fr/panel_media_settings_permissions.xml create mode 100644 indra/newview/skins/default/xui/fr/panel_media_settings_security.xml create mode 100644 indra/newview/skins/default/xui/fr/panel_nearby_media.xml create mode 100644 indra/newview/skins/default/xui/pt/panel_media_settings_general.xml create mode 100644 indra/newview/skins/default/xui/pt/panel_media_settings_permissions.xml create mode 100644 indra/newview/skins/default/xui/pt/panel_media_settings_security.xml create mode 100644 indra/newview/skins/default/xui/pt/panel_nearby_media.xml diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index ad0fac5df..ada81b639 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -307,9 +307,6 @@ LLXMLNodePtr LLCheckBoxCtrl::getXML(bool save_children) const // static LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("checkbox"); - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); @@ -326,7 +323,7 @@ LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto LLRect rect; createRect(node, rect, parent, LLRect()); - LLCheckBoxCtrl* checkbox = new LLCheckboxCtrl(name, + LLCheckBoxCtrl* checkbox = new LLCheckboxCtrl("checkbox", rect, label, font, diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index bdd0fe4a8..30a10c39a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -149,9 +149,6 @@ LLXMLNodePtr LLComboBox::getXML(bool save_children) const // static LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("combo_box"); - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); @@ -164,7 +161,7 @@ LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * S32 max_chars = 20; node->getAttributeS32("max_chars", max_chars); - LLComboBox* combo_box = new LLComboBox(name, + LLComboBox* combo_box = new LLComboBox("combo_box", rect, label); combo_box->setAllowTextEntry(allow_text_entry, max_chars); @@ -1245,16 +1242,13 @@ LLXMLNodePtr LLFlyoutButton::getXML(bool save_children) const //static LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name = "flyout_button"; - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); LLRect rect; createRect(node, rect, parent, LLRect()); - LLFlyoutButton* flyout_button = new LLFlyoutButton(name, + LLFlyoutButton* flyout_button = new LLFlyoutButton("flyout_button", rect, label); diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp index fa05c03d7..12e1c2d52 100644 --- a/indra/llui/llfiltereditor.cpp +++ b/indra/llui/llfiltereditor.cpp @@ -52,9 +52,6 @@ void LLFilterEditor::handleKeystroke() // static LLView* LLFilterEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("filter_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -63,7 +60,7 @@ LLView* LLFilterEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto std::string text = node->getValue().substr(0, max_text_length - 1); - LLFilterEditor* search_editor = new LLFilterEditor(name, + LLFilterEditor* search_editor = new LLFilterEditor("filter_editor", rect, max_text_length); diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 889878af1..90b313d45 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -158,9 +158,6 @@ LLXMLNodePtr LLIconCtrl::getXML(bool save_children) const LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("icon"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -173,7 +170,7 @@ LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * LLColor4 color(LLColor4::white); LLUICtrlFactory::getAttributeColor(node,"color", color); - LLIconCtrl* icon = new LLIconCtrl(name, rect, image_name); + LLIconCtrl* icon = new LLIconCtrl("icon", rect, image_name); icon->setColor(color); diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 46e60bd3e..e939cd411 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -976,8 +976,6 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor layout_stackp->setName(name); layout_stackp->initFromXML(node, parent); - llinfos << "Created layout stack '"<getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -2584,7 +2581,7 @@ LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory S32 border_thickness = 1; node->getAttributeS32("border_thickness", border_thickness); - LLLineEditor* line_editor = new LLLineEditor(name, + LLLineEditor* line_editor = new LLLineEditor("line_editor", rect, text, font, diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 8b16476ed..33e39bc60 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -4150,13 +4150,10 @@ LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("menu"); - node->getAttributeString("name", name); - BOOL opaque = FALSE; node->getAttributeBOOL("opaque", opaque); - LLMenuBarGL *menubar = new LLMenuBarGL(name); + LLMenuBarGL *menubar = new LLMenuBarGL("menu"); LLHandle parent_handle; LLFloater* parent_floater = dynamic_cast(parent); diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index f0b140b40..183a1245f 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -624,9 +624,6 @@ LLXMLNodePtr LLMultiSlider::getXML(bool save_children) const //static LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("multi_slider_bar"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -654,7 +651,7 @@ LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor BOOL use_triangle = FALSE; node->getAttributeBOOL("use_triangle", use_triangle); - LLMultiSlider* multiSlider = new LLMultiSlider(name, + LLMultiSlider* multiSlider = new LLMultiSlider("multi_slider_bar", rect, NULL, initial_value, diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 1cd91c3a4..06de6f937 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -472,9 +472,6 @@ LLXMLNodePtr LLMultiSliderCtrl::getXML(bool save_children) const LLView* LLMultiSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("multi_slider"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -558,7 +555,7 @@ LLView* LLMultiSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFa label.assign(node->getTextContents()); } - LLMultiSliderCtrl* slider = new LLMultiSliderCtrl(name, + LLMultiSliderCtrl* slider = new LLMultiSliderCtrl("multi_slider", rect, label, font, diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index ce01f75bf..c592fccfb 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -352,9 +352,6 @@ LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const // static LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("radio_group"); - node->getAttributeString("name", name); - U32 initial_value = 0; node->getAttributeU32("initial_value", initial_value); @@ -364,7 +361,7 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLRect rect; createRect(node, rect, parent, LLRect()); - LLRadioGroup* radio_group = new LLRadioGroup(name, + LLRadioGroup* radio_group = new LLRadioGroup("radio_group", rect, initial_value, NULL, @@ -408,10 +405,8 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLRect item_rect; createRect(child, item_rect, radio_group, rect); - std::string radioname("radio"); - child->getAttributeString("name", radioname); std::string item_label = child->getTextContents(); - LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label, item_rect, font); + LLRadioCtrl* radio = radio_group->addRadioButton("radio", item_label, item_rect, font); radio->initFromXML(child, radio_group); } diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index 8aa8b9fe3..c5d1ad6ed 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -152,13 +152,10 @@ LLXMLNodePtr LLScrollingPanelList::getXML(bool save_children) const // static LLView* LLScrollingPanelList::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("scrolling_panel_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); - LLScrollingPanelList* scrolling_panel_list = new LLScrollingPanelList(name, rect); + LLScrollingPanelList* scrolling_panel_list = new LLScrollingPanelList("scrolling_panel_list", rect); scrolling_panel_list->initFromXML(node, parent); return scrolling_panel_list; } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index a5bbc9b90..5a5bc1d41 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -3104,9 +3104,6 @@ void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node) // static LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("scroll_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -3132,7 +3129,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac node->getAttributeBOOL("mouse_wheel_opaque", mouse_wheel_opaque); LLScrollListCtrl* scroll_list = new LLScrollListCtrl( - name, + "scroll_list", rect, NULL, multi_select, diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index a326803e4..6ccfed88a 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -147,9 +147,6 @@ void LLSearchEditor::handleKeystroke() // static LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("search_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -158,7 +155,7 @@ LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto std::string text = node->getValue().substr(0, max_text_length - 1); - LLSearchEditor* search_editor = new LLSearchEditor(name, + LLSearchEditor* search_editor = new LLSearchEditor("search_editor", rect, max_text_length); diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index becc33424..893354252 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -250,6 +250,8 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) void LLSlider::draw() { + F32 alpha = getDrawContext().mAlpha; + // since thumb image might still be decoding, need thumb to accomodate image size updateThumbRect(); @@ -258,31 +260,47 @@ void LLSlider::draw() // drawing solids requires texturing be disabled gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - F32 opacity = getEnabled() ? 1.f : 0.3f; - LLColor4 center_color = (mThumbCenterColor % opacity); - // Track LLRect track_rect(mThumbImage->getWidth() / 2, getLocalRect().getCenterY() + (mTrackImage->getHeight() / 2), getRect().getWidth() - mThumbImage->getWidth() / 2, getLocalRect().getCenterY() - (mTrackImage->getHeight() / 2) ); LLRect highlight_rect(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom); - mTrackImage->draw(track_rect); - mTrackHighlightImage->draw(highlight_rect); + mTrackImage->draw(track_rect, LLColor4::white % alpha); + mTrackHighlightImage->draw(highlight_rect, LLColor4::white % alpha); // Thumb - if( hasMouseCapture() ) - { - // Show ghost where thumb was before dragging began. - mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor % 0.3f); - } if (hasFocus()) { // Draw focus highlighting. - mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); + mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth()); + } + + if( hasMouseCapture() ) + { + // Show ghost where thumb was before dragging began. + if (mThumbImage.notNull()) + { + mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor % (0.3f * alpha)); + mThumbImage->draw(mThumbRect, mThumbOutlineColor % alpha); + } + } + else if(!getEnabled()) + { + if (mThumbImage.notNull()) + { + mThumbImage->draw(mThumbRect, mThumbCenterColor % (0.3f * alpha)); + } + } + else + { + if (mThumbImage.notNull()) + { + mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha); + } } // Fill in the thumb. - mThumbImage->draw(mThumbRect, hasMouseCapture() ? mThumbOutlineColor : center_color); + LLUICtrl::draw(); } @@ -325,9 +343,6 @@ boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t: //static LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory) { - std::string name("slider_bar"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -346,7 +361,7 @@ LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFacto BOOL volume = node->hasName("volume_slider") ? TRUE : FALSE; node->getAttributeBOOL("volume", volume); - LLSlider* slider = new LLSlider(name, + LLSlider* slider = new LLSlider("slider_bar", rect, NULL, initial_value, diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 8d16911f6..59bda673f 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -96,9 +96,9 @@ private: S32 mMouseOffset; LLRect mDragStartThumbRect; - LLUIImage* mThumbImage; - LLUIImage* mTrackImage; - LLUIImage* mTrackHighlightImage; + LLPointer mThumbImage; + LLPointer mTrackImage; + LLPointer mTrackHighlightImage; LLRect mThumbRect; LLColor4 mTrackColor; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 53f54d75c..42c72a2f5 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -403,9 +403,6 @@ LLXMLNodePtr LLSliderCtrl::getXML(bool save_children) const LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("slider"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -480,7 +477,7 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory label.assign(node->getTextContents()); } - LLSliderCtrl* slider = new LLSliderCtrl(name, + LLSliderCtrl* slider = new LLSliderCtrl("slider", rect, label, font, diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index a7950ccbf..37244e640 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -519,9 +519,6 @@ LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("spinner"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -556,7 +553,7 @@ LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * label.assign( node->getValue() ); } - LLSpinCtrl* spinner = new LLSpinCtrl(name, + LLSpinCtrl* spinner = new LLSpinCtrl("spinner", rect, label, font, diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 799441a99..239ac5b74 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -387,6 +387,69 @@ void LLTextBox::reshapeToFitText() reshape( width + 2 * mHPad, height + 2 * mVPad ); } +// virtual +void LLTextBox::initFromXML(LLXMLNodePtr node, LLView* parent) +{ + LLUICtrl::initFromXML(node, parent); + + LLFontGL* font = LLView::selectFont(node); + if(font) + mFontGL = font; + + setText(node->getTextContents()); + + LLFontGL::HAlign halign = LLView::selectFontHAlign(node); + setHAlign(halign); + + node->getAttributeS32("line_spacing", mLineSpacing); + + std::string font_style; + if (node->getAttributeString("font-style", font_style)) + { + mFontStyle = LLFontGL::getStyleFromString(font_style); + } + + if (node->getAttributeString("font-shadow", font_style)) + { + if(font_style == "soft") + mFontShadow = LLFontGL::DROP_SHADOW_SOFT; + else if(font_style == "hard") + mFontShadow = LLFontGL::DROP_SHADOW; + else + mFontShadow = LLFontGL::NO_SHADOW; + } + + BOOL mouse_opaque = getMouseOpaque(); + if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) + { + setMouseOpaque(mouse_opaque); + } + + if(node->hasAttribute("text_color")) + { + LLColor4 color; + LLUICtrlFactory::getAttributeColor(node, "text_color", color); + setColor(color); + } + + if(node->hasAttribute("hover_color")) + { + LLColor4 color; + LLUICtrlFactory::getAttributeColor(node, "hover_color", color); + setHoverColor(color); + setHoverActive(true); + } + + BOOL hover_active = FALSE; + if(node->getAttributeBOOL("hover", hover_active)) + { + setHoverActive(hover_active); + } + + node->getAttributeBOOL("use_ellipses", mUseEllipses); + +} + // virtual LLXMLNodePtr LLTextBox::getXML(bool save_children) const { @@ -416,68 +479,8 @@ LLXMLNodePtr LLTextBox::getXML(bool save_children) const // static LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_box"); - node->getAttributeString("name", name); - LLFontGL* font = LLView::selectFont(node); - - std::string text = node->getTextContents(); - - LLTextBox* text_box = new LLTextBox(name, - LLRect(), - text, - font, - FALSE); - - - LLFontGL::HAlign halign = LLView::selectFontHAlign(node); - text_box->setHAlign(halign); - + LLTextBox* text_box = new LLTextBox("text_box", LLRect(), "" , NULL, FALSE); text_box->initFromXML(node, parent); - node->getAttributeS32("line_spacing", text_box->mLineSpacing); - - std::string font_style; - if (node->getAttributeString("font-style", font_style)) - { - text_box->mFontStyle = LLFontGL::getStyleFromString(font_style); - } - - if (node->getAttributeString("font-shadow", font_style)) - { - if(font_style == "soft") - text_box->mFontShadow = LLFontGL::DROP_SHADOW_SOFT; - else if(font_style == "hard") - text_box->mFontShadow = LLFontGL::DROP_SHADOW; - else - text_box->mFontShadow = LLFontGL::NO_SHADOW; - } - - BOOL mouse_opaque = text_box->getMouseOpaque(); - if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) - { - text_box->setMouseOpaque(mouse_opaque); - } - - if(node->hasAttribute("text_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "text_color", color); - text_box->setColor(color); - } - - if(node->hasAttribute("hover_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "hover_color", color); - text_box->setHoverColor(color); - text_box->setHoverActive(true); - } - - BOOL hover_active = FALSE; - if(node->getAttributeBOOL("hover", hover_active)) - { - text_box->setHoverActive(hover_active); - } - return text_box; } diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index de3e0c6e5..ce9f71aff 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -63,6 +63,7 @@ public: virtual ~LLTextBox() {} + virtual void initFromXML(LLXMLNodePtr node, LLView* parent); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 09d41c589..e6049f843 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4748,9 +4748,6 @@ LLXMLNodePtr LLTextEditor::getXML(bool save_children) const // static LLView* LLTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -4764,7 +4761,7 @@ LLView* LLTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory std::string text = node->getTextContents().substr(0, max_text_length - 1); - LLTextEditor* text_editor = new LLTextEditor(name, + LLTextEditor* text_editor = new LLTextEditor("text_editor", rect, max_text_length, text, diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 625aa7eab..803cb7f0f 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -485,6 +485,10 @@ BOOL LLUICtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect void LLUICtrl::initFromXML(LLXMLNodePtr node, LLView* parent) { + std::string name; + if(node->getAttributeString("name", name)) + setName(name); + BOOL has_tab_stop = hasTabStop(); node->getAttributeBOOL("tab_stop", has_tab_stop); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 126a8ba46..d5bb963d4 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -102,11 +102,8 @@ public: static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("pad"); - node->getAttributeString("name", name); - LLUICtrlLocate *new_ctrl = new LLUICtrlLocate(); - new_ctrl->setName(name); + new_ctrl->setName("pad"); new_ctrl->initFromXML(node, parent); return new_ctrl; } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 878141ad0..fca9817c1 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -370,6 +370,7 @@ set(viewer_SOURCE_FILES llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp + llpanelnearbymedia.cpp llpanelmorph.cpp llpanelmsgs.cpp llpanelnetwork.cpp @@ -871,6 +872,7 @@ set(viewer_HEADER_FILES llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h + llpanelnearbymedia.h llpanelmorph.h llpanelmsgs.h llpanelnetwork.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8569c5258..d7ce62765 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -425,7 +425,19 @@ - + ShowNearbyMediaFloater + + Comment + Show nearby media floter + Persist + 1 + Type + Boolean + Value + 0 + + + ShowAOSitPopup @@ -1125,7 +1137,7 @@ This should be as low as possible, but too low may break functionality Comment Show Windlight popup Persist - 0 + 1 Type Boolean Value @@ -16940,6 +16952,22 @@ This should be as low as possible, but too low may break functionality 473 + FloaterNearbyMediaRect + + Comment + Rectangle for nearby media floater. + Persist + 1 + Type + Rect + Value + + 0 + 0 + 0 + 0 + + WindEnabled Comment diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index 5c044a496..b5bc95088 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -352,9 +352,6 @@ LLXMLNodePtr LLColorSwatchCtrl::getXML(bool save_children) const LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("colorswatch"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -373,7 +370,7 @@ LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFa } LLColorSwatchCtrl* color_swatch = new LLColorSwatchCtrl( - name, + "colorswatch", rect, label, color ); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 387c20ccc..ea6a0e0fd 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -124,6 +124,7 @@ public: static void closeWithoutSaving(); protected: + friend class LLPanelNearByMedia; LLPreferenceCore *mPreferenceCore; /*virtual*/ void onClose(bool app_quitting); diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp index 5e6f3e1b0..c33667061 100644 --- a/indra/newview/lljoystickbutton.cpp +++ b/indra/newview/lljoystickbutton.cpp @@ -331,9 +331,6 @@ LLXMLNodePtr LLJoystickAgentTurn::getXML(bool save_children) const LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("button"); - node->getAttributeString("name", name); - std::string image_unselected; if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected); @@ -343,7 +340,7 @@ LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrl EJoystickQuadrant quad = JQ_ORIGIN; if (node->hasAttribute("quadrant")) quad = selectQuadrant(node); - LLJoystickAgentTurn *button = new LLJoystickAgentTurn(name, + LLJoystickAgentTurn *button = new LLJoystickAgentTurn("button", LLRect(), image_unselected, image_selected, @@ -449,9 +446,6 @@ LLXMLNodePtr LLJoystickAgentSlide::getXML(bool save_children) const // static LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("button"); - node->getAttributeString("name", name); - std::string image_unselected; if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected); @@ -462,7 +456,7 @@ LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtr EJoystickQuadrant quad = JQ_ORIGIN; if (node->hasAttribute("quadrant")) quad = selectQuadrant(node); - LLJoystickAgentSlide *button = new LLJoystickAgentSlide(name, + LLJoystickAgentSlide *button = new LLJoystickAgentSlide("button", LLRect(), image_unselected, image_selected, diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp index 449bf9329..28381301d 100644 --- a/indra/newview/llnamebox.cpp +++ b/indra/newview/llnamebox.cpp @@ -46,20 +46,18 @@ // statics std::set LLNameBox::sInstances; +static LLRegisterWidget r("name_box"); -LLNameBox::LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id, BOOL is_group, const LLFontGL* font, BOOL mouse_opaque) -: LLTextBox(name, rect, std::string("(retrieving)"), font, mouse_opaque), - mNameID(name_id) + +LLNameBox::LLNameBox(const std::string& name) +: LLTextBox(name, LLRect(), "" , NULL, TRUE) { + mNameID = LLUUID::null; + mLink = false; + //mParseHTML = mLink; // STORM-215 + mInitialValue = "(retrieving)"; LLNameBox::sInstances.insert(this); - if(!name_id.isNull()) - { - setNameID(name_id, is_group); - } - else - { - setText(LLStringUtil::null); - } + setText(LLStringUtil::null); } LLNameBox::~LLNameBox() @@ -72,25 +70,30 @@ void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group) mNameID = name_id; std::string name; + BOOL got_name = FALSE; if (!is_group) { - gCacheName->getFullName(name_id, name); + got_name = gCacheName->getFullName(name_id, name); } else { - gCacheName->getGroupName(name_id, name); + got_name = gCacheName->getGroupName(name_id, name); } - setText(name); + // Got the name already? Set it. + // Otherwise it will be set later in refresh(). + if (got_name) + setName(name, is_group); + else + setText(mInitialValue); } void LLNameBox::refresh(const LLUUID& id, const std::string& full_name, bool is_group) - { if (id == mNameID) { - setText(full_name); + setName(full_name, is_group); } } @@ -105,3 +108,39 @@ void LLNameBox::refreshAll(const LLUUID& id, const std::string& full_name, bool box->refresh(id, full_name, is_group); } } + +void LLNameBox::setName(const std::string& name, BOOL is_group) +{ + if (mLink) + { + std::string url; + + if (is_group) + url = "[secondlife:///app/group/" + mNameID.asString() + "/about " + name + "]"; + else + url = "[secondlife:///app/agent/" + mNameID.asString() + "/about " + name + "]"; + + setText(url); + } + else + { + setText(name); + } +} + +// virtual +void LLNameBox::initFromXML(LLXMLNodePtr node, LLView* parent) +{ + LLTextBox::initFromXML(node, parent); + node->getAttributeBOOL("link", mLink); + node->getAttributeString("initial_value", mInitialValue); +} + +// static +LLView* LLNameBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + LLNameBox* name_box = new LLNameBox("name_box"); + name_box->initFromXML(node,parent); + return name_box; +} + diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h index cb968e78d..0d5ec6a83 100644 --- a/indra/newview/llnamebox.h +++ b/indra/newview/llnamebox.h @@ -44,10 +44,9 @@ class LLNameBox : public LLTextBox { public: - LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id = LLUUID::null, BOOL is_group = FALSE, const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE ); - // By default, follows top and left and is mouse-opaque. - // If no text, text = name. - // If no font, uses default system font. + virtual void initFromXML(LLXMLNodePtr node, LLView* parent); + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + virtual ~LLNameBox(); void setNameID(const LLUUID& name_id, BOOL is_group); @@ -56,11 +55,19 @@ public: static void refreshAll(const LLUUID& id, const std::string& full_name, bool is_group); +protected: + LLNameBox (const std::string& name); + + friend class LLUICtrlFactory; private: + void setName(const std::string& name, BOOL is_group); + static std::set sInstances; private: LLUUID mNameID; + BOOL mLink; + std::string mInitialValue; }; diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index 1fac553c0..d7c948d36 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -132,9 +132,6 @@ LLXMLNodePtr LLNameEditor::getXML(bool save_children) const LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("name_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -143,7 +140,7 @@ LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLFontGL* font = LLView::selectFont(node); - LLNameEditor* line_editor = new LLNameEditor(name, + LLNameEditor* line_editor = new LLNameEditor("name_editor", rect, LLUUID::null, FALSE, font, diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 7c2b128d6..49f26ad4e 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -319,9 +319,6 @@ LLXMLNodePtr LLNameListCtrl::getXML(bool save_children) const LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("name_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -337,7 +334,7 @@ LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto S32 name_column_index = 0; node->getAttributeS32("name_column_index", name_column_index); - LLNameListCtrl* name_list = new LLNameListCtrl(name, + LLNameListCtrl* name_list = new LLNameListCtrl("name_list", rect, multi_select, draw_border, diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 4ef005d0a..fcb924e9f 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -69,7 +69,7 @@ #include "llmediactrl.h" #include "llselectmgr.h" #include "wlfPanel_AdvSettings.h" - +#include "llpanelnearbymedia.h" @@ -90,7 +90,6 @@ LLOverlayBar *gOverlayBar = NULL; extern S32 MENU_BAR_HEIGHT; extern ImportTracker gImportTracker; -BOOL LLOverlayBar::sAdvSettingsPopup; BOOL LLOverlayBar::sChatVisible; // @@ -156,15 +155,15 @@ LLOverlayBar::LLOverlayBar() bool updateAdvSettingsPopup(const LLSD &data) { - LLOverlayBar::sAdvSettingsPopup = gSavedSettings.getBOOL("wlfAdvSettingsPopup"); + bool wfl_adv_settings_popup = gSavedSettings.getBOOL("wlfAdvSettingsPopup"); wlfPanel_AdvSettings::updateClass(); if(LLLayoutStack* layout_stack = gOverlayBar->findChild("overlay_layout_panel")) { LLLayoutPanel* layout_panel = layout_stack->findChild("AdvSettings_container"); if(layout_panel) { - layout_stack->collapsePanel(layout_panel,LLOverlayBar::sAdvSettingsPopup); - if(!LLOverlayBar::sAdvSettingsPopup) + layout_stack->collapsePanel(layout_panel,!wfl_adv_settings_popup); + if(wfl_adv_settings_popup) layout_panel->setTargetDim(layout_panel->getChild("Adv_Settings")->getBoundingRect().getWidth()); } } @@ -184,6 +183,11 @@ bool updateAORemote(const LLSD &data) return true; } +bool updateNearbyMediaFloater(const LLSD &data) +{ + LLFloaterNearbyMedia::updateClass(); + return true; +} BOOL LLOverlayBar::postBuild() { @@ -212,22 +216,26 @@ BOOL LLOverlayBar::postBuild() layoutButtons(); - sAdvSettingsPopup = gSavedSettings.getBOOL("wlfAdvSettingsPopup"); sChatVisible = gSavedSettings.getBOOL("ChatVisible"); - gSavedSettings.getControl("wlfAdvSettingsPopup")->getSignal()->connect(boost::bind(&updateAdvSettingsPopup,_2)); + LLControlVariable* wfl_adv_settings_popupp = gSavedSettings.getControl("wlfAdvSettingsPopup"); + wfl_adv_settings_popupp->getSignal()->connect(boost::bind(&updateAdvSettingsPopup,_2)); gSavedSettings.getControl("ChatVisible")->getSignal()->connect(boost::bind(&updateChatVisible,_2)); gSavedSettings.getControl("EnableAORemote")->getSignal()->connect(boost::bind(&updateAORemote,_2)); + gSavedSettings.getControl("ShowNearbyMediaFloater")->getSignal()->connect(boost::bind(&updateNearbyMediaFloater,_2)); + childSetVisible("ao_remote_container", gSavedSettings.getBOOL("EnableAORemote")); wlfPanel_AdvSettings::updateClass(); + + bool wfl_adv_settings_popup = wfl_adv_settings_popupp->getValue().asBoolean(); if(LLLayoutStack* layout_stack = findChild("overlay_layout_panel")) { LLLayoutPanel* layout_panel = layout_stack->findChild("AdvSettings_container"); if(layout_panel) { - layout_stack->collapsePanel(layout_panel,LLOverlayBar::sAdvSettingsPopup); - if(!LLOverlayBar::sAdvSettingsPopup) + layout_stack->collapsePanel(layout_panel,!wfl_adv_settings_popup); + if(wfl_adv_settings_popup) layout_panel->setTargetDim(layout_panel->getChild("Adv_Settings")->getBoundingRect().getWidth()); } } diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h index a668d4fbd..1b8cf767d 100644 --- a/indra/newview/lloverlaybar.h +++ b/indra/newview/lloverlaybar.h @@ -99,7 +99,6 @@ public: void setCancelTPButtonVisible(BOOL b, const std::string& label); static BOOL sChatVisible; - static BOOL sAdvSettingsPopup; protected: static void* createMediaRemote(void* userdata); static void* createVoiceRemote(void* userdata); @@ -110,6 +109,7 @@ protected: void enableMediaButtons(); protected: + friend class LLFloaterNearbyMedia; //Crappy workaround to access mMediaRemote LLMediaRemoteCtrl* mMediaRemote; LLVoiceRemoteCtrl* mVoiceRemote; LLButton* mCancelBtn; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index f215e8266..6b7a445c9 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -142,14 +142,7 @@ BOOL LLPanelGroupGeneral::postBuild() mBtnInfo->setClickedCallback(boost::bind(&LLPanelGroupGeneral::onClickInfo, this)); } - LLTextBox* founder = getChild("founder_name"); - if (founder) - { - mFounderName = new LLNameBox(founder->getName(),founder->getRect(),LLUUID::null,FALSE,founder->getFont(),founder->getMouseOpaque()); - removeChild(founder); - delete founder; - addChild(mFounderName); - } + mFounderName = getChild("founder_name"); mListVisibleMembers = getChild("visible_members", recurse); if (mListVisibleMembers) diff --git a/indra/newview/llpanelmediasettingspermissions.cpp b/indra/newview/llpanelmediasettingspermissions.cpp index 5f7042a3c..d9e5d4ad2 100644 --- a/indra/newview/llpanelmediasettingspermissions.cpp +++ b/indra/newview/llpanelmediasettingspermissions.cpp @@ -76,7 +76,7 @@ BOOL LLPanelMediaSettingsPermissions::postBuild() mPermsWorldInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); mPermsWorldControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); - //mPermsGroupName = getChild< LLNameBox >( "perms_group_name" ); + mPermsGroupName = getChild< LLNameBox >( "perms_group_name" ); return true; } @@ -94,7 +94,7 @@ void LLPanelMediaSettingsPermissions::draw() // housekeeping LLPanel::draw(); - //getChild("perms_group_name")->setValue(LLStringUtil::null); + getChild("perms_group_name")->setValue(LLStringUtil::null); LLUUID group_id; BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id); if (groups_identical) @@ -139,7 +139,7 @@ void LLPanelMediaSettingsPermissions::clearValues( void* userdata, bool editable self->getChild< LLTextBox >("controls_label")->setEnabled(editable); self->getChild< LLTextBox >("owner_label")->setEnabled(editable); self->getChild< LLTextBox >("group_label")->setEnabled(editable); - //self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); } @@ -218,7 +218,7 @@ void LLPanelMediaSettingsPermissions::initValues( void* userdata, const LLSD& me self->getChild< LLTextBox >("controls_label")->setEnabled(editable); self->getChild< LLTextBox >("owner_label")->setEnabled(editable); self->getChild< LLTextBox >("group_label")->setEnabled(editable); - //self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); } diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp new file mode 100644 index 000000000..0c7d3990d --- /dev/null +++ b/indra/newview/llpanelnearbymedia.cpp @@ -0,0 +1,1313 @@ +/** + * @file llpanelnearbymedia.cpp + * @brief Management interface for muting and controlling nearby media + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelnearbymedia.h" + +#include "llaudioengine.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llresizebar.h" +#include "llresizehandle.h" +#include "llscrolllistctrl.h" +#include "llslider.h" +#include "llsliderctrl.h" +#include "llagent.h" +#include "llagentui.h" +#include "llbutton.h" +#include "lltextbox.h" +#include "llviewermedia.h" +#include "llviewerparcelmedia.h" +#include "llviewerregion.h" +#include "llviewermediafocus.h" +#include "llviewerparcelmgr.h" +#include "llparcel.h" +#include "llpluginclassmedia.h" +#include "llvovolume.h" +#include "llstatusbar.h" +#include "llsdutil.h" +#include "llvieweraudio.h" +#include "lluictrlfactory.h" + +#include "llfloaterpreference.h" // for the gear icon +#include "lltabcontainer.h" + +#include + +#include "lloverlaybar.h" +#include "llmediaremotectrl.h" +#include "llchatbar.h" +#include "lltoolbar.h" + +extern LLControlGroup gSavedSettings; + +static const LLUUID PARCEL_MEDIA_LIST_ITEM_UUID = LLUUID("CAB5920F-E484-4233-8621-384CF373A321"); +static const LLUUID PARCEL_AUDIO_LIST_ITEM_UUID = LLUUID("DF4B020D-8A24-4B95-AB5D-CA970D694822"); + +// +// LLPanelNearByMedia +// + + +LLPanelNearByMedia::LLPanelNearByMedia(bool standalone_panel) +: mMediaList(NULL), + mEnableAllCtrl(NULL), + mAllMediaDisabled(false), + mDebugInfoVisible(false), + mParcelMediaItem(NULL), + mParcelAudioItem(NULL), + mStandalonePanel(standalone_panel) +{ + mHoverTimer.stop(); + + mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay"); + + gSavedSettings.getControl(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING)->getSignal()->connect(boost::bind(&LLPanelNearByMedia::handleMediaAutoPlayChanged, this, _2)); + + mCommitCallbackRegistrar.add("MediaListCtrl.EnableAll", boost::bind(&LLPanelNearByMedia::onClickEnableAll, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.DisableAll", boost::bind(&LLPanelNearByMedia::onClickDisableAll, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.GoMediaPrefs", boost::bind(&LLPanelNearByMedia::onAdvancedButtonClick, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.MoreLess", boost::bind(&LLPanelNearByMedia::onMoreLess, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Stop", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaStop, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Play", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaPlay, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Pause", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaPause, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Mute", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaMute, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Volume", boost::bind(&LLPanelNearByMedia::onCommitSelectedMediaVolume, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Zoom", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaZoom, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Unzoom", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaUnzoom, this)); + + //buildFromFile( "panel_nearby_media.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_nearby_media.xml"); +} + +LLPanelNearByMedia::~LLPanelNearByMedia() +{ + //Wipe the mInNearbyMediaList value for all impls since we don't need them anymore. + LLViewerMedia::impl_list impls = LLViewerMedia::getPriorityList(); + LLViewerMedia::impl_list::iterator priority_iter; + for(priority_iter = impls.begin(); priority_iter != impls.end(); priority_iter++) + { + (*priority_iter)->setInNearbyMediaList(false); + } +} + +BOOL LLPanelNearByMedia::postBuild() +{ + LLPanel::postBuild(); + + if(mStandalonePanel) + { + const S32 RESIZE_BAR_THICKNESS = 6; + LLResizeBar::Params p; + p.rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0); + p.name = "resizebar_bottom"; + p.min_size = getRect().getHeight(); + p.side = LLResizeBar::BOTTOM; + p.resizing_view = this; + addChild( LLUICtrlFactory::create(p) ); + + p.rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0); + p.name = "resizebar_left"; + p.min_size = getRect().getWidth(); + p.side = LLResizeBar::LEFT; + addChild( LLUICtrlFactory::create(p) ); + + LLResizeHandle::Params resize_handle_p; + resize_handle_p.rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ); + resize_handle_p.mouse_opaque(false); + resize_handle_p.min_width(getRect().getWidth()); + resize_handle_p.min_height(getRect().getHeight()); + resize_handle_p.corner(LLResizeHandle::LEFT_BOTTOM); + addChild(LLUICtrlFactory::create(resize_handle_p)); + } + + mNearbyMediaPanel = getChild("nearby_media_panel"); + mMediaList = getChild("media_list"); + mEnableAllCtrl = getChild("all_nearby_media_enable_btn"); + mDisableAllCtrl = getChild("all_nearby_media_disable_btn"); + mShowCtrl = getChild("show_combo"); + + // Dynamic (selection-dependent) controls + mStopCtrl = getChild("stop"); + mPlayCtrl = getChild("play"); + mPauseCtrl = getChild("pause"); + mMuteCtrl = getChild("mute"); + mVolumeSliderCtrl = getChild("volume_slider_ctrl"); + mZoomCtrl = getChild("zoom"); + mUnzoomCtrl = getChild("unzoom"); + mVolumeSlider = getChild("volume_slider"); + mMuteBtn = getChild("mute_btn"); + + mEmptyNameString = getString("empty_item_text"); + mParcelMediaName = getString("parcel_media_name"); + mParcelAudioName = getString("parcel_audio_name"); + mPlayingString = getString("playing_suffix"); + + mMediaList->setDoubleClickCallback(onZoomMedia, this); + mMediaList->sortByColumnIndex(PROXIMITY_COLUMN, TRUE); + mMediaList->sortByColumnIndex(VISIBILITY_COLUMN, FALSE); + + refreshList(); + updateControls(); + updateColumns(); + + LLView* minimized_controls = getChildView("minimized_controls"); + mMoreRect = getRect(); + mLessRect = getRect(); + mLessRect.mBottom = minimized_controls->getRect().mBottom; + + getChild("more_btn")->setVisible(false); + onMoreLess(); + + return TRUE; +} + +void LLPanelNearByMedia::handleMediaAutoPlayChanged(const LLSD& newvalue) +{ + // update mParcelAudioAutoStart if AUTO_PLAY_MEDIA_SETTING changes + mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay"); +} + +/*virtual*/ +void LLPanelNearByMedia::onMouseEnter(S32 x, S32 y, MASK mask) +{ + mHoverTimer.stop(); + LLPanel::onMouseEnter(x,y,mask); +} + + +/*virtual*/ +void LLPanelNearByMedia::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if(mStandalonePanel) + mHoverTimer.start(); + LLPanel::onMouseLeave(x,y,mask); +} + +/*virtual*/ +void LLPanelNearByMedia::onTopLost() +{ + setVisible(FALSE); +} + + +/*virtual*/ +void LLPanelNearByMedia::handleVisibilityChange ( BOOL new_visibility ) +{ + if (mStandalonePanel && new_visibility) + { + mHoverTimer.start(); // timer will be stopped when mouse hovers over panel + } + else + { + mHoverTimer.stop(); + } +} + +/*virtual*/ +void LLPanelNearByMedia::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLPanel::reshape(width, height, called_from_parent); + + LLButton* more_btn = findChild("more_btn"); + if (!mStandalonePanel || more_btn && more_btn->getValue().asBoolean()) + { + mMoreRect = getRect(); + } + +} + +const F32 AUTO_CLOSE_FADE_TIME_START= 4.0f; +const F32 AUTO_CLOSE_FADE_TIME_END = 5.0f; + +/*virtual*/ +void LLPanelNearByMedia::draw() +{ + // keep bottom of panel on screen + LLRect screen_rect = calcScreenRect(); + if (screen_rect.mBottom < 0) + { + LLRect new_rect = getRect(); + new_rect.mBottom += 0 - screen_rect.mBottom; + setShape(new_rect); + } + + refreshList(); + updateControls(); + + + F32 alpha = mHoverTimer.getStarted() + ? clamp_rescale(mHoverTimer.getElapsedTimeF32(), AUTO_CLOSE_FADE_TIME_START, AUTO_CLOSE_FADE_TIME_END, 1.f, 0.f) + : 1.0f; + + LLViewDrawContext context(alpha); + + LLPanel::draw(); + + if (alpha == 0.f) + { + setVisible(false); + } +} + +/*virtual*/ +BOOL LLPanelNearByMedia::handleHover(S32 x, S32 y, MASK mask) +{ + LLPanel::handleHover(x, y, mask); + + // If we are hovering over this panel, make sure to clear any hovered media + // ID. Note that the more general solution would be to clear this ID when + // the mouse leaves the in-scene view, but that proved to be problematic. + // See EXT-5517 + LLViewerMediaFocus::getInstance()->clearHover(); + + // Always handle + return true; +} + +bool LLPanelNearByMedia::getParcelAudioAutoStart() +{ + return mParcelAudioAutoStart; +} + +LLScrollListItem* LLPanelNearByMedia::addListItem(const LLUUID &id) +{ + if (NULL == mMediaList) return NULL; + + // Just set up the columns -- the values will be filled in by updateListItem(). + + LLSD row; + row["id"] = id; + + LLSD &columns = row["columns"]; + columns[CHECKBOX_COLUMN]["name"]="media_checkbox_ctrl"; + columns[CHECKBOX_COLUMN]["column"] = "media_checkbox_ctrl"; + columns[CHECKBOX_COLUMN]["type"] = "checkbox"; + //if(mDebugInfoVisible) + { + columns[PROXIMITY_COLUMN]["name"]="media_proximity"; + columns[PROXIMITY_COLUMN]["column"] = "media_proximity"; + columns[PROXIMITY_COLUMN]["value"] = ""; + columns[VISIBILITY_COLUMN]["name"]="media_visibility"; + columns[VISIBILITY_COLUMN]["column"] = "media_visibility"; + columns[VISIBILITY_COLUMN]["value"] = ""; + columns[CLASS_COLUMN]["name"]="media_class"; + columns[CLASS_COLUMN]["column"] = "media_class"; + columns[CLASS_COLUMN]["type"] = "text"; + columns[CLASS_COLUMN]["value"] = ""; + } + columns[NAME_COLUMN]["name"] = "media_name"; + columns[NAME_COLUMN]["column"] = "media_name"; + columns[NAME_COLUMN]["type"] = "text"; + columns[NAME_COLUMN]["value"] = ""; + //if(mDebugInfoVisible) + { + columns[DEBUG_COLUMN]["name"] = "media_debug"; + columns[DEBUG_COLUMN]["column"] = "media_debug"; + columns[DEBUG_COLUMN]["type"] = "text"; + columns[DEBUG_COLUMN]["value"] = ""; + } + + LLScrollListItem* new_item = mMediaList->addElement(row); + if (NULL != new_item) + { + LLScrollListCheck* scroll_list_check = dynamic_cast(new_item->getColumn(CHECKBOX_COLUMN)); + if (scroll_list_check) + { + LLCheckBoxCtrl *check = scroll_list_check->getCheckBox(); + check->setCommitCallback(boost::bind(&LLPanelNearByMedia::onCheckItem, this, _1, id)); + } + } + return new_item; +} + +extern char const* PRIORITYToString(LLViewerMediaImpl::EPriority priority); +void LLPanelNearByMedia::updateListItem(LLScrollListItem* item, LLViewerMediaImpl* impl) +{ + std::string item_name; + std::string item_tooltip; + std::string debug_str; + LLPanelNearByMedia::MediaClass media_class = MEDIA_CLASS_ALL; + + getNameAndUrlHelper(impl, item_name, item_tooltip, mEmptyNameString); + // Focused + if (impl->hasFocus()) + { + media_class = MEDIA_CLASS_FOCUSED; + } + // Is attached to another avatar? + else if (impl->isAttachedToAnotherAvatar()) + { + media_class = MEDIA_CLASS_ON_OTHERS; + } + // Outside agent parcel + else if (!impl->isInAgentParcel()) + { + media_class = MEDIA_CLASS_OUTSIDE_PARCEL; + } + else { + // inside parcel + media_class = MEDIA_CLASS_WITHIN_PARCEL; + } + + if(mDebugInfoVisible) + { + debug_str += llformat("%g/", (float)impl->getInterest()); + + // proximity distance is actually distance squared -- display it as straight distance. + debug_str += llformat("%g/", (F32) sqrt(impl->getProximityDistance())); + + // s += llformat("%g/", (float)impl->getCPUUsage()); + // s += llformat("%g/", (float)impl->getApproximateTextureInterest()); + debug_str += llformat("%g/", (float)(NULL == impl->getSomeObject()) ? 0.0 : impl->getSomeObject()->getPixelArea()); + + debug_str += PRIORITYToString(impl->getPriority()); + + if(impl->hasMedia()) + { + debug_str += '@'; + } + else if(impl->isPlayable()) + { + debug_str += '+'; + } + else if(impl->isForcedUnloaded()) + { + debug_str += '!'; + } + } + + updateListItem(item, + item_name, + item_tooltip, + impl->getProximity(), + impl->isMediaDisabled(), + impl->hasMedia(), + impl->isMediaTimeBased() && impl->isMediaPlaying(), + media_class, + debug_str); +} + +void LLPanelNearByMedia::updateListItem(LLScrollListItem* item, + const std::string &item_name, + const std::string &item_tooltip, + S32 proximity, + bool is_disabled, + bool has_media, + bool is_time_based_and_playing, + LLPanelNearByMedia::MediaClass media_class, + const std::string &debug_str) +{ + LLScrollListCell* cell = item->getColumn(PROXIMITY_COLUMN); + if(cell) + { + // since we are forced to sort by text, encode sort order as string + std::string proximity_string = STRINGIZE(proximity); + std::string old_proximity_string = cell->getValue().asString(); + if(proximity_string != old_proximity_string) + { + cell->setValue(proximity_string); + mMediaList->setNeedsSort(true); + } + } + + cell = item->getColumn(CHECKBOX_COLUMN); + if(cell) + { + cell->setValue(!is_disabled); + } + + cell = item->getColumn(VISIBILITY_COLUMN); + if(cell) + { + S32 old_visibility = cell->getValue(); + // *HACK ALERT: force ordering of Media before Audio before the rest of the list + S32 new_visibility = + item->getUUID() == PARCEL_MEDIA_LIST_ITEM_UUID ? 3 + : item->getUUID() == PARCEL_AUDIO_LIST_ITEM_UUID ? 2 + : (has_media) ? 1 + : ((is_disabled) ? 0 + : -1); + cell->setValue(STRINGIZE(new_visibility)); + if (new_visibility != old_visibility) + { + mMediaList->setNeedsSort(true); + } + } + + cell = item->getColumn(NAME_COLUMN); + if(cell) + { + std::string name = item_name; + std::string old_name = cell->getValue().asString(); + if (has_media) + { + name += " " + mPlayingString; + } + if (name != old_name) + { + cell->setValue(name); + } + item->setToolTip(item_tooltip); + + // *TODO: Make these font styles/colors configurable via XUI + U8 font_style = LLFontGL::NORMAL; + LLColor4 cell_color = LLColor4::black; + + // Only colorize by class in debug + if (mDebugInfoVisible) + { + switch (media_class) { + case MEDIA_CLASS_FOCUSED: + cell_color = LLColor4::yellow; + break; + case MEDIA_CLASS_ON_OTHERS: + cell_color = LLColor4::red; + break; + case MEDIA_CLASS_OUTSIDE_PARCEL: + cell_color = LLColor4::orange; + break; + case MEDIA_CLASS_WITHIN_PARCEL: + default: + break; + } + } + if (is_disabled) + { + if (mDebugInfoVisible) + { + font_style |= LLFontGL::ITALIC; + cell_color = LLColor4::black; + } + else { + // Dim it if it is disabled + cell_color.setAlpha(0.25); + } + } + // Dim it if it isn't "showing" + else if (!has_media) + { + cell_color.setAlpha(0.25); + } + // Bold it if it is time-based media and it is playing + else if (is_time_based_and_playing) + { + if (mDebugInfoVisible) font_style |= LLFontGL::BOLD; + } + cell->setColor(cell_color); + LLScrollListText *text_cell = dynamic_cast (cell); + if (text_cell) + { + text_cell->setFontStyle(font_style); + } + } + + cell = item->getColumn(CLASS_COLUMN); + if(cell) + { + // TODO: clean this up! + cell->setValue(STRINGIZE(media_class)); + } + + if(mDebugInfoVisible) + { + cell = item->getColumn(DEBUG_COLUMN); + if(cell) + { + cell->setValue(debug_str); + } + } +} + +void LLPanelNearByMedia::removeListItem(const LLUUID &id) +{ + if (NULL == mMediaList) return; + + mMediaList->deleteSingleItem(mMediaList->getItemIndex(id)); +} + +void LLPanelNearByMedia::refreshParcelItems() +{ + // + // First add/remove the "fake" items Parcel Media and Parcel Audio. + // These items will have special UUIDs + // PARCEL_MEDIA_LIST_ITEM_UUID + // PARCEL_AUDIO_LIST_ITEM_UUID + // + // Get the filter choice. + const LLSD &choice_llsd = mShowCtrl->getSelectedValue(); + MediaClass choice = (MediaClass)choice_llsd.asInteger(); + // Only show "special parcel items" if "All" or "Within" filter + // (and if media is "enabled") + bool should_include = (choice == MEDIA_CLASS_ALL || choice == MEDIA_CLASS_WITHIN_PARCEL); + + // First Parcel Media: add or remove it as necessary + if (gSavedSettings.getBOOL("AudioStreamingMedia") &&should_include && LLViewerMedia::hasParcelMedia()) + { + // Yes, there is parcel media. + if (NULL == mParcelMediaItem) + { + mParcelMediaItem = addListItem(PARCEL_MEDIA_LIST_ITEM_UUID); + mMediaList->setNeedsSort(true); + } + } + else { + if (NULL != mParcelMediaItem) { + removeListItem(PARCEL_MEDIA_LIST_ITEM_UUID); + mParcelMediaItem = NULL; + mMediaList->setNeedsSort(true); + } + } + + // ... then update it + if (NULL != mParcelMediaItem) + { + std::string name, url, tooltip; + getNameAndUrlHelper(LLViewerParcelMedia::getParcelMedia(), name, url, ""); + if (name.empty() || name == url) + { + tooltip = url; + } + else + { + tooltip = name + " : " + url; + } + LLViewerMediaImpl *impl = LLViewerParcelMedia::getParcelMedia(); + updateListItem(mParcelMediaItem, + mParcelMediaName, + tooltip, + -2, // Proximity closer than anything else, before Parcel Audio + impl == NULL || impl->isMediaDisabled(), + impl != NULL && !LLViewerParcelMedia::getURL().empty(), + impl != NULL && impl->isMediaTimeBased() && impl->isMediaPlaying(), + MEDIA_CLASS_ALL, + "parcel media"); + } + + // 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) + { + mParcelAudioItem = addListItem(PARCEL_AUDIO_LIST_ITEM_UUID); + mMediaList->setNeedsSort(true); + } + } + else { + if (NULL != mParcelAudioItem) { + removeListItem(PARCEL_AUDIO_LIST_ITEM_UUID); + mParcelAudioItem = NULL; + mMediaList->setNeedsSort(true); + } + } + + // ... then update it + if (NULL != mParcelAudioItem) + { + bool is_playing = LLViewerMedia::isParcelAudioPlaying(); + + std::string url; + url = LLViewerMedia::getParcelAudioURL(); + + updateListItem(mParcelAudioItem, + mParcelAudioName, + url, + -1, // Proximity after Parcel Media, but closer than anything else + (!is_playing), + is_playing, + is_playing, + MEDIA_CLASS_ALL, + "parcel audio"); + } +} + +void LLPanelNearByMedia::refreshList() +{ + bool all_items_deleted = false; + + if(!mMediaList) + { + // None of this makes any sense if the media list isn't there. + return; + } + + // Check whether the debug column has been shown/hidden. + bool debug_info_visible = gSavedSettings.getBOOL("MediaPerformanceManagerDebug"); + if(debug_info_visible != mDebugInfoVisible) + { + mDebugInfoVisible = debug_info_visible; + + // Clear all items so the list gets regenerated. + mMediaList->deleteAllItems(); + mParcelAudioItem = NULL; + mParcelMediaItem = NULL; + all_items_deleted = true; + + updateColumns(); + } + + refreshParcelItems(); + + // Get the canonical list from LLViewerMedia + LLViewerMedia::impl_list impls = LLViewerMedia::getPriorityList(); + LLViewerMedia::impl_list::iterator priority_iter; + + U32 enabled_count = 0; + U32 disabled_count = 0; + + // iterate over the impl list, creating rows as necessary. + for(priority_iter = impls.begin(); priority_iter != impls.end(); priority_iter++) + { + LLViewerMediaImpl *impl = *priority_iter; + + // If we just emptied out the list, every flag needs to be reset. + if(all_items_deleted) + { + impl->setInNearbyMediaList(false); + } + + if (!impl->isParcelMedia()) + { + LLUUID media_id = impl->getMediaTextureID(); + S32 proximity = impl->getProximity(); + // This is expensive (i.e. a linear search) -- don't use it here. We now use mInNearbyMediaList instead. + //S32 index = mMediaList->getItemIndex(media_id); + if (proximity < 0 || !shouldShow(impl)) + { + if (impl->getInNearbyMediaList()) + { + // There's a row for this impl -- remove it. + removeListItem(media_id); + impl->setInNearbyMediaList(false); + } + } + else + { + if (!impl->getInNearbyMediaList()) + { + // We don't have a row for this impl -- add one. + addListItem(media_id); + impl->setInNearbyMediaList(true); + } + } + // Update counts + if (impl->isMediaDisabled()) + { + disabled_count++; + } + else { + enabled_count++; + } + } + } + mDisableAllCtrl->setEnabled((gSavedSettings.getBOOL("AudioStreamingMusic") || + gSavedSettings.getBOOL("AudioStreamingMedia")) && + (LLViewerMedia::isAnyMediaShowing() || + LLViewerMedia::isParcelMediaPlaying() || + LLViewerMedia::isParcelAudioPlaying())); + + mEnableAllCtrl->setEnabled( (gSavedSettings.getBOOL("AudioStreamingMusic") || + 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 items = mMediaList->getAllData(); + + for (std::vector::iterator item_it = items.begin(); + item_it != items.end(); + ++item_it) + { + LLScrollListItem* item = (*item_it); + LLUUID row_id = item->getUUID(); + + if (row_id != PARCEL_MEDIA_LIST_ITEM_UUID && + row_id != PARCEL_AUDIO_LIST_ITEM_UUID) + { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(row_id); + if(impl) + { + updateListItem(item, impl); + } + else + { + // This item's impl has been deleted -- remove the row. + // Removing the row won't throw off our iteration, since we have a local copy of the array. + // We just need to make sure we don't access this item after the delete. + removeListItem(row_id); + } + } + } + + // Set the selection to whatever media impl the media focus/hover is on. + // This is an experiment, and can be removed by ifdefing out these 4 lines. + LLUUID media_target = LLViewerMediaFocus::getInstance()->getControlsMediaID(); + if(media_target.notNull()) + { + mMediaList->selectByID(media_target); + } +} + +void LLPanelNearByMedia::updateColumns() +{ + if (!mDebugInfoVisible) + { + if (mMediaList->getColumn(CHECKBOX_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(VISIBILITY_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(PROXIMITY_COLUMN)) mMediaList->getColumn(PROXIMITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(CLASS_COLUMN)) mMediaList->getColumn(CLASS_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(DEBUG_COLUMN)) mMediaList->getColumn(DEBUG_COLUMN)->setWidth(-1); + } + else { + if (mMediaList->getColumn(CHECKBOX_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(20); + if (mMediaList->getColumn(VISIBILITY_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(20); + if (mMediaList->getColumn(PROXIMITY_COLUMN)) mMediaList->getColumn(PROXIMITY_COLUMN)->setWidth(30); + if (mMediaList->getColumn(CLASS_COLUMN)) mMediaList->getColumn(CLASS_COLUMN)->setWidth(20); + if (mMediaList->getColumn(DEBUG_COLUMN)) mMediaList->getColumn(DEBUG_COLUMN)->setWidth(200); + } +} + +void LLPanelNearByMedia::onClickEnableAll() +{ + LLViewerMedia::setAllMediaEnabled(true); +} + +void LLPanelNearByMedia::onClickDisableAll() +{ + LLViewerMedia::setAllMediaEnabled(false); +} + +void LLPanelNearByMedia::onClickEnableParcelMedia() +{ + if ( ! LLViewerMedia::isParcelMediaPlaying() ) + { + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + } +} + +void LLPanelNearByMedia::onClickDisableParcelMedia() +{ + // This actually unloads the impl, as opposed to "stop"ping the media + LLViewerParcelMedia::stop(); +} + +void LLPanelNearByMedia::onCheckItem(LLUICtrl* ctrl, const LLUUID &row_id) +{ + LLCheckBoxCtrl* check = static_cast(ctrl); + + setDisabled(row_id, ! check->getValue()); +} + +bool LLPanelNearByMedia::setDisabled(const LLUUID &row_id, bool disabled) +{ + if (row_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + if (disabled) + { + onClickParcelAudioStop(); + } + else + { + onClickParcelAudioPlay(); + } + return true; + } + else if (row_id == PARCEL_MEDIA_LIST_ITEM_UUID) + { + if (disabled) + { + onClickDisableParcelMedia(); + } + else + { + onClickEnableParcelMedia(); + } + return true; + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(row_id); + if(impl) + { + impl->setDisabled(disabled, true); + return true; + } + } + return false; +} + +//static +void LLPanelNearByMedia::onZoomMedia(void* user_data) +{ + LLPanelNearByMedia* panelp = (LLPanelNearByMedia*)user_data; + LLUUID media_id = panelp->mMediaList->getValue().asUUID(); + + LLViewerMediaFocus::getInstance()->focusZoomOnMedia(media_id); +} + +void LLPanelNearByMedia::onClickParcelMediaPlay() +{ + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); +} + +void LLPanelNearByMedia::onClickParcelMediaStop() +{ + if (LLViewerParcelMedia::getParcelMedia()) + { + // This stops the media playing, as opposed to unloading it like + // LLViewerParcelMedia::stop() does + LLViewerParcelMedia::getParcelMedia()->stop(); + } +} + +void LLPanelNearByMedia::onClickParcelMediaPause() +{ + LLViewerParcelMedia::pause(); +} + +void LLPanelNearByMedia::onClickParcelAudioPlay() +{ + // User *explicitly* started the internet stream, so keep the stream + // playing and updated as they cross to other parcels etc. + mParcelAudioAutoStart = true; + if (!gAudiop) + return; + + if (LLAudioEngine::AUDIO_PAUSED == gAudiop->isInternetStreamPlaying()) + { + // 'false' means unpause + gAudiop->pauseInternetStream(false); + } + else + { + gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL()); + } +} + +void LLPanelNearByMedia::onClickParcelAudioStop() +{ + // User *explicitly* stopped the internet stream, so don't + // re-start audio when i.e. they move to another parcel, until + // they explicitly start it again. + mParcelAudioAutoStart = false; + if (!gAudiop) + return; + + gAudiop->stopInternetStream(); +} + +void LLPanelNearByMedia::onClickParcelAudioPause() +{ + if (!gAudiop) + return; + + // 'true' means pause + gAudiop->pauseInternetStream(true); +} + +bool LLPanelNearByMedia::shouldShow(LLViewerMediaImpl* impl) +{ + const LLSD &choice_llsd = mShowCtrl->getSelectedValue(); + MediaClass choice = (MediaClass)choice_llsd.asInteger(); + + switch (choice) + { + case MEDIA_CLASS_ALL: + return true; + break; + case MEDIA_CLASS_WITHIN_PARCEL: + return impl->isInAgentParcel(); + break; + case MEDIA_CLASS_OUTSIDE_PARCEL: + return ! impl->isInAgentParcel(); + break; + case MEDIA_CLASS_ON_OTHERS: + return impl->isAttachedToAnotherAvatar(); + break; + default: + break; + } + return true; +} + +void LLPanelNearByMedia::onAdvancedButtonClick() +{ + // bring up the prefs floater + + LLFloaterPreference::show(NULL); + LLFloaterPreference* prefsfloater = LLFloaterPreference::sInstance; + + if (prefsfloater) + { + // grab the 'audio' panel from the preferences floater and + // bring it the front! + LLTabContainer* tabcontainer = prefsfloater->getChild("pref core"); + if (tabcontainer) + { + tabcontainer->selectTabByName("Media Panel"); + } + } +} + +void LLPanelNearByMedia::onMoreLess() +{ + bool is_more = !mStandalonePanel || getChild("more_btn")->getToggleState(); + mNearbyMediaPanel->setVisible(is_more); + + // enable resizing when expanded + getChildView("resizebar_bottom")->setEnabled(is_more); + + LLRect new_rect = is_more ? mMoreRect : mLessRect; + new_rect.translate(getRect().mRight - new_rect.mRight, getRect().mTop - new_rect.mTop); + + setShape(new_rect); + + getChild("more_btn")->setVisible(mStandalonePanel); +} + +void LLPanelNearByMedia::updateControls() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + if (!LLViewerMedia::hasParcelAudio() || !gSavedSettings.getBOOL("AudioStreamingMusic")) + { + // disable controls if audio streaming music is disabled from preference + 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) + { + if (!LLViewerMedia::hasParcelMedia() || !gSavedSettings.getBOOL("AudioStreamingMedia")) + { + // disable controls if audio streaming media is disabled from preference + showDisabledControls(); + } + else { + LLViewerMediaImpl* impl = LLViewerParcelMedia::getParcelMedia(); + if (NULL == impl) + { + // Just means it hasn't started yet + showBasicControls(false, false, false, false, 0); + } + else if (impl->isMediaTimeBased()) + { + showTimeBasedControls(impl->isMediaPlaying(), + false, // include_zoom + false, // is_zoomed + impl->getVolume() == 0.0, + impl->getVolume() ); + } + else { + // non-time-based parcel media + showBasicControls(LLViewerMedia::isParcelMediaPlaying(), + false, + false, + impl->getVolume() == 0.0, + impl->getVolume()); + } + } + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + + if (NULL == impl || !gSavedSettings.getBOOL("AudioStreamingMedia")) + { + showDisabledControls(); + } + else { + if (impl->isMediaTimeBased()) + { + showTimeBasedControls(impl->isMediaPlaying(), + ! impl->isParcelMedia(), // include_zoom + LLViewerMediaFocus::getInstance()->isZoomed(), + impl->getVolume() == 0.0, + impl->getVolume()); + } + else { + showBasicControls(!impl->isMediaDisabled(), + ! impl->isParcelMedia(), // include_zoom + LLViewerMediaFocus::getInstance()->isZoomed(), + impl->getVolume() == 0.0, + impl->getVolume()); + } + } + } +} + +void LLPanelNearByMedia::showBasicControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume) +{ + mStopCtrl->setVisible(playing); + mPlayCtrl->setVisible(!playing); + mPauseCtrl->setVisible(false); + mVolumeSliderCtrl->setVisible(true); + mMuteCtrl->setVisible(true); + mMuteBtn->setValue(muted); + mVolumeSlider->setValue(volume); + mZoomCtrl->setVisible(include_zoom && !is_zoomed); + mUnzoomCtrl->setVisible(include_zoom && is_zoomed); + mStopCtrl->setEnabled(true); + mZoomCtrl->setEnabled(true); +} + +void LLPanelNearByMedia::showTimeBasedControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume) +{ + mStopCtrl->setVisible(true); + mPlayCtrl->setVisible(!playing); + mPauseCtrl->setVisible(playing); + mMuteCtrl->setVisible(true); + mVolumeSliderCtrl->setVisible(true); + mZoomCtrl->setVisible(include_zoom); + mZoomCtrl->setVisible(include_zoom && !is_zoomed); + mUnzoomCtrl->setVisible(include_zoom && is_zoomed); + mStopCtrl->setEnabled(true); + mZoomCtrl->setEnabled(true); + mMuteBtn->setValue(muted); + mVolumeSlider->setValue(volume); +} + +void LLPanelNearByMedia::showDisabledControls() +{ + mStopCtrl->setVisible(true); + mPlayCtrl->setVisible(false); + mPauseCtrl->setVisible(false); + mMuteCtrl->setVisible(false); + mVolumeSliderCtrl->setVisible(false); + mZoomCtrl->setVisible(true); + mUnzoomCtrl->setVisible(false); + mStopCtrl->setEnabled(false); + mZoomCtrl->setEnabled(false); +} + +void LLPanelNearByMedia::onClickSelectedMediaStop() +{ + setDisabled(mMediaList->getValue().asUUID(), true); +} + +void LLPanelNearByMedia::onClickSelectedMediaPlay() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + + // First enable it + setDisabled(selected_media_id, false); + + // Special code to make play "unpause" if time-based and playing + if (selected_media_id != PARCEL_AUDIO_LIST_ITEM_UUID) + { + LLViewerMediaImpl *impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + 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()); + } + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaPause() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + onClickParcelAudioPause(); + } + else if (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) + { + onClickParcelMediaPause(); + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl && impl->isMediaTimeBased() && impl->isMediaPlaying()) + { + impl->pause(); + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaMute() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + gSavedSettings.setBOOL("MuteMusic", mMuteBtn->getValue()); + } + else { + LLViewerMediaImpl* impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + F32 volume = impl->getVolume(); + if(volume > 0.0) + { + impl->setVolume(0.0); + } + else if (mVolumeSlider->getValueF32() == 0.0) + { + impl->setVolume(1.0); + mVolumeSlider->setValue(1.0); + } + else + { + impl->setVolume(mVolumeSlider->getValueF32()); + } + } + } +} + +void LLPanelNearByMedia::onCommitSelectedMediaVolume() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + F32 vol = mVolumeSlider->getValueF32(); + gSavedSettings.setF32("AudioLevelMusic", vol); + } + else { + LLViewerMediaImpl* impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + impl->setVolume(mVolumeSlider->getValueF32()); + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaZoom() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID || selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) + return; + LLViewerMediaFocus::getInstance()->focusZoomOnMedia(selected_media_id); +} + +void LLPanelNearByMedia::onClickSelectedMediaUnzoom() +{ + LLViewerMediaFocus::getInstance()->unZoom(); +} + + +// static +void LLPanelNearByMedia::getNameAndUrlHelper(LLViewerMediaImpl* impl, std::string& name, std::string & url, const std::string &defaultName) +{ + if (NULL == impl) return; + + name = impl->getName(); + url = impl->getCurrentMediaURL(); // This is the URL the media impl actually has loaded + if (url.empty()) + { + url = impl->getMediaEntryURL(); // This is the current URL from the media data + } + if (url.empty()) + { + url = impl->getHomeURL(); // This is the home URL from the media data + } + if (name.empty()) + { + name = url; + } + if (name.empty()) + { + name = defaultName; + } +} + +// static +void* createNearbyMediaPanel(void* userdata) +{ + return new LLPanelNearByMedia(false); +} + +LLFloaterNearbyMedia::LLFloaterNearbyMedia() +{ + mFactoryMap["nearby_media"] = LLCallbackMap(createNearbyMediaPanel, this); + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_nearby_media.xml",&mFactoryMap,false); +} + +// static +void LLFloaterNearbyMedia::updateClass() +{ + if(gSavedSettings.getBOOL("ShowNearbyMediaFloater")) + LLFloaterNearbyMedia::getInstance()->open(); + else if(LLFloaterNearbyMedia::instanceExists()) + LLFloaterNearbyMedia::getInstance()->close(); +} + +void LLFloaterNearbyMedia::onClose(bool app_quitting) +{ + if(!app_quitting) + gSavedSettings.setBOOL("ShowNearbyMediaFloater", false); + LLFloater::onClose(app_quitting); +} + +void LLFloaterNearbyMedia::onOpen() +{ + //Mess around with control rect to not change unless the user actually tweaked it.. + LLRect rect = gSavedSettings.getRect("FloaterNearbyMediaRect"); + if(!rect.isEmpty()) + { + setRectControl("FloaterNearbyMediaRect"); + applyRectControl(); + } + else + { + const LLRect media_rect = gOverlayBar->mMediaRemote->calcScreenRect(); + setOrigin(media_rect.mLeft - getRect().getWidth(), media_rect.mBottom + gOverlayBar->mChatBar->getRect().getHeight()); + } + gSavedSettings.setBOOL("ShowNearbyMediaFloater", true); +} + +// virtual +void LLFloaterNearbyMedia::handleReshape(const LLRect& new_rect, bool by_user) +{ + if(by_user && getRectControl().empty()) + { + setRectControl("FloaterNearbyMediaRect"); + } + LLFloater::handleReshape(new_rect, by_user); +} + diff --git a/indra/newview/llpanelnearbymedia.h b/indra/newview/llpanelnearbymedia.h new file mode 100644 index 000000000..e6d0a4d9b --- /dev/null +++ b/indra/newview/llpanelnearbymedia.h @@ -0,0 +1,198 @@ +/** + * @file llpanelnearbymedia.h + * @brief Management interface for muting and controlling nearby media + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELNEARBYMEDIA_H +#define LL_LLPANELNEARBYMEDIA_H + +#include "llpanel.h" +#include "llfloater.h" +#include "llsingleton.h" + +class LLPanelNearbyMedia; +class LLButton; +class LLScrollListCtrl; +class LLSlider; +class LLSliderCtrl; +class LLCheckBoxCtrl; +class LLTextBox; +class LLComboBox; +class LLViewerMediaImpl; + +class LLPanelNearByMedia : public LLPanel +{ +public: + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ void onTopLost(); + /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + + // this is part of the nearby media *dialog* so we can track whether + // the user *implicitly* wants audio on or off via their *explicit* + // interaction with our buttons. + bool getParcelAudioAutoStart(); + + // callback for when the auto play media preference changes + // to update mParcelAudioAutoStart + void handleMediaAutoPlayChanged(const LLSD& newvalue); + + LLPanelNearByMedia(bool standalone_panel = true); + virtual ~LLPanelNearByMedia(); + +private: + + enum ColumnIndex { + CHECKBOX_COLUMN = 0, + PROXIMITY_COLUMN = 1, + VISIBILITY_COLUMN = 2, + CLASS_COLUMN = 3, + NAME_COLUMN = 4, + DEBUG_COLUMN = 5 + }; + + // Media "class" enumeration + enum MediaClass { + MEDIA_CLASS_ALL = 0, + MEDIA_CLASS_FOCUSED = 1, + MEDIA_CLASS_WITHIN_PARCEL = 2, + MEDIA_CLASS_OUTSIDE_PARCEL = 3, + MEDIA_CLASS_ON_OTHERS = 4 + }; + + // Add/remove an LLViewerMediaImpl to/from the list + LLScrollListItem* addListItem(const LLUUID &id); + void updateListItem(LLScrollListItem* item, LLViewerMediaImpl* impl); + void updateListItem(LLScrollListItem* item, + const std::string &item_name, + const std::string &item_tooltip, + S32 proximity, + bool is_disabled, + bool has_media, + bool is_time_based_and_playing, + MediaClass media_class, + const std::string &debug_str); + void removeListItem(const LLUUID &id); + + // Refresh the list in the UI + void refreshList(); + + void refreshParcelItems(); + + // UI Callbacks + void onClickEnableAll(); + void onClickDisableAll(); + void onClickEnableParcelMedia(); + void onClickDisableParcelMedia(); + void onClickMuteParcelMedia(); + void onParcelMediaVolumeSlider(); + void onClickParcelMediaPlay(); + void onClickParcelMediaStop(); + void onClickParcelMediaPause(); + void onClickParcelAudioPlay(); + void onClickParcelAudioStop(); + void onClickParcelAudioPause(); + void onCheckAutoPlay(); + void onAdvancedButtonClick(); + void onMoreLess(); + + void onCheckItem(LLUICtrl* ctrl, const LLUUID &row_id); + + static void onZoomMedia(void* user_data); + +private: + bool setDisabled(const LLUUID &id, bool disabled); + + static void getNameAndUrlHelper(LLViewerMediaImpl* impl, std::string& name, std::string & url, const std::string &defaultName); + + void updateColumns(); + + bool shouldShow(LLViewerMediaImpl* impl); + + void showBasicControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume); + void showTimeBasedControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume); + void showDisabledControls(); + void updateControls(); + + void onClickSelectedMediaStop(); + void onClickSelectedMediaPlay(); + void onClickSelectedMediaPause(); + void onClickSelectedMediaMute(); + void onCommitSelectedMediaVolume(); + void onClickSelectedMediaZoom(); + void onClickSelectedMediaUnzoom(); + + LLUICtrl* mNearbyMediaPanel; + LLScrollListCtrl* mMediaList; + LLUICtrl* mEnableAllCtrl; + LLUICtrl* mDisableAllCtrl; + LLComboBox* mShowCtrl; + + // Dynamic (selection-dependent) controls + LLUICtrl* mStopCtrl; + LLUICtrl* mPlayCtrl; + LLUICtrl* mPauseCtrl; + LLUICtrl* mMuteCtrl; + LLUICtrl* mVolumeSliderCtrl; + LLUICtrl* mZoomCtrl; + LLUICtrl* mUnzoomCtrl; + LLSlider* mVolumeSlider; + LLButton* mMuteBtn; + + bool mAllMediaDisabled; + bool mDebugInfoVisible; + bool mParcelAudioAutoStart; + std::string mEmptyNameString; + std::string mPlayingString; + std::string mParcelMediaName; + std::string mParcelAudioName; + + LLRect mMoreRect; + LLRect mLessRect; + LLFrameTimer mHoverTimer; + LLScrollListItem* mParcelMediaItem; + LLScrollListItem* mParcelAudioItem; + + bool mStandalonePanel; +}; + +class LLFloaterNearbyMedia : public LLFloater, public LLSingleton +{ +public: + LLFloaterNearbyMedia(); + + static void updateClass(); + + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void onOpen(); + + virtual void handleReshape(const LLRect& new_rect, bool by_user); +}; + +#endif // LL_LLPANELNEARBYMEDIA_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 44ba523b8..6224935fc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -4207,8 +4207,6 @@ bool process_login_success_response(std::string& password) LLViewerMedia::openIDSetup(openid_url, openid_token); } - gIMMgr->loadIgnoreGroup(); - bool success = false; // JC: gesture loading done below, when we have an asset system // in place. Don't delete/clear user_credentials until then. diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ea0ac8b94..f125d2ca5 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1175,9 +1175,6 @@ LLXMLNodePtr LLTextureCtrl::getXML(bool save_children) const LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("texture_picker"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent); @@ -1208,7 +1205,7 @@ LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor } LLTextureCtrl* texture_picker = new LLTextureCtrl( - name, + "texture_picker", rect, label, LLUUID(image_id), diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp index 6adff36c6..de7bfc767 100644 --- a/indra/newview/llviewermedia_streamingaudio.cpp +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -35,6 +35,7 @@ #include "linden_common.h" #include "llpluginclassmedia.h" +#include "llpluginclassmediaowner.h" #include "llviewermedia.h" #include "llviewermedia_streamingaudio.h" @@ -63,18 +64,18 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url) if (!mMediaPlugin) // lazy-init the underlying media plugin { mMediaPlugin = initializeMedia("audio/mpeg"); // assumes that whatever media implementation supports mp3 also supports vorbis. - llinfos << "mMediaPlugin is now " << mMediaPlugin << llendl; + llinfos << "streaming audio mMediaPlugin is now " << mMediaPlugin << llendl; } if(!mMediaPlugin) return; - + if (!url.empty()) { llinfos << "Starting internet stream: " << url << llendl; mURL = url; mMediaPlugin->loadURI ( url ); mMediaPlugin->start(); - llinfos << "Playing....." << llendl; + llinfos << "Playing stream..." << llendl; } else { llinfos << "setting stream to NULL"<< llendl; mURL.clear(); @@ -84,11 +85,9 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url) void LLStreamingAudio_MediaPlugins::stop() { - llinfos << "entered LLStreamingAudio_MediaPlugins::stop()" << llendl; - + llinfos << "Stopping internet stream." << llendl; if(mMediaPlugin) { - llinfos << "Stopping internet stream: " << mURL << llendl; mMediaPlugin->stop(); } @@ -102,10 +101,12 @@ void LLStreamingAudio_MediaPlugins::pause(int pause) if(pause) { + llinfos << "Pausing internet stream." << llendl; mMediaPlugin->pause(); } else { + llinfos << "Unpausing internet stream." << llendl; mMediaPlugin->start(); } } @@ -119,20 +120,21 @@ void LLStreamingAudio_MediaPlugins::update() int LLStreamingAudio_MediaPlugins::isPlaying() { if (!mMediaPlugin) - return 0; + return 0; // stopped - // *TODO: can probably do better than this - if (mMediaPlugin->isPluginRunning()) - { - return 1; // Active and playing - } + LLPluginClassMediaOwner::EMediaStatus status = + mMediaPlugin->getStatus(); - if (mMediaPlugin->isPluginExited()) + switch (status) { + case LLPluginClassMediaOwner::MEDIA_LOADING: // but not MEDIA_LOADED + case LLPluginClassMediaOwner::MEDIA_PLAYING: + return 1; // Active and playing + case LLPluginClassMediaOwner::MEDIA_PAUSED: + return 2; // paused + default: return 0; // stopped } - - return 2; // paused } void LLStreamingAudio_MediaPlugins::setGain(F32 vol) diff --git a/indra/newview/llviewerparcelmediaautoplay.cpp b/indra/newview/llviewerparcelmediaautoplay.cpp index 523fc0e29..ecd887440 100644 --- a/indra/newview/llviewerparcelmediaautoplay.cpp +++ b/indra/newview/llviewerparcelmediaautoplay.cpp @@ -35,12 +35,14 @@ #include "llviewerparcelmedia.h" #include "llviewercontrol.h" #include "llviewermedia.h" +#include "llviewerregion.h" #include "llparcel.h" #include "llviewerparcelmgr.h" #include "lluuid.h" #include "message.h" #include "llviewertexturelist.h" // for texture stats #include "llagent.h" +#include "llmimetypes.h" const F32 AUTOPLAY_TIME = 5; // how many seconds before we autoplay const F32 AUTOPLAY_SIZE = 24*24; // how big the texture must be (pixel area) before we autoplay @@ -48,6 +50,8 @@ const F32 AUTOPLAY_SPEED = 0.1f; // how slow should the agent be moving t LLViewerParcelMediaAutoPlay::LLViewerParcelMediaAutoPlay() : LLEventTimer(1), + + mLastParcelID(-1), mPlayed(FALSE), mTimeInParcel(0) { @@ -81,39 +85,54 @@ void LLViewerParcelMediaAutoPlay::playStarted() BOOL LLViewerParcelMediaAutoPlay::tick() { LLParcel *this_parcel = NULL; + LLViewerRegion *this_region = NULL; std::string this_media_url; + std::string this_media_type; LLUUID this_media_texture_id; S32 this_parcel_id = 0; + LLUUID this_region_id; + + this_region = gAgent.getRegion(); + + if (this_region) + { + this_region_id = this_region->getRegionID(); + } this_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (this_parcel) { - this_media_url = std::string(this_parcel->getMediaURL()); + this_media_url = this_parcel->getMediaURL(); + + this_media_type = this_parcel->getMediaType(); this_media_texture_id = this_parcel->getMediaID(); this_parcel_id = this_parcel->getLocalID(); } - if (this_parcel_id != mLastParcelID) + if (this_parcel_id != mLastParcelID || + this_region_id != mLastRegionID) { // we've entered a new parcel mPlayed = FALSE; // we haven't autoplayed yet mTimeInParcel = 0; // reset our timer mLastParcelID = this_parcel_id; + mLastRegionID = this_region_id; } - mTimeInParcel += mPeriod; // increase mTimeInParcel by the amount of time between ticks + mTimeInParcel += mPeriod; // increase mTimeInParcel by the amount of time between ticks - if ((!mPlayed) && // if we've never played - (mTimeInParcel > AUTOPLAY_TIME) && // and if we've been here for so many seconds - (this_media_url.size() != 0) && // and if the parcel has media - (LLViewerParcelMedia::sMediaImpl.isNull())) // and if the media is not already playing + if ((!mPlayed) && // if we've never played + (mTimeInParcel > AUTOPLAY_TIME) && // and if we've been here for so many seconds + (!this_media_url.empty()) && // and if the parcel has media + (stricmp(this_media_type.c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) && + (LLViewerParcelMedia::sMediaImpl.isNull())) // and if the media is not already playing { - if (this_media_texture_id.notNull()) // and if the media texture is good + if (this_media_texture_id.notNull()) // and if the media texture is good { - LLViewerTexture *image = LLViewerTextureManager::getFetchedTexture(this_media_texture_id, FALSE); + LLViewerMediaTexture *image = LLViewerTextureManager::getMediaTexture(this_media_texture_id, FALSE) ; F32 image_size = 0; diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h index 243da2f38..40142c1dd 100644 --- a/indra/newview/llviewerparcelmediaautoplay.h +++ b/indra/newview/llviewerparcelmediaautoplay.h @@ -34,6 +34,7 @@ #define LLVIEWERPARCELMEDIAAUTOPLAY_H #include "lleventtimer.h" +#include "lluuid.h" // timer to automatically play media class LLViewerParcelMediaAutoPlay : LLEventTimer @@ -47,6 +48,7 @@ class LLViewerParcelMediaAutoPlay : LLEventTimer private: S32 mLastParcelID; + LLUUID mLastRegionID; BOOL mPlayed; F32 mTimeInParcel; }; diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index d12b63997..587c67dda 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -1625,9 +1625,6 @@ LLXMLNodePtr LLViewerTextEditor::getXML(bool save_children) const LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -1648,7 +1645,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF text.erase(max_text_length); } - LLViewerTextEditor* text_editor = new LLViewerTextEditor(name, + LLViewerTextEditor* text_editor = new LLViewerTextEditor("text_editor", rect, max_text_length, LLStringUtil::null, diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 2ef25f6c1..122ac3f10 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -199,6 +199,8 @@ #include "llfloatertest.h" // HACK! #include "llfloaternotificationsconsole.h" +#include "llpanelnearbymedia.h" + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -2107,11 +2109,24 @@ void LLViewerWindow::adjustControlRectanglesForFirstUse(const LLRect& window) void LLViewerWindow::initWorldUI() { pre_init_menus(); + if(!gMenuHolder) + { + // + // Tools for building + // + init_menus(); + } +} +// initWorldUI that wasn't before logging in. Some of this may require the access the 'LindenUserDir'. +void LLViewerWindow::initWorldUI_postLogin() +{ S32 height = mRootView->getRect().getHeight(); S32 width = mRootView->getRect().getWidth(); LLRect full_window(0, height, width, 0); + //============================================ + //Begin LLViewerWindow::initWorlUI // Don't re-enter if objects are alreay created if (gBottomPanel == NULL) { @@ -2119,18 +2134,15 @@ void LLViewerWindow::initWorldUI() gBottomPanel = new LLBottomPanel(mRootView->getRect()); mRootView->addChild(gBottomPanel); + LLFloaterNearbyMedia::updateClass(); //Dependent on the overlay panel being fully initialized. + // View for hover information gHoverView = new LLHoverView(std::string("gHoverView"), full_window); gHoverView->setVisible(TRUE); mRootView->addChild(gHoverView); gIMMgr = LLIMMgr::getInstance(); - - // - // Tools for building - // - - init_menus(); + gIMMgr->loadIgnoreGroup(); // Toolbox floater gFloaterTools = new LLFloaterTools(); @@ -2149,19 +2161,16 @@ void LLViewerWindow::initWorldUI() // put behind everything else in the UI mRootView->addChildInBack(gHUDView); } + //End LLViewerWindow::initWorlUI + //============================================ + LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container"); panel_ssf_container->setVisible(TRUE); LLMenuOptionPathfindingRebakeNavmesh::getInstance()->initialize(); -} -// initWorldUI that wasn't before logging in. Some of this may require the access the 'LindenUserDir'. -void LLViewerWindow::initWorldUI_postLogin() -{ - S32 height = mRootView->getRect().getHeight(); - S32 width = mRootView->getRect().getWidth(); - LLRect full_window(0, height, width, 0); + // Don't re-enter if objects are alreay created. if (!gStatusBar) @@ -2236,7 +2245,8 @@ void LLViewerWindow::shutdownViews() mRootView = NULL; llinfos << "RootView deleted." << llendl ; - LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); + if(LLMenuOptionPathfindingRebakeNavmesh::instanceExists()) + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); // Automatically deleted as children of mRootView. Fix the globals. gFloaterTools = NULL; @@ -3440,7 +3450,7 @@ void LLViewerWindow::updateLayout() } // Update rectangles for the various toolbars - if (gOverlayBar && gNotifyBoxView && gConsole && gToolBar) + if (gOverlayBar && gNotifyBoxView && gConsole && gToolBar && gHUDView) { LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1); diff --git a/indra/newview/skins/default/xui/en-us/floater_nearby_media.xml b/indra/newview/skins/default/xui/en-us/floater_nearby_media.xml new file mode 100644 index 000000000..2b9ecafd8 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_nearby_media.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/en-us/floater_tools.xml b/indra/newview/skins/default/xui/en-us/floater_tools.xml index e08bd1703..349a2dffd 100644 --- a/indra/newview/skins/default/xui/en-us/floater_tools.xml +++ b/indra/newview/skins/default/xui/en-us/floater_tools.xml @@ -483,13 +483,14 @@ mouse_opaque="true" name="Group:" v_pad="0" width="78"> Group: - - The Lindens - + + + + + + + + + Show: + + + +All + + +In this Parcel + + +Outside this Parcel + + +On other Avatars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_overlaybar.xml b/indra/newview/skins/default/xui/en-us/panel_overlaybar.xml index 97acc4768..a10f0b74a 100644 --- a/indra/newview/skins/default/xui/en-us/panel_overlaybar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_overlaybar.xml @@ -50,7 +50,7 @@ + width="96"> + tool_tip="Media is Loading"/> + width="22" + tool_tip="Navigate forward"> @@ -130,6 +132,7 @@ follows="top" image_overlay="go-home.png" label="" + tool_tip="Home page" bottom="-22" height="22" width="22"> @@ -151,6 +154,7 @@ follows="top" image_overlay="go-media-stop.png" label="" + tool_tip="Stop media" bottom="-22" height="22" width="22"> @@ -172,6 +176,7 @@ follows="top" image_overlay="go-reload.png" label="" + tool_tip="Reload" bottom="-22" height="22" width="22"> @@ -193,6 +198,7 @@ follows="top" image_overlay="go-stop.png" label="" + tool_tip = "Stop loading" bottom="-22" height="22" width="22"> @@ -214,6 +220,7 @@ follows="top" image_overlay="go-media-play.png" label="" + tool_tip = "Play media" bottom="-22" height="22" width="22"> @@ -258,6 +265,7 @@ follows="top|left|right" height="22" bottom="-22" + tool_tip="Media URL" text_pad_right="16"> @@ -269,7 +277,7 @@ height="20" width="38" left="140" - bottom="0" + bottom="0" border_size="0" mouse_opaque="false" orientation="horizontal"> @@ -284,6 +292,7 @@ height="16" image_name="Flag.png" layout="topleft" + tool_tip="White List enabled" bottom="-16" width="16" /> @@ -297,6 +306,7 @@ height="16" image_name="lock.png" bottom="-16" + tool_tip="Secured Browsing" width="16" /> @@ -317,6 +327,7 @@ height="22" increment="0.01" initial_value="0.5" + tool_tip="Movie play progress" width="200"> @@ -337,6 +348,7 @@ bottom="-22" auto_resize="false" height="22" + tool_tip="Step back" width="22"> @@ -356,6 +368,7 @@ label="" bottom="-22" height="22" + tool_tip="Step forward" width="22"> @@ -383,6 +396,7 @@ hover_glow_amount="0.15" is_toggle="true" scale_image="false" + tool_tip="Mute This Media" left_delta="5" bottom="-22" draw_border="true" @@ -430,6 +444,7 @@ label="" bottom="-22" height="22" + tool_tip="Zoom into media" width="22"> @@ -449,6 +464,7 @@ image_overlay="go-media-unzoom.png" label="" height="22" + tool_tip ="Zoom Back" bottom="-22" width="21"> diff --git a/indra/newview/skins/default/xui/es/floater_tools.xml b/indra/newview/skins/default/xui/es/floater_tools.xml index 02f4d726e..37d162266 100644 --- a/indra/newview/skins/default/xui/es/floater_tools.xml +++ b/indra/newview/skins/default/xui/es/floater_tools.xml @@ -135,9 +135,7 @@ Grupo: - - The Lindens - + + name="radio zoom" radio_style="true" width="114" > + + + + mouse_opaque="true" name="slider zoom" width="134" > + + + name="radio orbit" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio pan" radio_style="true" width="114" > + + + name="radio move" radio_style="true" width="114" > + + + name="radio lift" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio spin" radio_style="true" width="114" > + + + name="radio position" radio_style="true" width="114" > + + + name="radio rotate" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio stretch" radio_style="true" width="123" > + + + name="radio select face" radio_style="true" width="114" > + + + name="radio align" radio_style="true" width="114" > + + + name="checkbox edit linked parts" width="114"> + + Reference + @@ -249,29 +314,53 @@ + name="radio select land" radio_style="true" width="114" > + + + name="radio flatten" radio_style="true" width="114" > + + + name="radio raise" radio_style="true" width="114" > + + + name="radio lower" radio_style="true" width="114" > + + + name="radio smooth" radio_style="true" width="114" > + + + name="radio noise" radio_style="true" width="114" > + + + name="radio revert" radio_style="true" width="114" > + + Group: - - The Lindens - + + + + + + + + + + + + + + + Note: Residents can override this setting + + + + + + Size: + + + + + + X + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml new file mode 100644 index 000000000..d3a58fed5 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml @@ -0,0 +1,172 @@ + + + + + Controls: + + + + Standard + + + Mini + + + + + Owner + + + + + + + + Group: + + + + + + + + + + Anyone + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml new file mode 100644 index 000000000..828635262 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml @@ -0,0 +1,91 @@ + + + + + + + Entries that the home page fails against are marked: + + + + +Warning: the home page specified in the General tab fails to +pass this whitelist. It has been disabled until a valid entry +has been added. + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml b/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml new file mode 100644 index 000000000..f4ad5defe --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml @@ -0,0 +1,382 @@ + + + (%ld media items) + <empty> + Parcel Streaming Media + Parcel Streaming Audio + (playing) + + + + + + + + + + Show: + + + +All + + +In this Parcel + + +Outside this Parcel + + +On other Avatars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml b/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml index 4e2339287..a5e734b12 100644 --- a/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml +++ b/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml @@ -1,16 +1,20 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml index 38178813f..4c6b39881 100644 --- a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml @@ -2,19 +2,45 @@ Redock Windows - - -