From ef3e91855880c4865367ef74908b88914a1f2cdc Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 31 May 2013 05:33:41 -0500 Subject: [PATCH] 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 @@