diff options
Diffstat (limited to 'indra')
216 files changed, 6895 insertions, 2701 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3c689930b8..60e88a1cfb 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -32,6 +32,7 @@ set(llcommon_SOURCE_FILES llapp.cpp llapr.cpp llassettype.cpp + llavatarname.cpp llbase32.cpp llbase64.cpp llcommon.cpp @@ -114,6 +115,7 @@ set(llcommon_HEADER_FILES llallocator.h llallocator_heap_profile.h llagentconstants.h + llavatarname.h llapp.h llapr.h llassettype.h diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp new file mode 100644 index 0000000000..14dc41591b --- /dev/null +++ b/indra/llcommon/llavatarname.cpp @@ -0,0 +1,119 @@ +/** + * @file llavatarname.cpp + * @brief Represents name-related data for an avatar, such as the + * username/SLID ("bobsmith123" or "james.linden") and the display + * name ("James Cook") + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "linden_common.h" + +#include "llavatarname.h" + +#include "lldate.h" +#include "llsd.h" + +// Store these in pre-built std::strings to avoid memory allocations in +// LLSD map lookups +static const std::string USERNAME("username"); +static const std::string DISPLAY_NAME("display_name"); +static const std::string LEGACY_FIRST_NAME("legacy_first_name"); +static const std::string LEGACY_LAST_NAME("legacy_last_name"); +static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default"); +static const std::string DISPLAY_NAME_EXPIRES("display_name_expires"); +static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update"); + +LLAvatarName::LLAvatarName() +: mUsername(), + mDisplayName(), + mLegacyFirstName(), + mLegacyLastName(), + mIsDisplayNameDefault(false), + mIsDummy(false), + mExpires(F64_MAX), + mNextUpdate(0.0) +{ } + +bool LLAvatarName::operator<(const LLAvatarName& rhs) const +{ + if (mUsername == rhs.mUsername) + return mDisplayName < rhs.mDisplayName; + else + return mUsername < rhs.mUsername; +} + +LLSD LLAvatarName::asLLSD() const +{ + LLSD sd; + sd[USERNAME] = mUsername; + sd[DISPLAY_NAME] = mDisplayName; + sd[LEGACY_FIRST_NAME] = mLegacyFirstName; + sd[LEGACY_LAST_NAME] = mLegacyLastName; + sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault; + sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires); + sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate); + return sd; +} + +void LLAvatarName::fromLLSD(const LLSD& sd) +{ + mUsername = sd[USERNAME].asString(); + mDisplayName = sd[DISPLAY_NAME].asString(); + mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString(); + mLegacyLastName = sd[LEGACY_LAST_NAME].asString(); + mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean(); + LLDate expires = sd[DISPLAY_NAME_EXPIRES]; + mExpires = expires.secondsSinceEpoch(); + LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE]; + mNextUpdate = next_update.secondsSinceEpoch(); +} + +std::string LLAvatarName::getCompleteName() const +{ + std::string name; + if (!mUsername.empty()) + { + name = mDisplayName + " (" + mUsername + ")"; + } + else + { + // ...display names are off, legacy name is in mDisplayName + name = mDisplayName; + } + return name; +} + +std::string LLAvatarName::getLegacyName() const +{ + std::string name; + name.reserve( mLegacyFirstName.size() + 1 + mLegacyLastName.size() ); + name = mLegacyFirstName; + name += " "; + name += mLegacyLastName; + return name; +} diff --git a/indra/llcommon/llavatarname.h b/indra/llcommon/llavatarname.h new file mode 100644 index 0000000000..650a09a125 --- /dev/null +++ b/indra/llcommon/llavatarname.h @@ -0,0 +1,101 @@ +/** + * @file llavatarname.h + * @brief Represents name-related data for an avatar, such as the + * username/SLID ("bobsmith123" or "james.linden") and the display + * name ("James Cook") + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#ifndef LLAVATARNAME_H +#define LLAVATARNAME_H + +#include <string> + +class LLSD; + +class LL_COMMON_API LLAvatarName +{ +public: + LLAvatarName(); + + bool operator<(const LLAvatarName& rhs) const; + + LLSD asLLSD() const; + + void fromLLSD(const LLSD& sd); + + // For normal names, returns "James Linden (james.linden)" + // When display names are disabled returns just "James Linden" + std::string getCompleteName() const; + + // Returns "James Linden" or "bobsmith123 Resident" for backwards + // compatibility with systems like voice and muting + // *TODO: Eliminate this in favor of username only + std::string getLegacyName() const; + + // "bobsmith123" or "james.linden", US-ASCII only + std::string mUsername; + + // "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode + // Contains data whether or not user has explicitly set + // a display name; may duplicate their username. + std::string mDisplayName; + + // For "James Linden", "James" + // For "bobsmith123", "bobsmith123" + // Used to communicate with legacy systems like voice and muting which + // rely on old-style names. + // *TODO: Eliminate this in favor of username only + std::string mLegacyFirstName; + + // For "James Linden", "Linden" + // For "bobsmith123", "Resident" + // see above for rationale + std::string mLegacyLastName; + + // If true, both display name and SLID were generated from + // a legacy first and last name, like "James Linden (james.linden)" + bool mIsDisplayNameDefault; + + // Under error conditions, we may insert "dummy" records with + // names like "???" into caches as placeholders. These can be + // shown in UI, but are not serialized. + bool mIsDummy; + + // Names can change, so need to keep track of when name was + // last checked. + // Unix time-from-epoch seconds for efficiency + F64 mExpires; + + // You can only change your name every N hours, so record + // when the next update is allowed + // Unix time-from-epoch seconds + F64 mNextUpdate; +}; + +#endif diff --git a/indra/llcommon/llchat.h b/indra/llcommon/llchat.h index 63cce24005..2cc6e5c9bc 100644 --- a/indra/llcommon/llchat.h +++ b/indra/llcommon/llchat.h @@ -34,7 +34,6 @@ #ifndef LL_LLCHAT_H #define LL_LLCHAT_H -#include "llstring.h" #include "lluuid.h" #include "v3math.h" @@ -77,7 +76,7 @@ typedef enum e_chat_style class LLChat { public: - LLChat(const std::string& text = LLStringUtil::null) + LLChat(const std::string& text = std::string()) : mText(text), mFromName(), mFromID(), diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index f14d947734..8277928d4d 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -705,14 +705,17 @@ void LLStringOps::setupDatetimeInfo (bool daylight) nowT = time (NULL); - tmpT = localtime (&nowT); - localT = mktime (tmpT); - tmpT = gmtime (&nowT); gmtT = mktime (tmpT); + tmpT = localtime (&nowT); + localT = mktime (tmpT); + sLocalTimeOffset = (long) (gmtT - localT); - + if (tmpT->tm_isdst) + { + sLocalTimeOffset -= 60 * 60; // 1 hour + } sPacificDaylightTime = daylight; sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60; diff --git a/indra/llinventory/lltransactionflags.cpp b/indra/llinventory/lltransactionflags.cpp index e0f87aa7db..79f8589bb1 100644 --- a/indra/llinventory/lltransactionflags.cpp +++ b/indra/llinventory/lltransactionflags.cpp @@ -114,6 +114,9 @@ std::string build_transfer_message_to_source( std::ostringstream ostr; if(dest_id.isNull()) { + // *NOTE: Do not change these strings! The viewer matches + // them in llviewermessage.cpp to perform localization. + // If you need to make changes, add a new, localizable message. JC ostr << "You paid L$" << amount; switch(transaction_type) { @@ -160,6 +163,9 @@ std::string build_transfer_message_to_destination( return description; } std::ostringstream ostr; + // *NOTE: Do not change these strings! The viewer matches + // them in llviewermessage.cpp to perform localization. + // If you need to make changes, add a new, localizable message. JC ostr << source_name << " paid you L$" << amount; append_reason(ostr, transaction_type, description); ostr << "."; diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 1f8ee26716..1cad0f6d22 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -25,6 +25,7 @@ set(llmessage_SOURCE_FILES llares.cpp llareslistener.cpp llassetstorage.cpp + llavatarnamecache.cpp llblowfishcipher.cpp llbuffer.cpp llbufferstream.cpp @@ -110,6 +111,7 @@ set(llmessage_HEADER_FILES llares.h llareslistener.h llassetstorage.h + llavatarnamecache.h llblowfishcipher.h llbuffer.h llbufferstream.h @@ -248,6 +250,7 @@ if (LL_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" ) + LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}") diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp new file mode 100644 index 0000000000..912f34fcae --- /dev/null +++ b/indra/llmessage/llavatarnamecache.cpp @@ -0,0 +1,795 @@ +/** + * @file llavatarnamecache.cpp + * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names + * ("James Cook") from avatar UUIDs. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "linden_common.h" + +#include "llavatarnamecache.h" + +#include "llcachename.h" // we wrap this system +#include "llframetimer.h" +#include "llhttpclient.h" +#include "llsd.h" +#include "llsdserialize.h" + +#include <boost/tokenizer.hpp> + +#include <map> +#include <set> + +namespace LLAvatarNameCache +{ + // Manual override for display names - can disable even if the region + // supports it. + bool sUseDisplayNames = true; + + // Cache starts in a paused state until we can determine if the + // current region supports display names. + bool sRunning = false; + + // Base lookup URL for name service. + // On simulator, loaded from indra.xml + // On viewer, usually a simulator capability (at People API team's request) + // Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/" + std::string sNameLookupURL; + + // accumulated agent IDs for next query against service + typedef std::set<LLUUID> ask_queue_t; + ask_queue_t sAskQueue; + + // agent IDs that have been requested, but with no reply + // maps agent ID to frame time request was made + typedef std::map<LLUUID, F64> pending_queue_t; + pending_queue_t sPendingQueue; + + // Callbacks to fire when we received a name. + // May have multiple callbacks for a single ID, which are + // represented as multiple slots bound to the signal. + // Avoid copying signals via pointers. + typedef std::map<LLUUID, callback_signal_t*> signal_map_t; + signal_map_t sSignalMap; + + // names we know about + typedef std::map<LLUUID, LLAvatarName> cache_t; + cache_t sCache; + + // Send bulk lookup requests a few times a second at most + // only need per-frame timing resolution + LLFrameTimer sRequestTimer; + + // Periodically clean out expired entries from the cache + LLFrameTimer sEraseExpiredTimer; + + //----------------------------------------------------------------------- + // Internal methods + //----------------------------------------------------------------------- + + // Handle name response off network. + // Optionally skip adding to cache, used when this is a fallback to the + // legacy name system. + void processName(const LLUUID& agent_id, + const LLAvatarName& av_name, + bool add_to_cache); + + void requestNamesViaCapability(); + + // Legacy name system callback + void legacyNameCallback(const LLUUID& agent_id, + const std::string& full_name, + bool is_group); + + void requestNamesViaLegacy(); + + // Fill in an LLAvatarName with the legacy name data + void buildLegacyName(const std::string& full_name, + LLAvatarName* av_name); + + // Do a single callback to a given slot + void fireSignal(const LLUUID& agent_id, + const callback_slot_t& slot, + const LLAvatarName& av_name); + + // Is a request in-flight over the network? + bool isRequestPending(const LLUUID& agent_id); + + // Erase expired names from cache + void eraseExpired(); + + bool expirationFromCacheControl(LLSD headers, F64 *expires); +} + +/* Sample response: +<?xml version="1.0"?> +<llsd> + <map> + <key>agents</key> + <array> + <map> + <key>display_name_next_update</key> + <date>2010-04-16T21:34:02+00:00Z</date> + <key>display_name_expires</key> + <date>2010-04-16T21:32:26.142178+00:00Z</date> + <key>display_name</key> + <string>MickBot390 LLQABot</string> + <key>sl_id</key> + <string>mickbot390.llqabot</string> + <key>id</key> + <string>0012809d-7d2d-4c24-9609-af1230a37715</string> + <key>is_display_name_default</key> + <boolean>false</boolean> + </map> + <map> + <key>display_name_next_update</key> + <date>2010-04-16T21:34:02+00:00Z</date> + <key>display_name_expires</key> + <date>2010-04-16T21:32:26.142178+00:00Z</date> + <key>display_name</key> + <string>Bjork Gudmundsdottir</string> + <key>sl_id</key> + <string>sardonyx.linden</string> + <key>id</key> + <string>3941037e-78ab-45f0-b421-bd6e77c1804d</string> + <key>is_display_name_default</key> + <boolean>true</boolean> + </map> + </array> + </map> +</llsd> +*/ + +class LLAvatarNameResponder : public LLHTTPClient::Responder +{ +private: + // need to store agent ids that are part of this request in case of + // an error, so we can flag them as unavailable + std::vector<LLUUID> mAgentIDs; + + // Need the headers to look up Expires: and Retry-After: + LLSD mHeaders; + +public: + LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids) + : mAgentIDs(agent_ids), + mHeaders() + { } + + /*virtual*/ void completedHeader(U32 status, const std::string& reason, + const LLSD& headers) + { + mHeaders = headers; + } + + /*virtual*/ void result(const LLSD& content) + { + // Pull expiration out of headers if available + F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders); + + LLSD agents = content["agents"]; + LLSD::array_const_iterator it = agents.beginArray(); + for ( ; it != agents.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID agent_id = row["id"].asUUID(); + + LLAvatarName av_name; + av_name.fromLLSD(row); + + // Use expiration time from header + av_name.mExpires = expires; + + // Some avatars don't have explicit display names set + if (av_name.mDisplayName.empty()) + { + av_name.mDisplayName = av_name.mUsername; + } + + // cache it and fire signals + LLAvatarNameCache::processName(agent_id, av_name, true); + } + + // Same logic as error response case + LLSD unresolved_agents = content["bad_ids"]; + if (unresolved_agents.size() > 0) + { + const std::string DUMMY_NAME("\?\?\?"); + LLAvatarName av_name; + av_name.mUsername = DUMMY_NAME; + av_name.mDisplayName = DUMMY_NAME; + av_name.mIsDisplayNameDefault = false; + av_name.mIsDummy = true; + av_name.mExpires = expires; + + it = unresolved_agents.beginArray(); + for ( ; it != unresolved_agents.endArray(); ++it) + { + const LLUUID& agent_id = *it; + // cache it and fire signals + LLAvatarNameCache::processName(agent_id, av_name, true); + } + } + } + + /*virtual*/ void error(U32 status, const std::string& reason) + { + // We're going to construct a dummy record and cache it for a while, + // either briefly for a 503 Service Unavailable, or longer for other + // errors. + F64 retry_timestamp = errorRetryTimestamp(status); + + // *NOTE: "??" starts trigraphs in C/C++, escape the question marks. + const std::string DUMMY_NAME("\?\?\?"); + LLAvatarName av_name; + av_name.mUsername = DUMMY_NAME; + av_name.mDisplayName = DUMMY_NAME; + av_name.mIsDisplayNameDefault = false; + av_name.mIsDummy = true; + av_name.mExpires = retry_timestamp; + + // Add dummy records for all agent IDs in this request + std::vector<LLUUID>::const_iterator it = mAgentIDs.begin(); + for ( ; it != mAgentIDs.end(); ++it) + { + const LLUUID& agent_id = *it; + // cache it and fire signals + LLAvatarNameCache::processName(agent_id, av_name, true); + } + } + + // Return time to retry a request that generated an error, based on + // error type and headers. Return value is seconds-since-epoch. + F64 errorRetryTimestamp(S32 status) + { + F64 now = LLFrameTimer::getTotalSeconds(); + + // Retry-After takes priority + LLSD retry_after = mHeaders["retry-after"]; + if (retry_after.isDefined()) + { + // We only support the delta-seconds type + S32 delta_seconds = retry_after.asInteger(); + if (delta_seconds > 0) + { + // ...valid delta-seconds + return now + F64(delta_seconds); + } + } + + // If no Retry-After, look for Cache-Control max-age + F64 expires = 0.0; + if (LLAvatarNameCache::expirationFromCacheControl(mHeaders, &expires)) + { + return expires; + } + + // No information in header, make a guess + if (status == 503) + { + // ...service unavailable, retry soon + const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min + return now + SERVICE_UNAVAILABLE_DELAY; + } + else + { + // ...other unexpected error + const F64 DEFAULT_DELAY = 3600.0; // 1 hour + return now + DEFAULT_DELAY; + } + } +}; + +void LLAvatarNameCache::processName(const LLUUID& agent_id, + const LLAvatarName& av_name, + bool add_to_cache) +{ + if (add_to_cache) + { + sCache[agent_id] = av_name; + } + + sPendingQueue.erase(agent_id); + + // signal everyone waiting on this name + signal_map_t::iterator sig_it = sSignalMap.find(agent_id); + if (sig_it != sSignalMap.end()) + { + callback_signal_t* signal = sig_it->second; + (*signal)(agent_id, av_name); + + sSignalMap.erase(agent_id); + + delete signal; + signal = NULL; + } +} + +void LLAvatarNameCache::requestNamesViaCapability() +{ + F64 now = LLFrameTimer::getTotalSeconds(); + + // URL format is like: + // http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0 + // + // Apache can handle URLs of 4096 chars, but let's be conservative + const U32 NAME_URL_MAX = 4096; + const U32 NAME_URL_SEND_THRESHOLD = 3000; + std::string url; + url.reserve(NAME_URL_MAX); + + std::vector<LLUUID> agent_ids; + agent_ids.reserve(128); + + ask_queue_t::const_iterator it = sAskQueue.begin(); + for ( ; it != sAskQueue.end(); ++it) + { + const LLUUID& agent_id = *it; + + if (url.empty()) + { + // ...starting new request + url += sNameLookupURL; + url += "?ids="; + } + else + { + // ...continuing existing request + url += "&ids="; + } + url += agent_id.asString(); + agent_ids.push_back(agent_id); + + // mark request as pending + sPendingQueue[agent_id] = now; + + if (url.size() > NAME_URL_SEND_THRESHOLD) + { + //llinfos << "requestNames " << url << llendl; + LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); + url.clear(); + agent_ids.clear(); + } + } + + if (!url.empty()) + { + //llinfos << "requestNames " << url << llendl; + LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); + url.clear(); + agent_ids.clear(); + } + + // We've moved all asks to the pending request queue + sAskQueue.clear(); +} + +void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, + const std::string& full_name, + bool is_group) +{ + // Construct a dummy record for this name. By convention, SLID is blank + // Never expires, but not written to disk, so lasts until end of session. + LLAvatarName av_name; + buildLegacyName(full_name, &av_name); + + // Don't add to cache, the data already exists in the legacy name system + // cache and we don't want or need duplicate storage, because keeping the + // two copies in sync is complex. + processName(agent_id, av_name, false); +} + +void LLAvatarNameCache::requestNamesViaLegacy() +{ + F64 now = LLFrameTimer::getTotalSeconds(); + std::string full_name; + ask_queue_t::const_iterator it = sAskQueue.begin(); + for (; it != sAskQueue.end(); ++it) + { + const LLUUID& agent_id = *it; + + // Mark as pending first, just in case the callback is immediately + // invoked below. This should never happen in practice. + sPendingQueue[agent_id] = now; + + gCacheName->get(agent_id, false, // legacy compatibility + boost::bind(&LLAvatarNameCache::legacyNameCallback, + _1, _2, _3)); + } + + // We've either answered immediately or moved all asks to the + // pending queue + sAskQueue.clear(); +} + +void LLAvatarNameCache::initClass(bool running) +{ + sRunning = running; +} + +void LLAvatarNameCache::cleanupClass() +{ +} + +void LLAvatarNameCache::importFile(std::istream& istr) +{ + LLSD data; + S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); + if (parse_count < 1) return; + + // by convention LLSD storage is a map + // we only store one entry in the map + LLSD agents = data["agents"]; + + LLUUID agent_id; + LLAvatarName av_name; + LLSD::map_const_iterator it = agents.beginMap(); + for ( ; it != agents.endMap(); ++it) + { + agent_id.set(it->first); + av_name.fromLLSD( it->second ); + sCache[agent_id] = av_name; + } + // entries may have expired since we last ran the viewer, just + // clean them out now + eraseExpired(); + llinfos << "loaded " << sCache.size() << llendl; +} + +void LLAvatarNameCache::exportFile(std::ostream& ostr) +{ + LLSD agents; + cache_t::const_iterator it = sCache.begin(); + for ( ; it != sCache.end(); ++it) + { + const LLUUID& agent_id = it->first; + const LLAvatarName& av_name = it->second; + if (!av_name.mIsDummy) + { + // key must be a string + agents[agent_id.asString()] = av_name.asLLSD(); + } + } + LLSD data; + data["agents"] = agents; + LLSDSerialize::toPrettyXML(data, ostr); +} + +void LLAvatarNameCache::setNameLookupURL(const std::string& name_lookup_url) +{ + sNameLookupURL = name_lookup_url; +} + +bool LLAvatarNameCache::hasNameLookupURL() +{ + return !sNameLookupURL.empty(); +} + +void LLAvatarNameCache::idle() +{ + // By convention, start running at first idle() call + sRunning = true; + + // *TODO: Possibly re-enabled this based on People API load measurements + // 100 ms is the threshold for "user speed" operations, so we can + // stall for about that long to batch up requests. + //const F32 SECS_BETWEEN_REQUESTS = 0.1f; + //if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) + //{ + // return; + //} + + // Must be large relative to above + const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds + if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + { + eraseExpired(); + } + + if (sAskQueue.empty()) + { + return; + } + + if (useDisplayNames()) + { + requestNamesViaCapability(); + } + else + { + // ...fall back to legacy name cache system + requestNamesViaLegacy(); + } +} + +bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id) +{ + const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; + F64 now = LLFrameTimer::getTotalSeconds(); + F64 expire_time = now - PENDING_TIMEOUT_SECS; + + pending_queue_t::const_iterator it = sPendingQueue.find(agent_id); + if (it != sPendingQueue.end()) + { + bool request_expired = (it->second < expire_time); + return !request_expired; + } + return false; +} + +void LLAvatarNameCache::eraseExpired() +{ + F64 now = LLFrameTimer::getTotalSeconds(); + cache_t::iterator it = sCache.begin(); + while (it != sCache.end()) + { + cache_t::iterator cur = it; + ++it; + const LLAvatarName& av_name = cur->second; + if (av_name.mExpires < now) + { + sCache.erase(cur); + } + } +} + +void LLAvatarNameCache::buildLegacyName(const std::string& full_name, + LLAvatarName* av_name) +{ + llassert(av_name); + av_name->mUsername = ""; + av_name->mDisplayName = full_name; + av_name->mIsDisplayNameDefault = true; + av_name->mIsDummy = true; + av_name->mExpires = F64_MAX; +} + +bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) +{ + if (sRunning) + { + // ...only do immediate lookups when cache is running + if (useDisplayNames()) + { + // ...use display names cache + std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id); + if (it != sCache.end()) + { + *av_name = it->second; + return true; + } + } + else + { + // ...use legacy names cache + std::string full_name; + if (gCacheName->getFullName(agent_id, full_name)) + { + buildLegacyName(full_name, av_name); + return true; + } + } + } + + if (!isRequestPending(agent_id)) + { + sAskQueue.insert(agent_id); + } + + return false; +} + +void LLAvatarNameCache::fireSignal(const LLUUID& agent_id, + const callback_slot_t& slot, + const LLAvatarName& av_name) +{ + callback_signal_t signal; + signal.connect(slot); + signal(agent_id, av_name); +} + +void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) +{ + if (sRunning) + { + // ...only do immediate lookups when cache is running + if (useDisplayNames()) + { + // ...use new cache + std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id); + if (it != sCache.end()) + { + // ...name already exists in cache, fire callback now + fireSignal(agent_id, slot, it->second); + return; + } + } + else + { + // ...use old name system + std::string full_name; + if (gCacheName->getFullName(agent_id, full_name)) + { + LLAvatarName av_name; + buildLegacyName(full_name, &av_name); + fireSignal(agent_id, slot, av_name); + return; + } + } + } + + // schedule a request + if (!isRequestPending(agent_id)) + { + sAskQueue.insert(agent_id); + } + + // always store additional callback, even if request is pending + signal_map_t::iterator sig_it = sSignalMap.find(agent_id); + if (sig_it == sSignalMap.end()) + { + // ...new callback for this id + callback_signal_t* signal = new callback_signal_t(); + signal->connect(slot); + sSignalMap[agent_id] = signal; + } + else + { + // ...existing callback, bind additional slot + callback_signal_t* signal = sig_it->second; + signal->connect(slot); + } +} + + +void LLAvatarNameCache::setUseDisplayNames(bool use) +{ + if (use != sUseDisplayNames) + { + sUseDisplayNames = use; + // flush our cache + sCache.clear(); + } +} + +bool LLAvatarNameCache::useDisplayNames() +{ + // Must be both manually set on and able to look up names. + return sUseDisplayNames && !sNameLookupURL.empty(); +} + +void LLAvatarNameCache::erase(const LLUUID& agent_id) +{ + sCache.erase(agent_id); +} + +void LLAvatarNameCache::fetch(const LLUUID& agent_id) +{ + // re-request, even if request is already pending + sAskQueue.insert(agent_id); +} + +void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + // *TODO: update timestamp if zero? + sCache[agent_id] = av_name; +} + +F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers) +{ + F64 expires = 0.0; + if (expirationFromCacheControl(headers, &expires)) + { + return expires; + } + else + { + // With no expiration info, default to a day + const F64 DEFAULT_EXPIRES = 24.0 * 60.0 * 60.0; + F64 now = LLFrameTimer::getTotalSeconds(); + return now + DEFAULT_EXPIRES; + } +} + +bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires) +{ + // Allow the header to override the default + LLSD cache_control_header = headers["cache-control"]; + if (cache_control_header.isDefined()) + { + S32 max_age = 0; + std::string cache_control = cache_control_header.asString(); + if (max_age_from_cache_control(cache_control, &max_age)) + { + F64 now = LLFrameTimer::getTotalSeconds(); + *expires = now + (F64)max_age; + return true; + } + } + return false; +} + +static const std::string MAX_AGE("max-age"); +static const boost::char_separator<char> EQUALS_SEPARATOR("="); +static const boost::char_separator<char> COMMA_SEPARATOR(","); + +bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age) +{ + // Split the string on "," to get a list of directives + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + tokenizer directives(cache_control, COMMA_SEPARATOR); + + tokenizer::iterator token_it = directives.begin(); + for ( ; token_it != directives.end(); ++token_it) + { + // Tokens may have leading or trailing whitespace + std::string token = *token_it; + LLStringUtil::trim(token); + + if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) + { + // ...this token starts with max-age, so let's chop it up by "=" + tokenizer subtokens(token, EQUALS_SEPARATOR); + tokenizer::iterator subtoken_it = subtokens.begin(); + + // Must have a token + if (subtoken_it == subtokens.end()) return false; + std::string subtoken = *subtoken_it; + + // Must exactly equal "max-age" + LLStringUtil::trim(subtoken); + if (subtoken != MAX_AGE) return false; + + // Must have another token + ++subtoken_it; + if (subtoken_it == subtokens.end()) return false; + subtoken = *subtoken_it; + + // Must be a valid integer + // *NOTE: atoi() returns 0 for invalid values, so we have to + // check the string first. + // *TODO: Do servers ever send "0000" for zero? We don't handle it + LLStringUtil::trim(subtoken); + if (subtoken == "0") + { + *max_age = 0; + return true; + } + S32 val = atoi( subtoken.c_str() ); + if (val > 0 && val < S32_MAX) + { + *max_age = val; + return true; + } + return false; + } + } + return false; +} + + diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h new file mode 100644 index 0000000000..0a8b987b05 --- /dev/null +++ b/indra/llmessage/llavatarnamecache.h @@ -0,0 +1,103 @@ +/** + * @file llavatarnamecache.h + * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names + * ("James Cook") from avatar UUIDs. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#ifndef LLAVATARNAMECACHE_H +#define LLAVATARNAMECACHE_H + +#include "llavatarname.h" // for convenience + +#include <boost/signals2.hpp> + +class LLSD; +class LLUUID; + +namespace LLAvatarNameCache +{ + // Until the cache is set running, immediate lookups will fail and + // async lookups will be queued. This allows us to block requests + // until we know if the first region supports display names. + void initClass(bool running); + void cleanupClass(); + + void importFile(std::istream& istr); + void exportFile(std::ostream& ostr); + + // On the viewer, usually a simulator capabilitity + // If empty, name cache will fall back to using legacy name + // lookup system + void setNameLookupURL(const std::string& name_lookup_url); + + // Do we have a valid lookup URL, hence are we trying to use the + // new display name lookup system? + bool hasNameLookupURL(); + + // Periodically makes a batch request for display names not already in + // cache. Call once per frame. + void idle(); + + // If name is in cache, returns true and fills in provided LLAvatarName + // otherwise returns false + bool get(const LLUUID& agent_id, LLAvatarName *av_name); + + // Callback types for get() below + typedef boost::signals2::signal< + void (const LLUUID& agent_id, const LLAvatarName& av_name)> + callback_signal_t; + typedef callback_signal_t::slot_type callback_slot_t; + + // Fetches name information and calls callback. + // If name information is in cache, callback will be called immediately. + void get(const LLUUID& agent_id, callback_slot_t slot); + + // Allow display names to be explicitly disabled for testing. + void setUseDisplayNames(bool use); + bool useDisplayNames(); + + void erase(const LLUUID& agent_id); + + // Force a re-fetch of the most recent data, but keep the current + // data in cache + void fetch(const LLUUID& agent_id); + + void insert(const LLUUID& agent_id, const LLAvatarName& av_name); + + // Compute name expiration time from HTTP Cache-Control header, + // or return default value, in seconds from epoch. + F64 nameExpirationFromHeaders(LLSD headers); +} + +// Parse a cache-control header to get the max-age delta-seconds. +// Returns true if header has max-age param and it parses correctly. +// Exported here to ease unit testing. +bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); + +#endif diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 9871c922f1..ef60fd5c4e 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -75,6 +75,8 @@ public: public: bool mIsGroup; U32 mCreateTime; // unix time_t + // IDEVO TODO collapse names to one field, which will eliminate + // many string compares on "Resident" std::string mFirstName; std::string mLastName; std::string mGroupName; @@ -220,7 +222,9 @@ public: Impl(LLMessageSystem* msg); ~Impl(); - + + BOOL getName(const LLUUID& id, std::string& first, std::string& last); + boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback); void addPending(const LLUUID& id, const LLHost& host); @@ -306,89 +310,10 @@ boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& return impl.mSignal.connect(callback); } -void LLCacheName::importFile(LLFILE* fp) -{ - S32 count = 0; - - const S32 BUFFER_SIZE = 1024; - char buffer[BUFFER_SIZE]; /*Flawfinder: ignore*/ - - // *NOTE: These buffer sizes are hardcoded into sscanf() below - char id_string[MAX_STRING]; /*Flawfinder: ignore*/ - char firstname[MAX_STRING]; /*Flawfinder: ignore*/ - char lastname[MAX_STRING]; /*Flawfinder: ignore*/ - U32 create_time; - - // This is OK if the first line is actually a name. We just don't load it. - char* valid = fgets(buffer, BUFFER_SIZE, fp); - if (!valid) return; - - // *NOTE: This buffer size is hardcoded into sscanf() below - char version_string[BUFFER_SIZE]; /*Flawfinder: ignore*/ - S32 version = 0; - S32 match = sscanf( /* Flawfinder: ignore */ - buffer, - "%1023s %d", - version_string, &version); - if ( match != 2 - || strcmp(version_string, "version") - || version != CN_FILE_VERSION) - { - llwarns << "Ignoring old cache name file format" << llendl; - return; - } - - // We'll expire entries more than a week old - U32 now = (U32)time(NULL); - const U32 SECS_PER_DAY = 60 * 60 * 24; - U32 delete_before_time = now - (7 * SECS_PER_DAY); - - while(!feof(fp)) - { - valid = fgets(buffer, BUFFER_SIZE, fp); - if (!valid) break; - - match = sscanf( /* Flawfinder: ignore */ - buffer, - "%254s %u %254s %254s", - id_string, - &create_time, - firstname, - lastname); - if (4 != match) continue; - - LLUUID id(id_string); - if (id.isNull()) continue; - - // undo trivial XOR - S32 i; - for (i = 0; i < UUID_BYTES; i++) - { - id.mData[i] ^= 0x33; - } - - // Don't load entries that are more than a week old - if (create_time < delete_before_time) continue; - - LLCacheNameEntry* entry = new LLCacheNameEntry(); - entry->mIsGroup = false; - entry->mCreateTime = create_time; - entry->mFirstName = firstname; - entry->mLastName = lastname; - impl.mCache[id] = entry; - std::string fullname = entry->mFirstName + " " + entry->mLastName; - impl.mReverseCache[fullname] = id; - - count++; - } - - llinfos << "LLCacheName loaded " << count << " names" << llendl; -} - bool LLCacheName::importFile(std::istream& istr) { LLSD data; - if(LLSDSerialize::fromXML(data, istr) < 1) + if(LLSDSerialize::fromXMLDocument(data, istr) < 1) return false; // We'll expire entries more than a week old @@ -414,7 +339,7 @@ bool LLCacheName::importFile(std::istream& istr) entry->mFirstName = agent[FIRST].asString(); entry->mLastName = agent[LAST].asString(); impl.mCache[id] = entry; - std::string fullname = entry->mFirstName + " " + entry->mLastName; + std::string fullname = buildFullName(entry->mFirstName, entry->mLastName); impl.mReverseCache[fullname] = id; ++count; @@ -463,6 +388,7 @@ void LLCacheName::exportFile(std::ostream& ostr) // store it LLUUID id = iter->first; std::string id_str = id.asString(); + // IDEVO TODO: Should we store SLIDs with last name "Resident" or not? if(!entry->mFirstName.empty() && !entry->mLastName.empty()) { data[AGENTS][id_str][FIRST] = entry->mFirstName; @@ -480,7 +406,7 @@ void LLCacheName::exportFile(std::ostream& ostr) } -BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& last) +BOOL LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last) { if(id.isNull()) { @@ -489,7 +415,7 @@ BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& las return TRUE; } - LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id ); + LLCacheNameEntry* entry = get_ptr_in_map(mCache, id ); if (entry) { first = entry->mFirstName; @@ -500,16 +426,17 @@ BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& las { first = sCacheName["waiting"]; last.clear(); - if (!impl.isRequestPending(id)) + if (!isRequestPending(id)) { - impl.mAskNameQueue.insert(id); + mAskNameQueue.insert(id); } return FALSE; } } + // static -void LLCacheName::LocalizeCacheName(std::string key, std::string value) +void LLCacheName::localizeCacheName(std::string key, std::string value) { if (key!="" && value!= "" ) sCacheName[key]=value; @@ -520,11 +447,13 @@ void LLCacheName::LocalizeCacheName(std::string key, std::string value) BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname) { std::string first_name, last_name; - BOOL res = getName(id, first_name, last_name); - fullname = first_name + " " + last_name; + BOOL res = impl.getName(id, first_name, last_name); + fullname = buildFullName(first_name, last_name); return res; } + + BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group) { if(id.isNull()) @@ -561,13 +490,13 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group) BOOL LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id) { - std::string fullname = first + " " + last; - return getUUID(fullname, id); + std::string full_name = buildFullName(first, last); + return getUUID(full_name, id); } -BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id) +BOOL LLCacheName::getUUID(const std::string& full_name, LLUUID& id) { - ReverseCache::iterator iter = impl.mReverseCache.find(fullname); + ReverseCache::iterator iter = impl.mReverseCache.find(full_name); if (iter != impl.mReverseCache.end()) { id = iter->second; @@ -579,6 +508,55 @@ BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id) } } +//static +std::string LLCacheName::buildFullName(const std::string& first, const std::string& last) +{ + std::string fullname = first; + if (!last.empty() + && last != "Resident") + { + fullname += ' '; + fullname += last; + } + return fullname; +} + +//static +std::string LLCacheName::cleanFullName(const std::string& full_name) +{ + return full_name.substr(0, full_name.find(" Resident")); +} + +//static +std::string LLCacheName::buildUsername(const std::string& full_name) +{ + // rare, but handle hard-coded error names returned from server + if (full_name == "(\?\?\?) (\?\?\?)") + { + return "(\?\?\?)"; + } + + std::string::size_type index = full_name.find(' '); + + if (index != std::string::npos) + { + std::string username; + username = full_name.substr(0, index); + std::string lastname = full_name.substr(index+1); + + if (lastname != "Resident") + { + username = username + "." + lastname; + } + + LLStringUtil::toLower(username); + return username; + } + + // if the input wasn't a correctly formatted legacy name just return it unchanged + return full_name; +} + // This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer. // The reason it is a slot is so that the legacy get() function below can bind an old callback // and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior @@ -586,7 +564,7 @@ BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id) // we call it immediately. -Steve // NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the // potential need for any parsing should any code need to handle first and last name independently. -boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback) +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback) { boost::signals2::connection res; @@ -594,7 +572,7 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co { LLCacheNameSignal signal; signal.connect(callback); - signal(id, sCacheName["nobody"], "", is_group); + signal(id, sCacheName["nobody"], is_group); return res; } @@ -606,11 +584,13 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co // id found in map therefore we can call the callback immediately. if (entry->mIsGroup) { - signal(id, entry->mGroupName, "", entry->mIsGroup); + signal(id, entry->mGroupName, entry->mIsGroup); } else { - signal(id, entry->mFirstName, entry->mLastName, entry->mIsGroup); + std::string fullname = + buildFullName(entry->mFirstName, entry->mLastName); + signal(id, fullname, entry->mIsGroup); } } else @@ -632,9 +612,15 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co return res; } -boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data) +boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id, + const LLCacheNameCallback& callback) +{ + return get(group_id, true, callback); +} + +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data) { - return get(id, is_group, boost::bind(callback, _1, _2, _3, _4, user_data)); + return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data)); } void LLCacheName::processPending() @@ -706,7 +692,7 @@ void LLCacheName::dump() { llinfos << iter->first << " = " - << entry->mFirstName << " " << entry->mLastName + << buildFullName(entry->mFirstName, entry->mLastName) << " @ " << entry->mCreateTime << llendl; } @@ -725,12 +711,24 @@ void LLCacheName::dumpStats() << llendl; } +void LLCacheName::clear() +{ + for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer()); + impl.mCache.clear(); +} + //static std::string LLCacheName::getDefaultName() { return sCacheName["waiting"]; } +//static +std::string LLCacheName::getDefaultLastName() +{ + return "Resident"; +} + void LLCacheName::Impl::processPendingAsks() { LLMemType mt_ppa(LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS); @@ -752,11 +750,13 @@ void LLCacheName::Impl::processPendingReplies() if (!entry->mIsGroup) { - (reply->mSignal)(reply->mID, entry->mFirstName, entry->mLastName, FALSE); + std::string fullname = + LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); + (reply->mSignal)(reply->mID, fullname, false); } else { - (reply->mSignal)(reply->mID, entry->mGroupName, "", TRUE); + (reply->mSignal)(reply->mID, entry->mGroupName, true); } } @@ -927,13 +927,27 @@ void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup) if (!isGroup) { - mSignal(id, entry->mFirstName, entry->mLastName, FALSE); - std::string fullname = entry->mFirstName + " " + entry->mLastName; - mReverseCache[fullname] = id; + // NOTE: Very occasionally the server sends down a full name + // in the first name field with an empty last name, for example, + // first = "Ladanie1 Resident", last = "". + // I cannot reproduce this, nor can I find a bug in the server code. + // Ensure "Resident" does not appear via cleanFullName, because + // buildFullName only checks last name. JC + std::string full_name; + if (entry->mLastName.empty()) + { + full_name = cleanFullName(entry->mFirstName); + } + else + { + full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); + } + mSignal(id, full_name, false); + mReverseCache[full_name] = id; } else { - mSignal(id, entry->mGroupName, "", TRUE); + mSignal(id, entry->mGroupName, true); mReverseCache[entry->mGroupName] = id; } } @@ -962,4 +976,3 @@ void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** us { ((LLCacheName::Impl*)userData)->processUUIDReply(msg, true); } - diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index 111cc8b650..697bb27556 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -42,13 +42,12 @@ class LLUUID; typedef boost::signals2::signal<void (const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group)> LLCacheNameSignal; + const std::string& name, + bool is_group)> LLCacheNameSignal; typedef LLCacheNameSignal::slot_type LLCacheNameCallback; // Old callback with user data for compatability -typedef void (*old_callback_t)(const LLUUID&, const std::string&, const std::string&, BOOL, void*); +typedef void (*old_callback_t)(const LLUUID&, const std::string&, bool, void*); // Here's the theory: // If you request a name that isn't in the cache, it returns "waiting" @@ -71,24 +70,31 @@ public: boost::signals2::connection addObserver(const LLCacheNameCallback& callback); - // janky old format. Remove after a while. Phoenix. 2008-01-30 - void importFile(LLFILE* fp); - // storing cache on disk; for viewer, in name.cache bool importFile(std::istream& istr); void exportFile(std::ostream& ostr); - // If available, copies the first and last name into the strings provided. - // first must be at least DB_FIRST_NAME_BUF_SIZE characters. - // last must be at least DB_LAST_NAME_BUF_SIZE characters. + // If available, copies name ("bobsmith123" or "James Linden") into string // If not available, copies the string "waiting". // Returns TRUE iff available. - BOOL getName(const LLUUID& id, std::string& first, std::string& last); - BOOL getFullName(const LLUUID& id, std::string& fullname); - + BOOL getFullName(const LLUUID& id, std::string& full_name); + // Reverse lookup of UUID from name BOOL getUUID(const std::string& first, const std::string& last, LLUUID& id); BOOL getUUID(const std::string& fullname, LLUUID& id); + + // IDEVO Temporary code + // Clean up new-style "bobsmith123 Resident" names to "bobsmith123" for display + static std::string buildFullName(const std::string& first, const std::string& last); + + // Clean up legacy "bobsmith123 Resident" to "bobsmith123" + // If name does not contain "Resident" returns it unchanged. + static std::string cleanFullName(const std::string& full_name); + + // Converts a standard legacy name to a username + // "bobsmith123 Resident" -> "bobsmith" + // "Random Linden" -> "random.linden" + static std::string buildUsername(const std::string& name); // If available, this method copies the group name into the string // provided. The caller must allocate at least @@ -100,10 +106,15 @@ public: // If the data is currently available, may call the callback immediatly // otherwise, will request the data, and will call the callback when // available. There is no garuntee the callback will ever be called. - boost::signals2::connection get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback); - + boost::signals2::connection get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback); + + // Convenience method for looking up a group name, so you can + // tell the difference between avatar lookup and group lookup + // in global searches + boost::signals2::connection getGroup(const LLUUID& group_id, const LLCacheNameCallback& callback); + // LEGACY - boost::signals2::connection get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data); + boost::signals2::connection get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data); // This method needs to be called from time to time to send out // requests. void processPending(); @@ -114,9 +125,15 @@ public: // Debugging void dump(); // Dumps the contents of the cache void dumpStats(); // Dumps the sizes of the cache and associated queues. + void clear(); // Deletes all entries from the cache static std::string getDefaultName(); - static void LocalizeCacheName(std::string key, std::string value); + + // Returns "Resident", the default last name for SLID-based accounts + // that have no last name. + static std::string getDefaultLastName(); + + static void localizeCacheName(std::string key, std::string value); static std::map<std::string, std::string> sCacheName; private: diff --git a/indra/llmessage/mean_collision_data.h b/indra/llmessage/mean_collision_data.h index 03b96f9f90..a6c635e81e 100644 --- a/indra/llmessage/mean_collision_data.h +++ b/indra/llmessage/mean_collision_data.h @@ -61,7 +61,7 @@ public: LLMeanCollisionData(LLMeanCollisionData *mcd) : mVictim(mcd->mVictim), mPerp(mcd->mPerp), mTime(mcd->mTime), mType(mcd->mType), mMag(mcd->mMag), - mFirstName(mcd->mFirstName), mLastName(mcd->mLastName) + mFullName(mcd->mFullName) { } @@ -95,8 +95,7 @@ public: time_t mTime; EMeanCollisionType mType; F32 mMag; - std::string mFirstName; - std::string mLastName; + std::string mFullName; }; diff --git a/indra/llmessage/tests/llavatarnamecache_test.cpp b/indra/llmessage/tests/llavatarnamecache_test.cpp new file mode 100644 index 0000000000..eeadb703d1 --- /dev/null +++ b/indra/llmessage/tests/llavatarnamecache_test.cpp @@ -0,0 +1,109 @@ +/** + * @file llhost_test.cpp + * @author Adroit + * @date 2007-02 + * @brief llhost test cases. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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 "linden_common.h" + +#include "../llavatarnamecache.h" + +#include "../test/lltut.h" + +namespace tut +{ + struct avatarnamecache_data + { + }; + typedef test_group<avatarnamecache_data> avatarnamecache_test; + typedef avatarnamecache_test::object avatarnamecache_object; + tut::avatarnamecache_test avatarnamecache_testcase("llavatarnamecache"); + + template<> template<> + void avatarnamecache_object::test<1>() + { + bool valid = false; + S32 max_age = 0; + + valid = max_age_from_cache_control("max-age=3600", &max_age); + ensure("typical input valid", valid); + ensure_equals("typical input parsed", max_age, 3600); + + valid = max_age_from_cache_control( + " max-age=600 , no-cache,private=\"stuff\" ", &max_age); + ensure("complex input valid", valid); + ensure_equals("complex input parsed", max_age, 600); + + valid = max_age_from_cache_control( + "no-cache, max-age = 123 ", &max_age); + ensure("complex input 2 valid", valid); + ensure_equals("complex input 2 parsed", max_age, 123); + } + + template<> template<> + void avatarnamecache_object::test<2>() + { + bool valid = false; + S32 max_age = -1; + + valid = max_age_from_cache_control("", &max_age); + ensure("empty input returns invalid", !valid); + ensure_equals("empty input doesn't change val", max_age, -1); + + valid = max_age_from_cache_control("no-cache", &max_age); + ensure("no max-age field returns invalid", !valid); + + valid = max_age_from_cache_control("max", &max_age); + ensure("just 'max' returns invalid", !valid); + + valid = max_age_from_cache_control("max-age", &max_age); + ensure("partial max-age is invalid", !valid); + + valid = max_age_from_cache_control("max-age=", &max_age); + ensure("longer partial max-age is invalid", !valid); + + valid = max_age_from_cache_control("max-age=FOO", &max_age); + ensure("invalid integer max-age is invalid", !valid); + + valid = max_age_from_cache_control("max-age 234", &max_age); + ensure("space separated max-age is invalid", !valid); + + valid = max_age_from_cache_control("max-age=0", &max_age); + ensure("zero max-age is valid", valid); + + // *TODO: Handle "0000" as zero + //valid = max_age_from_cache_control("max-age=0000", &max_age); + //ensure("multi-zero max-age is valid", valid); + + valid = max_age_from_cache_control("max-age=-123", &max_age); + ensure("less than zero max-age is invalid", !valid); + } +} diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index cc107c972d..f49069f065 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -58,8 +58,6 @@ #include "lltooltip.h" // Globals -S32 LLCOMBOBOX_HEIGHT = 0; -S32 LLCOMBOBOX_WIDTH = 0; S32 MAX_COMBO_WIDTH = 500; static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box"); @@ -711,10 +709,10 @@ void LLComboBox::onItemSelected(const LLSD& data) setLabel(getSelectedItemLabel()); if (mAllowTextEntry) - { - gFocusMgr.setKeyboardFocus(mTextEntry); - mTextEntry->selectAll(); - } + { + gFocusMgr.setKeyboardFocus(mTextEntry); + mTextEntry->selectAll(); + } } // hiding the list reasserts the old value stored in the text editor/dropdown button hideList(); diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index f0bd432f3a..7f93cd1477 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -49,9 +49,6 @@ class LLFontGL; class LLViewBorder; -extern S32 LLCOMBOBOX_HEIGHT; -extern S32 LLCOMBOBOX_WIDTH; - class LLComboBox : public LLUICtrl, public LLCtrlListInterface { @@ -230,8 +227,8 @@ private: commit_callback_t mPrearrangeCallback; commit_callback_t mTextEntryCallback; commit_callback_t mSelectionCallback; - boost::signals2::connection mTopLostSignalConnection; - S32 mLastSelectedIndex; + boost::signals2::connection mTopLostSignalConnection; + S32 mLastSelectedIndex; }; // A combo box with icons for the list of items. diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 7b8f51ae3c..27e59a0475 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -34,6 +34,8 @@ #include "llnotifications.h" +#include "llavatarnamecache.h" +#include "llcachename.h" #include "llxmlnode.h" #include "lluictrl.h" #include "lluictrlfactory.h" @@ -1488,10 +1490,42 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification) return s; } -void LLPostponedNotification::onCachedNameReceived(const LLUUID& id, const std::string& first, - const std::string& last, bool is_group) +//static +void LLPostponedNotification::lookupName(LLPostponedNotification* thiz, + const LLUUID& id, + bool is_group) +{ + if (is_group) + { + gCacheName->getGroup(id, + boost::bind(&LLPostponedNotification::onGroupNameCache, + thiz, _1, _2, _3)); + } + else + { + LLAvatarNameCache::get(id, + boost::bind(&LLPostponedNotification::onAvatarNameCache, + thiz, _1, _2)); + } +} + +void LLPostponedNotification::onGroupNameCache(const LLUUID& id, + const std::string& full_name, + bool is_group) +{ + finalizeName(full_name); +} + +void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + std::string name = av_name.getCompleteName(); + finalizeName(name); +} + +void LLPostponedNotification::finalizeName(const std::string& name) { - gCacheName->getFullName(id, mName); + mName = name; modifyNotificationParams(); LLNotifications::instance().add(mParams); cleanup(); diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index c942a32512..73357daaef 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -104,8 +104,8 @@ #include "llinitparam.h" #include "llnotificationslistener.h" #include "llnotificationptr.h" -#include "llcachename.h" +class LLAvatarName; typedef enum e_notification_priority { @@ -1000,17 +1000,20 @@ public: { // upcast T to the base type to restrict T derivation from LLPostponedNotification LLPostponedNotification* thiz = new T(); - thiz->mParams = params; - gCacheName->get(id, is_group, boost::bind( - &LLPostponedNotification::onCachedNameReceived, thiz, _1, _2, - _3, _4)); + // Avoid header file dependency on llcachename.h + lookupName(thiz, id, is_group); } private: - void onCachedNameReceived(const LLUUID& id, const std::string& first, - const std::string& last, bool is_group); + static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group); + // only used for groups + void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group); + // only used for avatars + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + // used for both group and avatar names + void finalizeName(const std::string& name); void cleanup() { diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index b8f93b6a0e..5965ca6fb5 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -44,7 +44,8 @@ LLStyle::Params::Params() color("color", LLColor4::black), font("font", LLFontGL::getFontMonospace()), image("image"), - link_href("href") + link_href("href"), + is_link("is_link") {} @@ -57,6 +58,7 @@ LLStyle::LLStyle(const LLStyle::Params& p) mReadOnlyColor(p.readonly_color()), mFont(p.font()), mLink(p.link_href), + mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()), mDropShadow(p.drop_shadow), mImagep(p.image()) {} @@ -79,7 +81,7 @@ void LLStyle::setLinkHREF(const std::string& href) BOOL LLStyle::isLink() const { - return mLink.size(); + return mIsLink; } BOOL LLStyle::isVisible() const diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 2067e8e8be..0ca1f2027e 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -51,6 +51,7 @@ public: Optional<const LLFontGL*> font; Optional<LLUIImage*> image; Optional<std::string> link_href; + Optional<bool> is_link; Params(); }; LLStyle(const Params& p = Params()); @@ -113,6 +114,7 @@ private: std::string mFontName; const LLFontGL* mFont; // cached for performance std::string mLink; + bool mIsLink; LLUIImagePtr mImagep; }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 55dbf50fd7..38b35020b5 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -963,9 +963,9 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) if (width != getRect().getWidth() || height != getRect().getHeight()) { bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; - + LLUICtrl::reshape( width, height, called_from_parent ); - + if (mScroller && scrolled_to_bottom && mTrackEnd) { // keep bottom of text buffer visible @@ -974,7 +974,7 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) // not be scrolled to the bottom, since the scroll index // specified the _top_ of the visible document region mScroller->goToBottom(); - } + } // do this first after reshape, because other things depend on // up-to-date mVisibleTextRect @@ -1288,28 +1288,28 @@ void LLTextBase::reflow() } } - // apply scroll constraints after reflowing text - if (!hasMouseCapture() && mScroller) - { - if (scrolled_to_bottom && mTrackEnd) + // apply scroll constraints after reflowing text + if (!hasMouseCapture() && mScroller) { - // keep bottom of text buffer visible - endOfDoc(); - } - else if (hasSelection() && follow_selection) - { - // keep cursor in same vertical position on screen when selecting text - LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); + if (scrolled_to_bottom && mTrackEnd) + { + // keep bottom of text buffer visible + endOfDoc(); + } + else if (hasSelection() && follow_selection) + { + // keep cursor in same vertical position on screen when selecting text + LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); LLRect old_cursor_rect = cursor_rect; old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop; old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom; - mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); - } - else - { - // keep first line of text visible - LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); + mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + } + else + { + // keep first line of text visible + LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex); // pass in desired rect in the coordinate frame of the document viewport LLRect old_first_char_rect = first_char_rect; @@ -1317,12 +1317,12 @@ void LLTextBase::reflow() old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom; mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect); + } } - } - // reset desired x cursor position - updateCursorXPos(); -} + // reset desired x cursor position + updateCursorXPos(); + } LLRect LLTextBase::getTextBoundingRect() { @@ -1583,7 +1583,21 @@ std::string LLTextBase::getText() const return getViewModel()->getValue().asString(); } -void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) +// IDEVO - icons can be UI image names or UUID sent from +// server with avatar display name +static LLUIImagePtr image_from_icon_name(const std::string& icon_name) +{ + if (LLUUID::validate(icon_name)) + { + return LLUI::getUIImageByID( LLUUID(icon_name) ); + } + else + { + return LLUI::getUIImage(icon_name); + } +} + +void LLTextBase::appendTextImpl(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); style_params.fillFrom(getDefaultStyleParams()); @@ -1595,16 +1609,13 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para LLUrlMatch match; std::string text = new_text; while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBase::replaceUrlLabel, this, _1, _2)) ) + boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) ) { start = match.getStart(); end = match.getEnd()+1; - LLStyle::Params link_params = style_params; - link_params.color = match.getColor(); - link_params.readonly_color = match.getColor(); - link_params.font.style("UNDERLINE"); - link_params.link_href = match.getUrl(); + LLStyle::Params link_params(style_params); + link_params.overwriteFrom(match.getStyle()); // output the text before the Url if (start > 0) @@ -1625,11 +1636,14 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para // output an optional icon before the Url if (! match.getIcon().empty()) { - LLUIImagePtr image = LLUI::getUIImage(match.getIcon()); + LLUIImagePtr image = image_from_icon_name( match.getIcon() ); if (image) { - LLStyle::Params icon; - icon.image = image; + LLStyle::Params icon_params; + icon_params.image = image; + // must refer to our link so we can update the icon later + // after name/group data is looked up + icon_params.link_href = match.getUrl(); // Text will be replaced during rendering with the icon, // but string cannot be empty or the segment won't be // added (or drawn). @@ -1637,26 +1651,20 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para } } - // output the styled Url (unless we've been asked to suppress hyperlinking) - if (match.isLinkDisabled()) + // output the styled Url + appendAndHighlightTextImpl(match.getLabel(), part, link_params); + + // set the tooltip for the Url label + if (! match.getTooltip().empty()) { - appendAndHighlightTextImpl(match.getLabel(), part, style_params); + segment_set_t::iterator it = getSegIterContaining(getLength()-1); + if (it != mSegments.end()) + { + LLTextSegmentPtr segment = *it; + segment->setToolTip(match.getTooltip()); + } } - else - { - appendAndHighlightTextImpl(match.getLabel(), part, link_params); - // set the tooltip for the Url label - if (! match.getTooltip().empty()) - { - segment_set_t::iterator it = getSegIterContaining(getLength()-1); - if (it != mSegments.end()) - { - LLTextSegmentPtr segment = *it; - segment->setToolTip(match.getTooltip()); - } - } - } // move on to the rest of the text after the Url if (end < (S32)text.length()) { @@ -1759,7 +1767,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig highlight_params.color = lcolor; LLWString wide_text; - wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); S32 cur_length = getLength(); LLStyleConstSP sp(new LLStyle(highlight_params)); @@ -1772,7 +1780,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig else { LLWString wide_text; - wide_text = utf8str_to_wstring(new_text); + wide_text = utf8str_to_wstring(new_text); segment_vec_t segments; S32 segment_start = old_length; @@ -1829,8 +1837,9 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen } -void LLTextBase::replaceUrlLabel(const std::string &url, - const std::string &label) +void LLTextBase::replaceUrl(const std::string &url, + const std::string &label, + const std::string &icon) { // get the full (wide) text for the editor so we can change it LLWString text = getWText(); @@ -1851,7 +1860,7 @@ void LLTextBase::replaceUrlLabel(const std::string &url, seg->setEnd(seg_start + seg_length); // if we find a link with our Url, then replace the label - if (style->isLink() && style->getLinkHREF() == url) + if (style->getLinkHREF() == url) { S32 start = seg->getStart(); S32 end = seg->getEnd(); @@ -1860,6 +1869,21 @@ void LLTextBase::replaceUrlLabel(const std::string &url, modified = true; } + // Icon might be updated when more avatar or group info + // becomes available + if (style->isImage() && style->getLinkHREF() == url) + { + LLUIImagePtr image = image_from_icon_name( icon ); + if (image) + { + LLStyle::Params icon_params; + icon_params.image = image; + LLStyleConstSP new_style(new LLStyle(icon_params)); + seg->setStyle(new_style); + modified = true; + } + } + // work out the character offset for the next segment seg_start = seg->getEnd(); } @@ -1957,8 +1981,8 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1) { // segment wraps to next line, so just set doc pos to the end of the line - // segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) - pos = llmin(getLength(), line_iter->mDocIndexEnd); + // segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) + pos = llmin(getLength(), line_iter->mDocIndexEnd); break; } start_x += text_width; @@ -2599,8 +2623,8 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt height = mFontHeight; const LLWString &text = mEditor.getWText(); // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); - } + width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); + } return false; } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 176308c61a..7041b9a109 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -314,7 +314,11 @@ protected: // misc void updateRects(); void needsScroll() { mScrollNeeded = TRUE; } - void replaceUrlLabel(const std::string &url, const std::string &label); + + struct URLLabelCallback; + // Replace a URL with a new icon and label, for example, when + // avatar names are looked up. + void replaceUrl(const std::string &url, const std::string &label, const std::string& icon); void appendLineBreakSegment(const LLStyle::Params& style_params); void appendImageSegment(S32 highlight_part, const LLStyle::Params& style_params); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index fd56a87345..472c15a240 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -37,18 +37,19 @@ #include "llurlmatch.h" #include "llurlregistry.h" +#include "llavatarnamecache.h" #include "llcachename.h" #include "lltrans.h" #include "lluicolortable.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() : - mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")), - mDisabledLink(false) -{ -} + +LLUrlEntryBase::LLUrlEntryBase() +{} LLUrlEntryBase::~LLUrlEntryBase() { @@ -59,6 +60,22 @@ 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 @@ -136,16 +153,20 @@ void LLUrlEntryBase::addObserver(const std::string &id, mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer)); } } - -void LLUrlEntryBase::callObservers(const std::string &id, const std::string &label) + +// *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 - std::multimap<std::string, LLUrlEntryObserver>::iterator it; - for (it = mObservers.find(id); it != mObservers.end();) + typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it; + std::pair<observer_it, observer_it> 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); + (*observer.signal)(it->second.url, label, icon); // then remove the signal - we only need to call it once delete observer.signal; mObservers.erase(it++); @@ -314,16 +335,35 @@ LLUrlEntryAgent::LLUrlEntryAgent() boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_agent.xml"; mIcon = "Generic_Person"; - mColor = LLUIColorTable::instance().getColor("AgentLinkColor"); } -void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) +// 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<std::string, LLUrlEntryObserver>::iterator observer_it; + std::pair<observer_it, observer_it> 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(), first + " " + last); + callObservers(id.asString(), label, mIcon); } std::string LLUrlEntryAgent::getTooltip(const std::string &string) const @@ -331,6 +371,10 @@ 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"); @@ -374,50 +418,182 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } LLUUID agent_id(agent_id_string); - std::string full_name; if (agent_id.isNull()) { return LLTrans::getString("AvatarNameNobody"); } - else if (gCacheName->getFullName(agent_id, 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; + + 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 { - gCacheName->get(agent_id, FALSE, - boost::bind(&LLUrlEntryAgent::onAgentNameReceived, - this, _1, _2, _3, _4)); + 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("AgentLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("AgentLinkColor"); + 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 @@ -431,16 +607,14 @@ LLUrlEntryGroup::LLUrlEntryGroup() mMenuName = "menu_url_group.xml"; mIcon = "Generic_Group"; mTooltip = LLTrans::getString("TooltipGroupUrl"); - mColor = LLUIColorTable::instance().getColor("GroupLinkColor"); } void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) + const std::string& name, + bool is_group) { // received the group name from the server - tell our observers - callObservers(id.asString(), first); + callObservers(id.asString(), name, mIcon); } std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb) @@ -470,14 +644,23 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa } else { - gCacheName->get(group_id, TRUE, + gCacheName->getGroup(group_id, boost::bind(&LLUrlEntryGroup::onGroupNameReceived, - this, _1, _2, _3, _4)); + 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("GroupLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("GroupLinkColor"); + return style_params; +} + + // // LLUrlEntryInventory Describes a Second Life inventory Url, e.g., // secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select @@ -765,7 +948,6 @@ LLUrlEntryNoLink::LLUrlEntryNoLink() { mPattern = boost::regex("<nolink>[^<]*</nolink>", boost::regex::perl|boost::regex::icase); - mDisabledLink = true; } std::string LLUrlEntryNoLink::getUrl(const std::string &url) const @@ -779,6 +961,12 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC return getUrl(url); } +LLStyle::Params LLUrlEntryNoLink::getStyle() const +{ + return LLStyle::Params(); +} + + // // LLUrlEntryIcon describes an icon with <icon>...</icon> tags // @@ -786,7 +974,6 @@ LLUrlEntryIcon::LLUrlEntryIcon() { mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>", boost::regex::perl|boost::regex::icase); - mDisabledLink = true; } std::string LLUrlEntryIcon::getUrl(const std::string &url) const diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 3c21fe8d61..04423a81a3 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -36,13 +36,17 @@ #include "lluuid.h" #include "lluicolor.h" +#include "llstyle.h" #include <boost/signals2.hpp> #include <boost/regex.hpp> #include <string> #include <map> +class LLAvatarName; + typedef boost::signals2::signal<void (const std::string& url, - const std::string& label)> LLUrlLabelSignal; + const std::string& label, + const std::string& icon)> LLUrlLabelSignal; typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; /// @@ -77,10 +81,10 @@ public: 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 mIcon; } + virtual std::string getIcon(const std::string &url); - /// Return the color to render the displayed text - LLUIColor getColor() const { return mColor; } + /// 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; } @@ -91,9 +95,6 @@ public: /// Return the name of a SL location described by this Url, if any virtual std::string getLocation(const std::string &url) const { return ""; } - /// is this a match for a URL that should not be hyperlinked? - bool isLinkDisabled() const { return mDisabledLink; } - virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } protected: @@ -103,7 +104,7 @@ protected: 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); - void callObservers(const std::string &id, const std::string &label); + virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); typedef struct { std::string url; @@ -114,9 +115,7 @@ protected: std::string mIcon; std::string mMenuName; std::string mTooltip; - LLUIColor mColor; std::multimap<std::string, LLUrlEntryObserver> mObservers; - bool mDisabledLink; }; /// @@ -165,16 +164,76 @@ public: /// /// 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; +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: + 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: - void onAgentNameReceived(const LLUUID& id, const std::string& first, - const std::string& last, BOOL is_group); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); }; /// @@ -186,9 +245,9 @@ class LLUrlEntryGroup : public LLUrlEntryBase public: LLUrlEntryGroup(); /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ LLStyle::Params getStyle() const; private: - void onGroupNameReceived(const LLUUID& id, const std::string& first, - const std::string& last, BOOL is_group); + void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); }; /// @@ -296,6 +355,7 @@ 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; }; /// diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index 7c96665ce4..73d880522b 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -42,8 +42,7 @@ LLUrlMatch::LLUrlMatch() : mTooltip(""), mIcon(""), mMenuName(""), - mLocation(""), - mDisabledLink(false) + mLocation("") { } @@ -51,7 +50,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string &tooltip, const std::string &icon, const LLUIColor& color, const std::string &menu, const std::string &location, - bool disabled_link, const LLUUID& id) + bool disabled_link) { mStart = start; mEnd = end; @@ -59,9 +58,9 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, mLabel = label; mTooltip = tooltip; mIcon = icon; - mColor = color; + mStyle = style; + mStyle.link_href = url; mMenuName = menu; mLocation = location; - mDisabledLink = disabled_link; mID = id; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index 78dd2c528f..458a27d01f 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -34,11 +34,11 @@ #ifndef LL_LLURLMATCH_H #define LL_LLURLMATCH_H -#include "linden_common.h" +//#include "linden_common.h" #include <string> #include <vector> -#include "lluicolor.h" +#include "llstyle.h" /// /// LLUrlMatch describes a single Url that was matched within a string by @@ -75,7 +75,7 @@ public: std::string getIcon() const { return mIcon; } /// Return the color to render the displayed text - LLUIColor getColor() const { return mColor; } + LLStyle::Params getStyle() const { return mStyle; } /// Return the name of a XUI file containing the context menu items std::string getMenuName() const { return mMenuName; } @@ -83,18 +83,13 @@ public: /// return the SL location that this Url describes, or "" if none. std::string getLocation() const { return mLocation; } - /// is this a match for a URL that should not be hyperlinked? - bool isLinkDisabled() const { return mDisabledLink; } - /// 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 LLUIColor& color, const std::string &menu, - const std::string &location, bool disabled_link - , const LLUUID& id ); - - const LLUUID& getID() const { return mID;} + const std::string &location, const LLUUID& id); + const LLUUID& getID() const { return mID; } private: U32 mStart; U32 mEnd; @@ -104,10 +99,8 @@ private: std::string mIcon; std::string mMenuName; std::string mLocation; - LLUUID mID; - LLUIColor mColor; - bool mDisabledLink; + LLStyle::Params mStyle; }; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 1f86f72faa..5de249d994 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -37,18 +37,25 @@ #include <boost/regex.hpp> // default dummy callback that ignores any label updates from the server -void LLUrlRegistryNullCallback(const std::string &url, const std::string &label) +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()); @@ -77,10 +84,13 @@ LLUrlRegistry::~LLUrlRegistry() } } -void LLUrlRegistry::registerUrl(LLUrlEntryBase *url) +void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front) { if (url) { + if (force_front) // IDEVO + mUrlEntry.insert(mUrlEntry.begin(), url); + else mUrlEntry.push_back(url); } } @@ -180,10 +190,9 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL match_entry->getLabel(url, cb), match_entry->getTooltip(url), match_entry->getIcon(url), - match_entry->getColor(), + match_entry->getStyle(), match_entry->getMenuName(), match_entry->getLocation(url), - match_entry->isLinkDisabled(), match_entry->getID(url)); return true; } @@ -215,10 +224,9 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr match.getLabel(), match.getTooltip(), match.getIcon(), - match.getColor(), + match.getStyle(), match.getMenuName(), match.getLocation(), - match.isLinkDisabled(), match.getID()); return true; } diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index 399ee0a988..dffbd9d4bf 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -43,7 +43,9 @@ #include <vector> /// This default callback for findUrl() simply ignores any label updates -void LLUrlRegistryNullCallback(const std::string &url, const std::string &label); +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 @@ -70,7 +72,9 @@ public: ~LLUrlRegistry(); /// add a new Url handler to the registry (will be freed on destruction) - void registerUrl(LLUrlEntryBase *url); + /// 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 diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index 26d1f2e067..bc0f5dc9c2 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -22,11 +22,28 @@ #include "llstring.h" #include "llfile.h" +#include "llavatarnamecache.h" #include "llcachename.h" #include "lluuid.h" #include <string> +// Stub for LLAvatarNameCache +bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) +{ + return false; +} + +void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) +{ + return; +} + +bool LLAvatarNameCache::useDisplayNames() +{ + return false; +} + // // Stub implementation for LLCacheName // @@ -42,7 +59,12 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group) return TRUE; } -boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback) +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback) +{ + return boost::signals2::connection(); +} + +boost::signals2::connection LLCacheName::getGroup(const LLUUID& id, const LLCacheNameCallback& callback) { return boost::signals2::connection(); } @@ -62,3 +84,106 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil:: { return std::string(); } + +// +// Stub implementation for LLStyle::Params::Params +// + +LLStyle::Params::Params() +{ +} + +// +// Stub implementations for various LLInitParam classes +// + +namespace LLInitParam +{ + BaseBlock::BaseBlock() {} + BaseBlock::~BaseBlock() {} + Param::Param(BaseBlock* enclosing_block) + : mIsProvided(false) + { + const U8* my_addr = reinterpret_cast<const U8*>(this); + const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); + mEnclosingBlockOffset = (U16)(my_addr - block_addr); + } + void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {} + + void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){} + param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;} + + void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) + { + mBlockDescriptor = &descriptor; + descriptor.mCurrentBlockPtr = this; + } + bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; } + bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; } + bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; } + bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; } + bool BaseBlock::validateBlock(bool emit_errors) const { return true; } + + TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) + : super_t(descriptor, name, value, func, min_count, max_count) + {} + + void TypedParam<LLUIColor>::setValueFromBlock() const + {} + + void TypedParam<LLUIColor>::setBlockFromValue() + {} + + void TypeValues<LLUIColor>::declareValues() + {} + + bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) + { + return false; + } + + TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) + : super_t(descriptor, _name, value, func, min_count, max_count) + {} + + void TypedParam<const LLFontGL*>::setValueFromBlock() const + {} + + void TypedParam<const LLFontGL*>::setBlockFromValue() + {} + + void TypeValues<LLFontGL::HAlign>::declareValues() + {} + + void TypeValues<LLFontGL::VAlign>::declareValues() + {} + + void TypeValues<LLFontGL::ShadowType>::declareValues() + {} + + void TypedParam<LLUIImage*>::setValueFromBlock() const + {} + + void TypedParam<LLUIImage*>::setBlockFromValue() + {} + + + bool ParamCompare<LLUIImage*, false>::equals( + LLUIImage* const &a, + LLUIImage* const &b) + { + return false; + } + + bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b) + { + return false; + } + +} + +//static +LLFontGL* LLFontGL::getFontDefault() +{ + return NULL; +} diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 4463b6cc6f..009d82ed99 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -25,6 +25,7 @@ #include "llurlentry_stub.cpp" #include "lltut.h" #include "../lluicolortable.h" +#include "../lluiimage.h" #include <boost/regex.hpp> @@ -35,6 +36,26 @@ LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& defa LLUIColor::LLUIColor() : mColorPtr(NULL) {} +LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image) +{ +} + +LLUIImage::~LLUIImage() +{ +} + +//virtual +S32 LLUIImage::getWidth() const +{ + return 0; +} + +//virtual +S32 LLUIImage::getHeight() const +{ + return 0; +} + namespace tut { struct LLUrlEntryData diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 06b850d233..e8e1ea118b 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -20,14 +20,136 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "../llurlmatch.h" +#include "../lluiimage.h" #include "lltut.h" -// link seam +// link seams + LLUIColor::LLUIColor() : mColorPtr(NULL) {} +LLStyle::Params::Params() +{ +} + +LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image) +{ +} + +LLUIImage::~LLUIImage() +{ +} + +//virtual +S32 LLUIImage::getWidth() const +{ + return 0; +} + +//virtual +S32 LLUIImage::getHeight() const +{ + return 0; +} + +namespace LLInitParam +{ + BaseBlock::BaseBlock() {} + BaseBlock::~BaseBlock() {} + + void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {} + + void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){} + param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;} + + void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) + { + mBlockDescriptor = &descriptor; + descriptor.mCurrentBlockPtr = this; + } + + Param::Param(BaseBlock* enclosing_block) + : mIsProvided(false) + { + const U8* my_addr = reinterpret_cast<const U8*>(this); + const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); + mEnclosingBlockOffset = (U16)(my_addr - block_addr); + } + + bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; } + bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; } + bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; } + bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; } + bool BaseBlock::validateBlock(bool emit_errors) const { return true; } + + TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) + : super_t(descriptor, name, value, func, min_count, max_count) + {} + + void TypedParam<LLUIColor>::setValueFromBlock() const + {} + + void TypedParam<LLUIColor>::setBlockFromValue() + {} + + void TypeValues<LLUIColor>::declareValues() + {} + + bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) + { + return false; + } + + TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) + : super_t(descriptor, _name, value, func, min_count, max_count) + {} + + void TypedParam<const LLFontGL*>::setValueFromBlock() const + {} + + void TypedParam<const LLFontGL*>::setBlockFromValue() + {} + + void TypeValues<LLFontGL::HAlign>::declareValues() + {} + + void TypeValues<LLFontGL::VAlign>::declareValues() + {} + + void TypeValues<LLFontGL::ShadowType>::declareValues() + {} + + void TypedParam<LLUIImage*>::setValueFromBlock() const + {} + + void TypedParam<LLUIImage*>::setBlockFromValue() + {} + + bool ParamCompare<LLUIImage*, false>::equals( + LLUIImage* const &a, + LLUIImage* const &b) + { + return false; + } + + bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b) + { + return false; + } + +} + +//static +LLFontGL* LLFontGL::getFontDefault() +{ + return NULL; +} + + namespace tut { struct LLUrlMatchData @@ -54,7 +176,7 @@ namespace tut LLUrlMatch match; ensure("empty()", match.empty()); - match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", LLUUID::null); ensure("! empty()", ! match.empty()); } @@ -67,7 +189,7 @@ namespace tut LLUrlMatch match; ensure_equals("getStart() == 0", match.getStart(), 0); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getStart() == 10", match.getStart(), 10); } @@ -80,7 +202,7 @@ namespace tut LLUrlMatch match; ensure_equals("getEnd() == 0", match.getEnd(), 0); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getEnd() == 20", match.getEnd(), 20); } @@ -93,10 +215,10 @@ namespace tut LLUrlMatch match; ensure_equals("getUrl() == ''", match.getUrl(), ""); - match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getUrl() == '' (2)", match.getUrl(), ""); } @@ -109,10 +231,10 @@ namespace tut LLUrlMatch match; ensure_equals("getLabel() == ''", match.getLabel(), ""); - match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getLabel() == '' (2)", match.getLabel(), ""); } @@ -125,10 +247,10 @@ namespace tut LLUrlMatch match; ensure_equals("getTooltip() == ''", match.getTooltip(), ""); - match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getTooltip() == '' (2)", match.getTooltip(), ""); } @@ -141,10 +263,10 @@ namespace tut LLUrlMatch match; ensure_equals("getIcon() == ''", match.getIcon(), ""); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", LLUUID::null); ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure_equals("getIcon() == '' (2)", match.getIcon(), ""); } @@ -157,10 +279,10 @@ namespace tut LLUrlMatch match; ensure("getMenuName() empty", match.getMenuName().empty()); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", LLUUID::null); ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure("getMenuName() empty (2)", match.getMenuName().empty()); } @@ -173,10 +295,10 @@ namespace tut LLUrlMatch match; ensure("getLocation() empty", match.getLocation().empty()); - match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", LLUUID::null); ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris"); - match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", LLUUID::null); ensure("getLocation() empty (2)", match.getLocation().empty()); } } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b00104c427..cb5623a743 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -239,6 +239,7 @@ set(viewer_SOURCE_FILES llhudeffecttrail.cpp llhudicon.cpp llhudmanager.cpp + llhudnametag.cpp llhudobject.cpp llhudrender.cpp llhudtext.cpp @@ -473,6 +474,7 @@ set(viewer_SOURCE_FILES llviewercontrol.cpp llviewercontrollistener.cpp llviewerdisplay.cpp + llviewerdisplayname.cpp llviewerfloaterreg.cpp llviewerfoldertype.cpp llviewergenericmessage.cpp @@ -759,6 +761,7 @@ set(viewer_HEADER_FILES llhudeffecttrail.h llhudicon.h llhudmanager.h + llhudnametag.h llhudobject.h llhudrender.h llhudtext.h @@ -992,6 +995,7 @@ set(viewer_HEADER_FILES llviewercontrol.h llviewercontrollistener.h llviewerdisplay.h + llviewerdisplayname.h llviewerfloaterreg.h llviewerfoldertype.h llviewergenericmessage.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 6204c8047a..4178b91fcf 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7403,7 +7403,7 @@ <key>Value</key> <integer>0</integer> </map> - <key>RenderShowGroupTitleAll</key> + <key>NameTagShowGroupTitles</key> <map> <key>Comment</key> <string>Show group titles in name labels</string> @@ -7412,6 +7412,39 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> + <integer>0</integer> + </map> + <key>NameTagShowDisplayNames</key> + <map> + <key>Comment</key> + <string>Show display names in name labels</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>NameTagShowFriends</key> + <map> + <key>Comment</key> + <string>Highlight the name tags of your friends</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>NameTagShowUsernames</key> + <map> + <key>Comment</key> + <string>Show usernames in avatar name tags</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> <integer>1</integer> </map> <key>RenderInitError</key> @@ -10417,6 +10450,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>UseDisplayNames</key> + <map> + <key>Comment</key> + <string>Use new, changeable, unicode names</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>UseEnergy</key> <map> <key>Comment</key> diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index 15d9f36b74..b5f9810e24 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -46,29 +46,6 @@ #include "llslurl.h" //static -void LLAgentUI::buildName(std::string& name) -{ - name.clear(); - if (isAgentAvatarValid()) - { - LLNameValue *first_nv = gAgentAvatarp->getNVPair("FirstName"); - LLNameValue *last_nv = gAgentAvatarp->getNVPair("LastName"); - if (first_nv && last_nv) - { - name = first_nv->printData() + " " + last_nv->printData(); - } - else - { - llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl; - } - } - else - { - name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName"); - } -} - -//static void LLAgentUI::buildFullname(std::string& name) { if (isAgentAvatarValid()) diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index 577b752fbe..2c3c65fb8a 100644 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -47,7 +47,6 @@ public: LOCATION_FORMAT_FULL, // Parcel, Region (x, y, z) - Maturity }; - static void buildName(std::string& name); static void buildFullname(std::string &name); static void buildSLURL(LLSLURL& slurl, const bool escaped = true); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f0b3740086..531aca7a82 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -87,6 +87,7 @@ #include "llsecondlifeurls.h" // Linden library includes +#include "llavatarnamecache.h" #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" @@ -159,7 +160,6 @@ // Included so that constants/settings might be initialized // in save_settings_to_globals() #include "llbutton.h" -#include "llcombobox.h" #include "llstatusbar.h" #include "llsurface.h" #include "llvosky.h" @@ -405,9 +405,6 @@ static void settings_to_globals() MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight"); MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth"); - LLCOMBOBOX_HEIGHT = BTN_HEIGHT - 2; - LLCOMBOBOX_WIDTH = 128; - LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize")); LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); @@ -1302,8 +1299,7 @@ bool LLAppViewer::cleanup() LLPolyMesh::freeAllMeshes(); - delete gCacheName; - gCacheName = NULL; + LLStartUp::cleanupNameCache(); // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. @@ -3382,6 +3378,15 @@ void LLAppViewer::saveFinalSnapshot() void LLAppViewer::loadNameCache() { + // display names cache + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + llifstream name_cache_stream(filename); + if(name_cache_stream.is_open()) + { + LLAvatarNameCache::importFile(name_cache_stream); + } + if (!gCacheName) return; std::string name_cache; @@ -3391,19 +3396,19 @@ void LLAppViewer::loadNameCache() { if(gCacheName->importFile(cache_file)) return; } +} - // Try to load from the legacy format. This should go away after a - // while. Phoenix 2008-01-30 - LLFILE* name_cache_fp = LLFile::fopen(name_cache, "r"); // Flawfinder: ignore - if (name_cache_fp) +void LLAppViewer::saveNameCache() { - gCacheName->importFile(name_cache_fp); - fclose(name_cache_fp); - } + // display names cache + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + llofstream name_cache_stream(filename); + if(name_cache_stream.is_open()) + { + LLAvatarNameCache::exportFile(name_cache_stream); } -void LLAppViewer::saveNameCache() -{ if (!gCacheName) return; std::string name_cache; @@ -3599,6 +3604,7 @@ void LLAppViewer::idle() // NOTE: Starting at this point, we may still have pointers to "dead" objects // floating throughout the various object lists. // + idleNameCache(); idleNetwork(); @@ -3936,6 +3942,60 @@ void LLAppViewer::sendLogoutRequest() } } +void LLAppViewer::idleNameCache() +{ + // Neither old nor new name cache can function before agent has a region + LLViewerRegion* region = gAgent.getRegion(); + if (!region) return; + + // deal with any queued name requests and replies. + gCacheName->processPending(); + + // Can't run the new cache until we have the list of capabilities + // for the agent region, and can therefore decide whether to use + // display names or fall back to the old name system. + if (!region->capabilitiesReceived()) return; + + // Agent may have moved to a different region, so need to update cap URL + // for name lookups. Can't do this in the cap grant code, as caps are + // granted to neighbor regions before the main agent gets there. Can't + // do it in the move-into-region code because cap not guaranteed to be + // granted yet, for example on teleport. + bool had_capability = LLAvatarNameCache::hasNameLookupURL(); + std::string name_lookup_url; + name_lookup_url.reserve(128); // avoid a memory allocation below + name_lookup_url = region->getCapability("GetDisplayNames"); + bool have_capability = !name_lookup_url.empty(); + if (have_capability) + { + // we have support for display names, use it + U32 url_size = name_lookup_url.size(); + // capabilities require URLs with slashes before query params: + // https://<host>:<port>/cap/<uuid>/?ids=<blah> + // but the caps are granted like: + // https://<host>:<port>/cap/<uuid> + if (url_size > 0 && name_lookup_url[url_size-1] != '/') + { + name_lookup_url += '/'; + } + LLAvatarNameCache::setNameLookupURL(name_lookup_url); + } + else + { + // Display names not available on this region + LLAvatarNameCache::setNameLookupURL( std::string() ); + } + + // Error recovery - did we change state? + if (had_capability != have_capability) + { + // name tags are persistant on screen, so make sure they refresh + LLVOAvatar::invalidateNameTags(); + } + + LLAvatarNameCache::idle(); +} + // // Handle messages, and all message related stuff // @@ -3961,8 +4021,6 @@ void LLAppViewer::idleNetwork() { LLFastTimer t(FTM_IDLE_NETWORK); // decode - // deal with any queued name requests and replies. - gCacheName->processPending(); LLTimer check_message_timer; // Read all available packets from network const S64 frame_count = gFrameCount; // U32->S64 diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 5acd6e11c4..6e6a382e46 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -211,6 +211,8 @@ private: void idle(); void idleShutdown(); + // update avatar SLID and display name caches + void idleNameCache(); void idleNetwork(); void sendLogoutRequest(); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 01d6c3a8d5..f6d104cf0a 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -37,11 +37,11 @@ #include "boost/lambda/lambda.hpp" // for lambda::constant +#include "llavatarnamecache.h" // IDEVO #include "llsd.h" #include "lldarray.h" #include "llnotifications.h" #include "llnotificationsutil.h" - #include "roles_constants.h" // for GP_MEMBER_INVITE #include "llagent.h" @@ -69,6 +69,7 @@ #include "llimfloater.h" #include "lltrans.h" #include "llcallingcard.h" +#include "llslurl.h" // IDEVO // static void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name) @@ -80,26 +81,22 @@ void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::strin } LLSD args; - args["NAME"] = name; + args["NAME"] = LLSLURL("agent", id, "completename").getSLURLString(); LLSD payload; payload["id"] = id; payload["name"] = name; - // Look for server versions like: Second Life Server 1.24.4.95600 - if (gLastVersionChannel.find(" 1.24.") != std::string::npos) - { - // Old and busted server version, doesn't support friend - // requests with messages. - LLNotificationsUtil::add("AddFriend", args, payload, &callbackAddFriend); - } - else - { + LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage); - } // add friend to recent people list LLRecentPeople::instance().add(id); } +void on_avatar_name_friendship(const LLUUID& id, const LLAvatarName av_name) +{ + LLAvatarActions::requestFriendshipDialog(id, av_name.getCompleteName()); +} + // static void LLAvatarActions::requestFriendshipDialog(const LLUUID& id) { @@ -108,9 +105,7 @@ void LLAvatarActions::requestFriendshipDialog(const LLUUID& id) return; } - std::string full_name; - gCacheName->getFullName(id, full_name); - requestFriendshipDialog(id, full_name); + LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_friendship, _1, _2)); } // static @@ -135,11 +130,10 @@ void LLAvatarActions::removeFriendsDialog(const uuid_vec_t& ids) if(ids.size() == 1) { LLUUID agent_id = ids[0]; - std::string first, last; - if(gCacheName->getName(agent_id, first, last)) + LLAvatarName av_name; + if(LLAvatarNameCache::get(agent_id, &av_name)) { - args["FIRST_NAME"] = first; - args["LAST_NAME"] = last; + args["NAME"] = av_name.mDisplayName; } msgType = "RemoveFromFriends"; @@ -167,14 +161,6 @@ void LLAvatarActions::offerTeleport(const LLUUID& invitee) if (invitee.isNull()) return; - //waiting until Name Cache gets updated with corresponding avatar name - std::string just_to_request_name; - if (!gCacheName->getFullName(invitee, just_to_request_name)) - { - gCacheName->get(invitee, FALSE, boost::bind((void (*)(const LLUUID&)) &LLAvatarActions::offerTeleport, invitee)); - return; - } - LLDynamicArray<LLUUID> ids; ids.push_back(invitee); offerTeleport(ids); @@ -189,20 +175,11 @@ void LLAvatarActions::offerTeleport(const uuid_vec_t& ids) handle_lure(ids); } -// static -void LLAvatarActions::startIM(const LLUUID& id) +static void on_avatar_name_cache_start_im(const LLUUID& agent_id, + const LLAvatarName& av_name) { - if (id.isNull()) - return; - - std::string name; - if (!gCacheName->getFullName(id, name)) - { - gCacheName->get(id, FALSE, boost::bind(&LLAvatarActions::startIM, id)); - return; - } - - LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, id); + std::string name = av_name.getCompleteName(); + LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id); if (session_id != LLUUID::null) { LLIMFloater::show(session_id); @@ -211,6 +188,16 @@ void LLAvatarActions::startIM(const LLUUID& id) } // static +void LLAvatarActions::startIM(const LLUUID& id) +{ + if (id.isNull()) + return; + + LLAvatarNameCache::get(id, + boost::bind(&on_avatar_name_cache_start_im, _1, _2)); +} + +// static void LLAvatarActions::endIM(const LLUUID& id) { if (id.isNull()) @@ -223,6 +210,18 @@ void LLAvatarActions::endIM(const LLUUID& id) } } +static void on_avatar_name_cache_start_call(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + std::string name = av_name.getCompleteName(); + LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, true); + if (session_id != LLUUID::null) + { + gIMMgr->startCall(session_id); + } + make_ui_sound("UISndStartIM"); +} + // static void LLAvatarActions::startCall(const LLUUID& id) { @@ -230,15 +229,8 @@ void LLAvatarActions::startCall(const LLUUID& id) { return; } - - std::string name; - gCacheName->getFullName(id, name); - LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, id, true); - if (session_id != LLUUID::null) - { - gIMMgr->startCall(session_id); - } - make_ui_sound("UISndStartIM"); + LLAvatarNameCache::get(id, + boost::bind(&on_avatar_name_cache_start_call, _1, _2)); } // static @@ -334,14 +326,14 @@ void LLAvatarActions::showProfile(const LLUUID& id) // static void LLAvatarActions::showOnMap(const LLUUID& id) { - std::string name; - if (!gCacheName->getFullName(id, name)) + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) { - gCacheName->get(id, FALSE, boost::bind(&LLAvatarActions::showOnMap, id)); + LLAvatarNameCache::get(id, boost::bind(&LLAvatarActions::showOnMap, id)); return; } - gFloaterWorldMap->trackAvatar(id, name); + gFloaterWorldMap->trackAvatar(id, av_name.mDisplayName); LLFloaterReg::showInstance("world_map"); } @@ -584,7 +576,7 @@ namespace action_give_inventory } else { - LLGiveInventory::doGiveInventoryItem(avatar_uuid, inv_item, session_id); + LLGiveInventory::doGiveInventoryItem(avatar_uuid, inv_item, session_id); shared = true; } } @@ -625,7 +617,7 @@ namespace action_give_inventory if (NULL == active_panel) { return; - } + } const uuid_set_t inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); if (inventory_selected_uuids.empty()) @@ -663,7 +655,7 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) { std::string name; - gCacheName->getFullName(id, name); + gCacheName->getFullName(id, name); // needed for mute LLMute mute(id, name, LLMute::AGENT); if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) @@ -835,23 +827,6 @@ bool LLAvatarActions::handleUnfreeze(const LLSD& notification, const LLSD& respo } return false; } -// static -bool LLAvatarActions::callbackAddFriend(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) - { - // Servers older than 1.25 require the text of the message to be the - // calling card folder ID for the offering user. JC - LLUUID calling_card_folder_id = - gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - std::string message = calling_card_folder_id.asString(); - requestFriendship(notification["payload"]["id"].asUUID(), - notification["payload"]["name"].asString(), - message); - } - return false; -} // static void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message) @@ -869,7 +844,6 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri LLSD payload; payload["from_id"] = target_id; - payload["SESSION_NAME"] = target_name; payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipOffered", args, payload); } @@ -884,16 +858,16 @@ bool LLAvatarActions::isFriend(const LLUUID& id) bool LLAvatarActions::isBlocked(const LLUUID& id) { std::string name; - gCacheName->getFullName(id, name); + gCacheName->getFullName(id, name); // needed for mute return LLMuteList::getInstance()->isMuted(id, name); } // static bool LLAvatarActions::canBlock(const LLUUID& id) { - std::string firstname, lastname; - gCacheName->getName(id, firstname, lastname); - bool is_linden = !LLStringUtil::compareStrings(lastname, "Linden"); + std::string full_name; + gCacheName->getFullName(id, full_name); // needed for mute + bool is_linden = (full_name.find("Linden") != std::string::npos); bool is_self = id == gAgentID; return !is_self && !is_linden; } diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index d106a83eea..2d69c01ffe 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -184,7 +184,6 @@ public: private: - static bool callbackAddFriend(const LLSD& notification, const LLSD& response); static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); static bool handleRemove(const LLSD& notification, const LLSD& response); static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 87b8d807c4..f4d5d45726 100644 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -34,17 +34,19 @@ #include "llavatariconctrl.h" +// viewer includes #include "llagent.h" #include "llavatarconstants.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llavataractions.h" #include "llmenugl.h" #include "lluictrlfactory.h" - -#include "llcachename.h" #include "llagentdata.h" #include "llimfloater.h" +// library includes +#include "llavatarnamecache.h" + #define MENU_ITEM_VIEW_PROFILE 0 #define MENU_ITEM_SEND_IM 1 @@ -233,6 +235,9 @@ void LLAvatarIconCtrl::setValue(const LLSD& value) // Check if cache already contains image_id for that avatar if (!updateFromCache()) { + // *TODO: Consider getting avatar icon/badge directly from + // People API, rather than sending AvatarPropertyRequest + // messages. People API already hits the user table. LLIconCtrl::setValue(mDefaultIconName); app->addObserver(mAvatarId, this); app->sendAvatarPropertiesRequest(mAvatarId); @@ -244,7 +249,9 @@ void LLAvatarIconCtrl::setValue(const LLSD& value) LLIconCtrl::setValue(value); } - gCacheName->get(mAvatarId, FALSE, boost::bind(&LLAvatarIconCtrl::nameUpdatedCallback, this, _1, _2, _3, _4)); + LLAvatarNameCache::get(mAvatarId, + boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, + this, _1, _2)); } bool LLAvatarIconCtrl::updateFromCache() @@ -287,24 +294,21 @@ void LLAvatarIconCtrl::processProperties(void* data, EAvatarProcessorType type) } } -void LLAvatarIconCtrl::nameUpdatedCallback( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) +void LLAvatarIconCtrl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { - if (id == mAvatarId) + if (agent_id == mAvatarId) { - mFirstName = first; - mLastName = last; + // Most avatar icon controls are next to a UI element that shows + // a display name, so only show username. + mFullName = av_name.mUsername; if (mDrawTooltip) { - setToolTip(mFirstName + " " + mLastName); + setToolTip(mFullName); } else { - setToolTip(std::string("")); + setToolTip(std::string()); } } } diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h index 38616b7852..5befc73d92 100644 --- a/indra/newview/llavatariconctrl.h +++ b/indra/newview/llavatariconctrl.h @@ -37,6 +37,8 @@ #include "llavatarpropertiesprocessor.h" #include "llviewermenu.h" +class LLAvatarName; + class LLAvatarIconIDCache: public LLSingleton<LLAvatarIconIDCache> { public: @@ -90,22 +92,16 @@ public: // LLAvatarPropertiesProcessor observer trigger virtual void processProperties(void* data, EAvatarProcessorType type); - void nameUpdatedCallback( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); const LLUUID& getAvatarId() const { return mAvatarId; } - const std::string& getFirstName() const { return mFirstName; } - const std::string& getLastName() const { return mLastName; } + const std::string& getFullName() const { return mFullName; } void setDrawTooltip(bool value) { mDrawTooltip = value;} protected: LLUUID mAvatarId; - std::string mFirstName; - std::string mLastName; + std::string mFullName; bool mDrawTooltip; std::string mDefaultIconName; diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 3275d784a3..c35e71cc71 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -44,6 +44,7 @@ // newview #include "llagentdata.h" // for comparator #include "llavatariconctrl.h" +#include "llavatarnamecache.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llcachename.h" #include "lllistcontextmenu.h" @@ -242,12 +243,15 @@ void LLAvatarList::refresh() // Handle added items. unsigned nadded = 0; + const std::string waiting_str = LLTrans::getString("AvatarNameWaiting"); + for (uuid_vec_t::const_iterator it=added.begin(); it != added.end(); it++) { - std::string name; const LLUUID& buddy_id = *it; - have_names &= (bool)gCacheName->getFullName(buddy_id, name); - if (!have_filter || findInsensitive(name, mNameFilter)) + LLAvatarName av_name; + have_names &= LLAvatarNameCache::get(buddy_id, &av_name); + + if (!have_filter || findInsensitive(av_name.mDisplayName, mNameFilter)) { if (nadded >= ADD_LIMIT) { @@ -256,7 +260,11 @@ void LLAvatarList::refresh() } else { - addNewItem(buddy_id, name, LLAvatarTracker::instance().isBuddyOnline(buddy_id)); + // *NOTE: If you change the UI to show a different string, + // be sure to change the filter code below. + addNewItem(buddy_id, + av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName, + LLAvatarTracker::instance().isBuddyOnline(buddy_id)); modified = true; nadded++; } @@ -278,10 +286,10 @@ void LLAvatarList::refresh() for (std::vector<LLSD>::const_iterator it=cur_values.begin(); it != cur_values.end(); it++) { - std::string name; const LLUUID& buddy_id = it->asUUID(); - have_names &= (bool)gCacheName->getFullName(buddy_id, name); - if (!findInsensitive(name, mNameFilter)) + LLAvatarName av_name; + have_names &= LLAvatarNameCache::get(buddy_id, &av_name); + if (!findInsensitive(av_name.mDisplayName, mNameFilter)) { removeItemByUUID(buddy_id); modified = true; @@ -333,14 +341,14 @@ bool LLAvatarList::filterHasMatches() for (uuid_vec_t::const_iterator it=values.begin(); it != values.end(); it++) { - std::string name; const LLUUID& buddy_id = *it; - BOOL have_name = gCacheName->getFullName(buddy_id, name); + LLAvatarName av_name; + bool have_name = LLAvatarNameCache::get(buddy_id, &av_name); // If name has not been loaded yet we consider it as a match. // When the name will be loaded the filter will be applied again(in refresh()). - if (have_name && !findInsensitive(name, mNameFilter)) + if (have_name && !findInsensitive(av_name.mDisplayName, mNameFilter)) { continue; } @@ -374,7 +382,7 @@ S32 LLAvatarList::notifyParent(const LLSD& info) void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos) { LLAvatarListItem* item = new LLAvatarListItem(); - item->setName(name); + // This sets the name as a side effect item->setAvatarId(id, mSessionID, mIgnoreOnlineStatus); item->setOnline(mIgnoreOnlineStatus ? true : is_online); item->showLastInteractionTime(mShowLastInteractionTime); @@ -540,11 +548,13 @@ void LLAvalineListItem::setName(const std::string& name) std::string hidden_name = LLTrans::getString("AvalineCaller", args); LL_DEBUGS("Avaline") << "Avaline caller: " << uuid << ", name: " << hidden_name << LL_ENDL; - LLAvatarListItem::setName(hidden_name); + LLAvatarListItem::setAvatarName(hidden_name); + LLAvatarListItem::setAvatarToolTip(hidden_name); } else { const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name); - LLAvatarListItem::setName(formatted_phone); + LLAvatarListItem::setAvatarName(formatted_phone); + LLAvatarListItem::setAvatarToolTip(formatted_phone); } } diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index fffc6e6e73..b9b85094fb 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -34,7 +34,6 @@ #define LL_LLAVATARLIST_H #include "llflatlistview.h" - #include "llavatarlistitem.h" class LLTimer; diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index c74075d67f..079a7d3ed2 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -41,6 +41,7 @@ #include "lltextutil.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "lloutputmonitorctrl.h" @@ -167,11 +168,16 @@ void LLAvatarListItem::setOnline(bool online) setState(online ? IS_ONLINE : IS_OFFLINE); } -void LLAvatarListItem::setName(const std::string& name) +void LLAvatarListItem::setAvatarName(const std::string& name) { setNameInternal(name, mHighlihtSubstring); } +void LLAvatarListItem::setAvatarToolTip(const std::string& tooltip) +{ + mAvatarName->setToolTip(tooltip); +} + void LLAvatarListItem::setHighlight(const std::string& highlight) { setNameInternal(mAvatarName->getText(), mHighlihtSubstring = highlight); @@ -230,7 +236,8 @@ void LLAvatarListItem::setAvatarId(const LLUUID& id, const LLUUID& session_id, b mAvatarIcon->setValue(id); // Set avatar name. - gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); + LLAvatarNameCache::get(id, + boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2)); } } @@ -323,23 +330,27 @@ const LLUUID& LLAvatarListItem::getAvatarId() const return mAvatarId; } -const std::string LLAvatarListItem::getAvatarName() const +std::string LLAvatarListItem::getAvatarName() const { return mAvatarName->getValue(); } +std::string LLAvatarListItem::getAvatarToolTip() const +{ + return mAvatarName->getToolTip(); +} + //== PRIVATE SECITON ========================================================== void LLAvatarListItem::setNameInternal(const std::string& name, const std::string& highlight) { LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle, name, highlight); - mAvatarName->setToolTip(name); } -void LLAvatarListItem::onNameCache(const std::string& first_name, const std::string& last_name) +void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name) { - std::string name = first_name + " " + last_name; - setName(name); + setAvatarName(av_name.mDisplayName); + setAvatarToolTip(av_name.mUsername); //requesting the list to resort notifyParent(LLSD().with("sort", LLSD())); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index ba9c3574d5..78257ce9ba 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -42,6 +42,7 @@ #include "llcallingcard.h" // for LLFriendObserver class LLAvatarIconCtrl; +class LLAvatarName; class LLAvatarListItem : public LLPanel, public LLFriendObserver { @@ -91,7 +92,8 @@ public: virtual void changed(U32 mask); // from LLFriendObserver void setOnline(bool online); - void setName(const std::string& name); + void setAvatarName(const std::string& name); + void setAvatarToolTip(const std::string& tooltip); void setHighlight(const std::string& highlight); void setState(EItemState item_style); void setAvatarId(const LLUUID& id, const LLUUID& session_id, bool ignore_status_changes = false, bool is_resident = true); @@ -104,7 +106,8 @@ public: void setAvatarIconVisible(bool visible); const LLUUID& getAvatarId() const; - const std::string getAvatarName() const; + std::string getAvatarName() const; + std::string getAvatarToolTip() const; void onInfoBtnClick(); void onProfileBtnClick(); @@ -145,7 +148,7 @@ private: } EAvatarListItemChildIndex; void setNameInternal(const std::string& name, const std::string& highlight); - void onNameCache(const std::string& first_name, const std::string& last_name); + void onAvatarNameCache(const LLAvatarName& av_name); std::string formatSeconds(U32 secs); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index dd99c6564c..9120990813 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -33,13 +33,14 @@ #include "llviewerprecompiledheaders.h" +#include "llcallfloater.h" + #include "llnotificationsutil.h" #include "lltrans.h" -#include "llcallfloater.h" - #include "llagent.h" #include "llagentdata.h" // for gAgentID +#include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "llavatarlist.h" #include "llbottomtray.h" @@ -83,7 +84,8 @@ public: void setName(const std::string& name) { const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name); - LLAvatarListItem::setName(formatted_phone); + LLAvatarListItem::setAvatarName(formatted_phone); + LLAvatarListItem::setAvatarToolTip(formatted_phone); } void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); } @@ -375,9 +377,31 @@ void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) call_floater->connectToChannel(channel); } +void LLCallFloater::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + LLStringUtil::format_map_t args; + args["[NAME]"] = av_name.getCompleteName(); + std::string title = getString("title_peer_2_peer", args); + setTitle(title); +} + void LLCallFloater::updateTitle() { LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); + if (mVoiceType == VC_PEER_TO_PEER) + { + LLUUID session_id = voice_channel->getSessionID(); + LLIMModel::LLIMSession* im_session = + LLIMModel::getInstance()->findIMSession(session_id); + if (im_session) + { + LLAvatarNameCache::get(im_session->mOtherParticipantID, + boost::bind(&LLCallFloater::onAvatarNameCache, + this, _1, _2)); + return; + } + } std::string title; switch (mVoiceType) { @@ -422,9 +446,10 @@ void LLCallFloater::initAgentData() { mAgentPanel->childSetValue("user_icon", gAgentID); - std::string name; - gCacheName->getFullName(gAgentID, name); - mAgentPanel->childSetValue("user_text", name); + // Just use display name, because it's you + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + mAgentPanel->childSetValue("user_text", av_name.mDisplayName); mSpeakingIndicator = mAgentPanel->getChild<LLOutputMonitorCtrl>("speaking_indicator"); mSpeakingIndicator->setSpeakerId(gAgentID); @@ -445,7 +470,10 @@ void LLCallFloater::setModeratorMutedVoice(bool moderator_muted) void LLCallFloater::updateAgentModeratorState() { std::string name; - gCacheName->getFullName(gAgentID, name); + // Just use display name, because it's you + LLAvatarName av_name; + LLAvatarNameCache::get(gAgentID, &av_name); + name = av_name.mDisplayName; if(gAgent.isInGroup(mSpeakerManager->getSessionID())) { diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 0a8ea7de39..4ec594a23a 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -40,6 +40,7 @@ class LLAvatarList; class LLAvatarListItem; +class LLAvatarName; class LLNonAvatarCaller; class LLOutputMonitorCtrl; class LLParticipantList; @@ -122,6 +123,10 @@ private: */ void onAvatarListRefreshed(); + /** + * Updates window title with an avatar name + */ + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); void updateTitle(); void initAgentData(); diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 1a6c11fa73..1685562013 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -43,6 +43,7 @@ //#include <iterator> #include "indra_constants.h" +#include "llavatarnamecache.h" #include "llcachename.h" #include "llstl.h" #include "lltimer.h" @@ -50,6 +51,7 @@ #include "message.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llbutton.h" #include "llinventoryobserver.h" #include "llinventorymodel.h" @@ -96,8 +98,10 @@ const F32 OFFLINE_SECONDS = FIND_FREQUENCY + 8.0f; // static LLAvatarTracker LLAvatarTracker::sInstance; - - +static void on_avatar_name_cache_notify(const LLUUID& agent_id, + const LLAvatarName& av_name, + bool online, + LLSD payload); ///---------------------------------------------------------------------------- /// Class LLAvatarTracker @@ -250,7 +254,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds) using namespace std; U32 new_buddy_count = 0; - std::string first,last; + std::string full_name; LLUUID agent_id; for(buddy_map_t::const_iterator itr = buds.begin(); itr != buds.end(); ++itr) { @@ -260,7 +264,8 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds) { ++new_buddy_count; mBuddyInfo[agent_id] = (*itr).second; - gCacheName->getName(agent_id, first, last); + // IDEVO: is this necessary? name is unused? + gCacheName->getFullName(agent_id, full_name); addChangedMask(LLFriendObserver::ADD, agent_id); lldebugs << "Added buddy " << agent_id << ", " << (mBuddyInfo[agent_id]->isOnline() ? "Online" : "Offline") @@ -633,12 +638,9 @@ void LLAvatarTracker::processChange(LLMessageSystem* msg) { if((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^ new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS) { - std::string name; LLSD args; - if(gCacheName->getFullName(agent_id, name)) - { - args["NAME"] = name; - } + args["NAME"] = LLSLURL("agent", agent_id, "displayname").getSLURLString(); + LLSD payload; payload["from_id"] = agent_id; if(LLRelationship::GRANT_MODIFY_OBJECTS & new_rights) @@ -680,8 +682,6 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) { tracking_id = mTrackingData->mAvatarID; } - BOOL notify = FALSE; - LLSD args; LLSD payload; for(S32 i = 0; i < count; ++i) { @@ -691,17 +691,6 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) if(info) { setBuddyOnline(agent_id,online); - if(chat_notify) - { - std::string first, last; - if(gCacheName->getName(agent_id, first, last)) - { - notify = TRUE; - args["FIRST"] = first; - args["LAST"] = last; - } - - } } else { @@ -717,29 +706,12 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) // *TODO: get actual inventory id gInventory.addChangedMask(LLInventoryObserver::CALLING_CARD, LLUUID::null); } - if(notify) + if(chat_notify) { - // Popup a notify box with online status of this agent - LLNotificationPtr notification; - - if (online) - { - notification = - LLNotificationsUtil::add("FriendOnline", - args, - payload.with("respond_on_mousedown", TRUE), - boost::bind(&LLAvatarActions::startIM, agent_id)); - } - else - { - notification = - LLNotificationsUtil::add("FriendOffline", args, payload); - } - - // If there's an open IM session with this agent, send a notification there too. - LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, agent_id); - std::string notify_msg = notification->getMessage(); - LLIMModel::instance().proccessOnlineOfflineNotification(session_id, notify_msg); + // Look up the name of this agent for the notification + LLAvatarNameCache::get(agent_id, + boost::bind(&on_avatar_name_cache_notify, + _1, _2, online, payload)); } mModifyMask |= LLFriendObserver::ONLINE; @@ -748,6 +720,37 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) } } +static void on_avatar_name_cache_notify(const LLUUID& agent_id, + const LLAvatarName& av_name, + bool online, + LLSD payload) +{ + // Popup a notify box with online status of this agent + // Use display name only because this user is your friend + LLSD args; + args["NAME"] = av_name.mDisplayName; + + LLNotificationPtr notification; + if (online) + { + notification = + LLNotificationsUtil::add("FriendOnline", + args, + payload.with("respond_on_mousedown", TRUE), + boost::bind(&LLAvatarActions::startIM, agent_id)); + } + else + { + notification = + LLNotificationsUtil::add("FriendOffline", args, payload); + } + + // If there's an open IM session with this agent, send a notification there too. + LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, agent_id); + std::string notify_msg = notification->getMessage(); + LLIMModel::instance().proccessOnlineOfflineNotification(session_id, notify_msg); +} + void LLAvatarTracker::formFriendship(const LLUUID& id) { if(id.notNull()) @@ -868,10 +871,9 @@ bool LLCollectProxyBuddies::operator()(const LLUUID& buddy_id, LLRelationship* b bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy) { - gCacheName->getName(buddy_id, mFirst, mLast); - std::ostringstream fullname; - fullname << mFirst << " " << mLast; - buddy_map_t::value_type value(fullname.str(), buddy_id); + LLAvatarName av_name; + LLAvatarNameCache::get( buddy_id, &av_name); + buddy_map_t::value_type value(av_name.mDisplayName, buddy_id); if(buddy->isOnline() && buddy->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) { mMappable.insert(value); @@ -881,10 +883,8 @@ bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship bool LLCollectOnlineBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy) { - gCacheName->getName(buddy_id, mFirst, mLast); - std::ostringstream fullname; - fullname << mFirst << " " << mLast; - buddy_map_t::value_type value(fullname.str(), buddy_id); + gCacheName->getFullName(buddy_id, mFullName); + buddy_map_t::value_type value(mFullName, buddy_id); if(buddy->isOnline()) { mOnline.insert(value); @@ -894,10 +894,10 @@ bool LLCollectOnlineBuddies::operator()(const LLUUID& buddy_id, LLRelationship* bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy) { - gCacheName->getName(buddy_id, mFirst, mLast); - std::ostringstream fullname; - fullname << mFirst << " " << mLast; - buddy_map_t::value_type value(fullname.str(), buddy_id); + LLAvatarName av_name; + LLAvatarNameCache::get(buddy_id, &av_name); + mFullName = av_name.mDisplayName; + buddy_map_t::value_type value(mFullName, buddy_id); if(buddy->isOnline()) { mOnline.insert(value); @@ -908,5 +908,3 @@ bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* bud } return true; } - - diff --git a/indra/newview/llcallingcard.h b/indra/newview/llcallingcard.h index 47b0dcb903..5bd1f05687 100644 --- a/indra/newview/llcallingcard.h +++ b/indra/newview/llcallingcard.h @@ -241,8 +241,7 @@ public: virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy); typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t; buddy_map_t mMappable; - std::string mFirst; - std::string mLast; + std::string mFullName; }; // collect dictionary sorted map of name -> agent_id for every online buddy @@ -254,8 +253,7 @@ public: virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy); typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t; buddy_map_t mOnline; - std::string mFirst; - std::string mLast; + std::string mFullName; }; // collect dictionary sorted map of name -> agent_id for every buddy, @@ -269,8 +267,7 @@ public: typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t; buddy_map_t mOnline; buddy_map_t mOffline; - std::string mFirst; - std::string mLast; + std::string mFullName; }; #endif // LL_LLCALLINGCARD_H diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 0bd03571da..b7bccf46be 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -32,10 +32,12 @@ #include "llviewerprecompiledheaders.h" +#include "llchathistory.h" + +#include "llavatarnamecache.h" #include "llinstantmessage.h" #include "llimview.h" -#include "llchathistory.h" #include "llcommandhandler.h" #include "llpanel.h" #include "lluictrlfactory.h" @@ -104,6 +106,18 @@ LLObjectIMHandler gObjectIMHandler; class LLChatHistoryHeader: public LLPanel { public: + LLChatHistoryHeader() + : LLPanel(), + mPopupMenuHandleAvatar(), + mPopupMenuHandleObject(), + mAvatarID(), + mSourceType(CHAT_SOURCE_UNKNOWN), + mFrom(), + mSessionID(), + mMinUserNameWidth(0), + mUserNameFont(NULL) + {} + static LLChatHistoryHeader* createInstance(const std::string& file_name) { LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; @@ -240,7 +254,6 @@ public: mAvatarID = chat.mFromID; mSessionID = chat.mSessionID; mSourceType = chat.mSourceType; - gCacheName->get(mAvatarID, FALSE, boost::bind(&LLChatHistoryHeader::nameUpdatedCallback, this, _1, _2, _3, _4)); //*TODO overly defensive thing, source type should be maintained out there if((chat.mFromID.isNull() && chat.mFromName.empty()) || chat.mFromName == SYSTEM_FROM) @@ -248,22 +261,40 @@ public: mSourceType = CHAT_SOURCE_SYSTEM; } - LLTextBox* userName = getChild<LLTextBox>("user_name"); + mUserNameFont = style_params.font(); + LLTextBox* user_name = getChild<LLTextBox>("user_name"); + user_name->setReadOnlyColor(style_params.readonly_color()); + user_name->setColor(style_params.color()); - userName->setReadOnlyColor(style_params.readonly_color()); - userName->setColor(style_params.color()); - - userName->setValue(chat.mFromName); - mFrom = chat.mFromName; - if (chat.mFromName.empty() || CHAT_SOURCE_SYSTEM == mSourceType) + if (chat.mFromName.empty() + || mSourceType == CHAT_SOURCE_SYSTEM + || mAvatarID.isNull()) { mFrom = LLTrans::getString("SECOND_LIFE"); - userName->setValue(mFrom); + user_name->setValue(mFrom); + updateMinUserNameWidth(); + } + else if (mSourceType == CHAT_SOURCE_AGENT + && chat.mChatStyle != CHAT_STYLE_HISTORY) + { + // ...from a normal user, lookup the name and fill in later. + // *NOTE: Do not do this for chat history logs, otherwise the viewer + // will flood the People API with lookup requests on startup + + // Start with blank so sample data from XUI XML doesn't + // flash on the screen + user_name->setValue( LLSD() ); + LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); + } + else { + // ...from an object, just use name as given + mFrom = chat.mFromName; + user_name->setValue(mFrom); + updateMinUserNameWidth(); } - mMinUserNameWidth = style_params.font()->getWidth(userName->getWText().c_str()) + PADDING; - setTimeField(chat); LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); @@ -317,12 +348,28 @@ public: LLPanel::draw(); } - void nameUpdatedCallback(const LLUUID& id,const std::string& first,const std::string& last,BOOL is_group) + void updateMinUserNameWidth() { - if (id != mAvatarID) - return; - mFrom = first + " " + last; + if (mUserNameFont) + { + LLTextBox* user_name = getChild<LLTextBox>("user_name"); + const LLWString& text = user_name->getWText(); + mMinUserNameWidth = mUserNameFont->getWidth(text.c_str()) + PADDING; + } } + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) + { + mFrom = av_name.mDisplayName; + + LLTextBox* user_name = getChild<LLTextBox>("user_name"); + user_name->setValue( LLSD(av_name.mDisplayName ) ); + user_name->setToolTip( av_name.mUsername ); + setToolTip( av_name.mUsername ); + // name might have changed, update width + updateMinUserNameWidth(); + } + protected: static const S32 PADDING = 20; @@ -439,6 +486,7 @@ protected: LLUUID mSessionID; S32 mMinUserNameWidth; + const LLFontGL* mUserNameFont; }; LLUICtrl* LLChatHistoryHeader::sInfoCtrl = NULL; diff --git a/indra/newview/lldateutil.cpp b/indra/newview/lldateutil.cpp index 3e71ecdfba..ae955b6cad 100644 --- a/indra/newview/lldateutil.cpp +++ b/indra/newview/lldateutil.cpp @@ -172,3 +172,23 @@ std::string LLDateUtil::ageFromDate(const std::string& date_string) { return ageFromDate(date_string, LLDate::now()); } + +//std::string LLDateUtil::ageFromDateISO(const std::string& date_string, +// const LLDate& now) +//{ +// S32 born_month, born_day, born_year; +// S32 matched = sscanf(date_string.c_str(), "%d-%d-%d", +// &born_year, &born_month, &born_day); +// if (matched != 3) return "???"; +// date.fromYMDHMS(year, month, day); +// F64 secs_since_epoch = date.secondsSinceEpoch(); +// // Correct for the fact that specified date is in Pacific time, == UTC - 8 +// secs_since_epoch += 8.0 * 60.0 * 60.0; +// date.secondsSinceEpoch(secs_since_epoch); +// return ageFromDate(born_year, born_month, born_day, now); +//} +// +//std::string LLDateUtil::ageFromDateISO(const std::string& date_string) +//{ +// return ageFromDateISO(date_string, LLDate::now()); +//} diff --git a/indra/newview/lldateutil.h b/indra/newview/lldateutil.h index a0df21022e..8d41eea511 100644 --- a/indra/newview/lldateutil.h +++ b/indra/newview/lldateutil.h @@ -67,6 +67,14 @@ namespace LLDateUtil // Calls the above with LLDate::now() std::string ageFromDate(const std::string& date_string); + + // As above, for YYYY-MM-DD dates + //std::string ageFromDateISO(const std::string& date_string, const LLDate& now); + + // Calls the above with LLDate::now() + //std::string ageFromDateISO(const std::string& date_string); + + //std::string ageFromDate(S32 born_year, S32 born_month, S32 born_day, const LLDate& now); } #endif diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 651dabff9e..b8b09b89f1 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -53,6 +53,7 @@ class LLCamera; class LLDrawPool; class LLDrawable; class LLFace; +class LLFacePool; class LLSpatialGroup; class LLSpatialBridge; class LLSpatialPartition; diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 96364f9418..972565743e 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -41,10 +41,14 @@ #include "llimview.h" // for gIMMgr #include "lltooldraganddrop.h" // for LLToolDragAndDrop #include "llviewercontrol.h" +#include "llviewerregion.h" // getCapability() #include "llworld.h" // Linden libraries +#include "llavatarnamecache.h" // IDEVO #include "llbutton.h" +#include "llcachename.h" +#include "llhttpclient.h" // IDEVO #include "lllineeditor.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" @@ -53,6 +57,11 @@ #include "lluictrlfactory.h" #include "message.h" +//#include "llsdserialize.h" + +//put it back as a member once the legacy path is out? +static std::map<LLUUID, LLAvatarName> sAvatarNameMap; + LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, BOOL allow_multiple, BOOL closeOnSelect) @@ -159,7 +168,7 @@ void LLFloaterAvatarPicker::onBtnFind() find(); } -static void getSelectedAvatarData(const LLScrollListCtrl* from, std::vector<std::string>& avatar_names, uuid_vec_t& avatar_ids) +static void getSelectedAvatarData(const LLScrollListCtrl* from, uuid_vec_t& avatar_ids, std::vector<LLAvatarName>& avatar_names) { std::vector<LLScrollListItem*> items = from->getAllSelected(); for (std::vector<LLScrollListItem*>::iterator iter = items.begin(); iter != items.end(); ++iter) @@ -167,8 +176,21 @@ static void getSelectedAvatarData(const LLScrollListCtrl* from, std::vector<std: LLScrollListItem* item = *iter; if (item->getUUID().notNull()) { - avatar_names.push_back(item->getColumn(0)->getValue().asString()); avatar_ids.push_back(item->getUUID()); + + std::map<LLUUID, LLAvatarName>::iterator iter = sAvatarNameMap.find(item->getUUID()); + if (iter != sAvatarNameMap.end()) + { + avatar_names.push_back(iter->second); + } + else + { + // the only case where it isn't in the name map is friends + // but it should be in the name cache + LLAvatarName av_name; + LLAvatarNameCache::get(item->getUUID(), &av_name); + avatar_names.push_back(av_name); + } } } } @@ -204,10 +226,10 @@ void LLFloaterAvatarPicker::onBtnSelect() if(list) { - std::vector<std::string> avatar_names; uuid_vec_t avatar_ids; - getSelectedAvatarData(list, avatar_names, avatar_ids); - mSelectionCallback(avatar_names, avatar_ids); + std::vector<LLAvatarName> avatar_names; + getSelectedAvatarData(list, avatar_ids, avatar_names); + mSelectionCallback(avatar_ids, avatar_names); } } getChild<LLScrollListCtrl>("SearchResults")->deselectAllItems(TRUE); @@ -257,15 +279,22 @@ void LLFloaterAvatarPicker::populateNearMe() if(av == gAgent.getID()) continue; LLSD element; element["id"] = av; // value - std::string fullname; - if(!gCacheName->getFullName(av, fullname)) + LLAvatarName av_name; + + if (!LLAvatarNameCache::get(av, &av_name)) { + element["columns"][0]["column"] = "name"; element["columns"][0]["value"] = LLCacheName::getDefaultName(); all_loaded = FALSE; } else { - element["columns"][0]["value"] = fullname; + element["columns"][0]["column"] = "name"; + element["columns"][0]["value"] = av_name.mDisplayName; + element["columns"][1]["column"] = "username"; + element["columns"][1]["value"] = av_name.mUsername; + + sAvatarNameMap[av] = av_name; } near_me_scroller->addElement(element); empty = FALSE; @@ -300,7 +329,6 @@ void LLFloaterAvatarPicker::populateFriend() LLAvatarTracker::instance().applyFunctor(collector); LLCollectAllBuddies::buddy_map_t::iterator it; - for(it = collector.mOnline.begin(); it!=collector.mOnline.end(); it++) { friends_scroller->addStringUUIDItem(it->first, it->second); @@ -352,14 +380,69 @@ BOOL LLFloaterAvatarPicker::visibleItemsSelected() const return FALSE; } +class LLAvatarPickerResponder : public LLHTTPClient::Responder +{ +public: + LLUUID mQueryID; + + LLAvatarPickerResponder(const LLUUID& id) : mQueryID(id) { } + + /*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) + { + //std::ostringstream ss; + //LLSDSerialize::toPrettyXML(content, ss); + //llinfos << ss.str() << llendl; + + if (isGoodStatus(status)) + { + LLFloaterAvatarPicker* floater = + LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker"); + if (floater) + { + floater->processResponse(mQueryID, content); + } + } + else + { + llinfos << "avatar picker failed " << status + << " reason " << reason << llendl; + } + } +}; + void LLFloaterAvatarPicker::find() { + //clear our stored LLAvatarNames + sAvatarNameMap.clear(); + std::string text = childGetValue("Edit").asString(); mQueryID.generate(); - LLMessageSystem* msg = gMessageSystem; + std::string url; + url.reserve(128); // avoid a memory allocation or two + LLViewerRegion* region = gAgent.getRegion(); + url = region->getCapability("AvatarPickerSearch"); + // Prefer use of capabilities to search on both SLID and display name + // but allow display name search to be manually turned off for test + if (!url.empty() + && LLAvatarNameCache::useDisplayNames()) + { + // capability urls don't end in '/', but we need one to parse + // query parameters correctly + if (url.size() > 0 && url[url.size()-1] != '/') + { + url += "/"; + } + url += "?names="; + url += LLURI::escape(text); + llinfos << "avatar picker " << url << llendl; + LLHTTPClient::get(url, new LLAvatarPickerResponder(mQueryID)); + } + else + { + LLMessageSystem* msg = gMessageSystem; msg->newMessage("AvatarPickerRequest"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgent.getID()); @@ -367,8 +450,8 @@ void LLFloaterAvatarPicker::find() msg->addUUID("QueryID", mQueryID); // not used right now msg->nextBlock("Data"); msg->addString("Name", text); - gAgent.sendReliableMessage(); + } getChild<LLScrollListCtrl>("SearchResults")->deleteAllItems(); getChild<LLScrollListCtrl>("SearchResults")->setCommentText(getString("searching")); @@ -496,12 +579,21 @@ void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void* } else { - avatar_name = first_name + " " + last_name; + avatar_name = LLCacheName::buildFullName(first_name, last_name); search_results->setEnabled(TRUE); found_one = TRUE; + + LLAvatarName av_name; + av_name.mLegacyFirstName = first_name; + av_name.mLegacyLastName = last_name; + av_name.mDisplayName = avatar_name; + const LLUUID& agent_id = avatar_id; + sAvatarNameMap[agent_id] = av_name; + } LLSD element; element["id"] = avatar_id; // value + element["columns"][0]["column"] = "name"; element["columns"][0]["value"] = avatar_name; search_results->addElement(element); } @@ -515,10 +607,61 @@ void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void* } } +void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD& content) +{ + // Check for out-of-date query + if (query_id != mQueryID) return; + + LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>("SearchResults"); + + LLSD agents = content["agents"]; + if (agents.size() == 0) + { + LLStringUtil::format_map_t map; + map["[TEXT]"] = childGetText("Edit"); + LLSD item; + item["id"] = LLUUID::null; + item["columns"][0]["column"] = "name"; + item["columns"][0]["value"] = getString("not_found", map); + search_results->addElement(item); + search_results->setEnabled(FALSE); + childDisable("ok_btn"); + return; + } + + // clear "Searching" label on first results + search_results->deleteAllItems(); + + LLSD item; + LLSD::array_const_iterator it = agents.beginArray(); + for ( ; it != agents.endArray(); ++it) + { + const LLSD& row = *it; + item["id"] = row["id"]; + LLSD& columns = item["columns"]; + columns[0]["column"] = "name"; + columns[0]["value"] = row["display_name"]; + columns[1]["column"] = "username"; + columns[1]["value"] = row["username"]; + search_results->addElement(item); + + // add the avatar name to our list + LLAvatarName avatar_name; + avatar_name.fromLLSD(row); + sAvatarNameMap[row["id"].asUUID()] = avatar_name; + } + + childEnable("ok_btn"); + search_results->setEnabled(true); + search_results->selectFirstItem(); + onList(); + search_results->setFocus(TRUE); +} + //static void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data) { - childSetEnabled("Find", caller->getText().size() >= 3); + childSetEnabled("Find", caller->getText().size() > 0); } // virtual @@ -576,8 +719,8 @@ bool LLFloaterAvatarPicker::isSelectBtnEnabled() if(list) { uuid_vec_t avatar_ids; - std::vector<std::string> avatar_names; - getSelectedAvatarData(list, avatar_names, avatar_ids); + std::vector<LLAvatarName> avatar_names; + getSelectedAvatarData(list, avatar_ids, avatar_names); return mOkButtonValidateSignal(avatar_ids); } } diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h index e69b814f9f..c6f96bee24 100644 --- a/indra/newview/llfloateravatarpicker.h +++ b/indra/newview/llfloateravatarpicker.h @@ -37,6 +37,7 @@ #include <vector> +class LLAvatarName; class LLScrollListCtrl; class LLFloaterAvatarPicker : public LLFloater @@ -46,7 +47,7 @@ public: typedef validate_signal_t::slot_type validate_callback_t; // The callback function will be called with an avatar name and UUID. - typedef boost::function<void (const std::vector<std::string>&, const uuid_vec_t&)> select_callback_t; + typedef boost::function<void (const uuid_vec_t&, const std::vector<LLAvatarName>&)> select_callback_t; // Call this to select an avatar. static LLFloaterAvatarPicker* show(select_callback_t callback, BOOL allow_multiple = FALSE, @@ -60,6 +61,7 @@ public: void setOkBtnEnableCb(validate_callback_t cb); static void processAvatarPickerReply(class LLMessageSystem* msg, void**); + void processResponse(const LLUUID& query_id, const LLSD& content); BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp index 847462a6c3..f09071e92d 100644 --- a/indra/newview/llfloateravatartextures.cpp +++ b/indra/newview/llfloateravatartextures.cpp @@ -33,6 +33,9 @@ #include "llviewerprecompiledheaders.h" #include "llfloateravatartextures.h" +// library headers +#include "llavatarnamecache.h" + #include "llagent.h" #include "llagentwearables.h" #include "lltexturectrl.h" @@ -138,10 +141,10 @@ void LLFloaterAvatarTextures::refresh() LLVOAvatar *avatarp = find_avatar(mID); if (avatarp) { - std::string fullname; - if (gCacheName->getFullName(avatarp->getID(), fullname)) + LLAvatarName av_name; + if (LLAvatarNameCache::get(avatarp->getID(), &av_name)) { - setTitle(mTitle + ": " + fullname); + setTitle(mTitle + ": " + av_name.getCompleteName()); } for (U32 i=0; i < TEX_NUM_INDICES; i++) { diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp index e925796526..9ccae43a92 100644 --- a/indra/newview/llfloaterbump.cpp +++ b/indra/newview/llfloaterbump.cpp @@ -89,7 +89,7 @@ void LLFloaterBump::onOpen(const LLSD& key) void LLFloaterBump::add(LLScrollListCtrl* list, LLMeanCollisionData* mcd) { - if (mcd->mFirstName.empty() || list->getItemCount() >= 20) + if (mcd->mFullName.empty() || list->getItemCount() >= 20) { return; } @@ -127,8 +127,7 @@ void LLFloaterBump::add(LLScrollListCtrl* list, LLMeanCollisionData* mcd) // All above action strings are in XML file LLUIString text = getString(action); text.setArg("[TIME]", timeStr); - text.setArg("[FIRST]", mcd->mFirstName); - text.setArg("[LAST]", mcd->mLastName); + text.setArg("[NAME]", mcd->mFullName); LLSD row; row["id"] = mcd->mPerp; diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 7ca7ace0fc..2faa9531c4 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -186,9 +186,8 @@ public: void updateNames(); // Name cache callback void updateGroupName(const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group); + const std::string& name, + bool is_group); void refreshUI(); @@ -824,28 +823,26 @@ void LLFloaterBuyLandUI::updateNames() } else if (parcelp->getIsGroupOwned()) { - gCacheName->get(parcelp->getGroupID(), TRUE, + gCacheName->getGroup(parcelp->getGroupID(), boost::bind(&LLFloaterBuyLandUI::updateGroupName, this, - _1, _2, _3, _4)); + _1, _2, _3)); } else { - mParcelSellerName = - LLSLURL("agent", parcelp->getOwnerID(), "inspect").getSLURLString(); + mParcelSellerName = LLSLURL("agent", parcelp->getOwnerID(), "completename").getSLURLString(); } } void LLFloaterBuyLandUI::updateGroupName(const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group) + const std::string& name, + bool is_group) { LLParcel* parcelp = mParcel->getParcel(); if (parcelp && parcelp->getGroupID() == id) { // request is current - mParcelSellerName = first_name; + mParcelSellerName = name; } } diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index bd07cfdfbf..44f104d687 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -34,6 +34,7 @@ #include "llfloatergodtools.h" +#include "llavatarnamecache.h" #include "llcoord.h" #include "llfontgl.h" #include "llframetimer.h" @@ -1151,11 +1152,11 @@ void LLPanelObjectTools::onClickSetBySelection(void* data) panelp->childSetValue("target_avatar_name", name); } -void LLPanelObjectTools::callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelObjectTools::callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (ids.empty() || names.empty()) return; mTargetAvatar = ids[0]; - childSetValue("target_avatar_name", names[0]); + childSetValue("target_avatar_name", names[0].getCompleteName()); refresh(); } diff --git a/indra/newview/llfloatergodtools.h b/indra/newview/llfloatergodtools.h index aee9db78a3..438f1024ce 100644 --- a/indra/newview/llfloatergodtools.h +++ b/indra/newview/llfloatergodtools.h @@ -41,6 +41,7 @@ #include "llpanel.h" #include <vector> +class LLAvatarName; class LLButton; class LLCheckBoxCtrl; class LLComboBox; @@ -231,7 +232,7 @@ public: void onChangeAnything(); void onApplyChanges(); void onClickSet(); - void callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids); + void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); void onClickDeletePublicOwnedBy(); void onClickDeleteAllScriptedOwnedBy(); void onClickDeleteAllOwnedBy(); diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 13ca7638c5..f22ab70678 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -37,7 +37,7 @@ #include "llfloaterreg.h" #include "llfloatertools.h" #include "llavataractions.h" -#include "llcachename.h" +#include "llavatarnamecache.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" #include "llselectmgr.h" @@ -212,8 +212,12 @@ void LLFloaterInspect::refresh() substitution["datetime"] = (S32) timestamp; LLStringUtil::format (timeStr, substitution); - gCacheName->getFullName(obj->mPermissions->getOwner(), owner_name); - gCacheName->getFullName(obj->mPermissions->getCreator(), creator_name); + LLAvatarName av_name; + LLAvatarNameCache::get(obj->mPermissions->getOwner(), &av_name); + owner_name = av_name.getCompleteName(); + LLAvatarNameCache::get(obj->mPermissions->getCreator(), &av_name); + creator_name = av_name.getCompleteName(); + row["id"] = obj->getObject()->getID(); row["columns"][0]["column"] = "object_name"; row["columns"][0]["type"] = "text"; diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 7c5586ec96..9ba8306a24 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -37,7 +37,7 @@ #include "llfloaterland.h" -#include "llcachename.h" +#include "llavatarnamecache.h" #include "llfocusmgr.h" #include "llnotificationsutil.h" #include "llparcel.h" @@ -1390,10 +1390,7 @@ bool LLPanelLandObjects::callbackReturnOwnerObjects(const LLSD& notification, co } else { - std::string first, last; - gCacheName->getName(owner_id, first, last); - args["FIRST"] = first; - args["LAST"] = last; + args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString(); LLNotificationsUtil::add("OtherObjectsReturned", args); } send_return_objects_message(parcel->getLocalID(), RT_OWNER); @@ -1611,9 +1608,9 @@ void LLPanelLandObjects::processParcelObjectOwnersReply(LLMessageSystem *msg, vo } // Placeholder for name. - std::string name; - gCacheName->getFullName(owner_id, name); - item_params.columns.add().value(name).font(FONT).column("name"); + LLAvatarName av_name; + LLAvatarNameCache::get(owner_id, &av_name); + item_params.columns.add().value(av_name.getCompleteName()).font(FONT).column("name"); object_count_str = llformat("%d", object_count); item_params.columns.add().value(object_count_str).font(FONT).column("count"); @@ -1722,9 +1719,7 @@ void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata) } else { - std::string name; - gCacheName->getFullName(owner_id, name); - args["NAME"] = name; + args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString(); LLNotificationsUtil::add("ReturnObjectsOwnedByUser", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOwnerObjects, panelp, _1, _2)); } } @@ -1783,10 +1778,7 @@ void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata) } else { - std::string name; - gCacheName->getFullName(owner_id, name); - args["NAME"] = name; - + args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString(); LLNotificationsUtil::add("ReturnObjectsNotOwnedByUser", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOtherObjects, panelp, _1, _2)); } } @@ -2778,12 +2770,12 @@ void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata) void LLPanelLandAccess::onClickAddAccess() { - gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1,_2)) ); + gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1)) ); } -void LLPanelLandAccess::callbackAvatarCBAccess(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelLandAccess::callbackAvatarCBAccess(const uuid_vec_t& ids) { - if (!names.empty() && !ids.empty()) + if (!ids.empty()) { LLUUID id = ids[0]; LLParcel* parcel = mParcel->getParcel(); @@ -2822,13 +2814,13 @@ void LLPanelLandAccess::onClickRemoveAccess(void* data) // static void LLPanelLandAccess::onClickAddBanned() { - gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1,_2))); + gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1))); } // static -void LLPanelLandAccess::callbackAvatarCBBanned(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelLandAccess::callbackAvatarCBBanned(const uuid_vec_t& ids) { - if (!names.empty() && !ids.empty()) + if (!ids.empty()) { LLUUID id = ids[0]; LLParcel* parcel = mParcel->getParcel(); diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index 0a743e5215..e742d04aa0 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -379,8 +379,8 @@ public: void onClickAddAccess(); void onClickAddBanned(); - void callbackAvatarCBBanned(const std::vector<std::string>& names, const uuid_vec_t& ids); - void callbackAvatarCBAccess(const std::vector<std::string>& names, const uuid_vec_t& ids); + void callbackAvatarCBBanned(const uuid_vec_t& ids); + void callbackAvatarCBAccess(const uuid_vec_t& ids); protected: LLNameListCtrl* mListAccess; diff --git a/indra/newview/llfloaterpay.cpp b/indra/newview/llfloaterpay.cpp index 51364594e4..6b811b5886 100644 --- a/indra/newview/llfloaterpay.cpp +++ b/indra/newview/llfloaterpay.cpp @@ -47,6 +47,7 @@ #include "lllineeditor.h" #include "llmutelist.h" #include "llfloaterreporter.h" +#include "llslurl.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" @@ -102,10 +103,6 @@ private: static void onGive(void* data); void give(S32 amount); static void processPayPriceReply(LLMessageSystem* msg, void **userdata); - void onCacheOwnerName(const LLUUID& owner_id, - const std::string& firstname, - const std::string& lastname, - BOOL is_group); void finishPayUI(const LLUUID& target_id, BOOL is_group); protected: @@ -427,33 +424,26 @@ void LLFloaterPay::payDirectly(money_callback callback, void LLFloaterPay::finishPayUI(const LLUUID& target_id, BOOL is_group) { - gCacheName->get(target_id, is_group, boost::bind(&LLFloaterPay::onCacheOwnerName, this, _1, _2, _3, _4)); - - // Make sure the amount field has focus - - childSetFocus("amount", TRUE); - - LLLineEditor* amount = getChild<LLLineEditor>("amount"); - amount->selectAll(); - mTargetIsGroup = is_group; -} - -void LLFloaterPay::onCacheOwnerName(const LLUUID& owner_id, - const std::string& firstname, - const std::string& lastname, - BOOL is_group) -{ + std::string slurl; if (is_group) { setTitle(getString("payee_group")); + slurl = LLSLURL("group", target_id, "inspect").getSLURLString(); } else { setTitle(getString("payee_resident")); + slurl = LLSLURL("agent", target_id, "inspect").getSLURLString(); } + childSetText("payee_name", slurl); - childSetTextArg("payee_name", "[FIRST]", firstname); - childSetTextArg("payee_name", "[LAST]", lastname); + // Make sure the amount field has focus + + childSetFocus("amount", TRUE); + + LLLineEditor* amount = getChild<LLLineEditor>("amount"); + amount->selectAll(); + mTargetIsGroup = is_group; } // static diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 593d5c267e..6087312a68 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -81,6 +81,7 @@ #include "llvosky.h" // linden library includes +#include "llavatarnamecache.h" #include "llerror.h" #include "llfontgl.h" #include "llrect.h" @@ -184,6 +185,8 @@ void LLVoiceSetKeyDialog::onCancel(void* user_data) // if creating/destroying these is too slow, we'll need to create // a static member and update all our static callbacks +void handleNameTagOptionChanged(const LLSD& newvalue); +void handleDisplayNamesOptionChanged(const LLSD& newvalue); bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); //bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); @@ -219,6 +222,18 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response return false; } +void handleNameTagOptionChanged(const LLSD& newvalue) +{ + LLVOAvatar::invalidateNameTags(); +} + +void handleDisplayNamesOptionChanged(const LLSD& newvalue) +{ + LLAvatarNameCache::setUseDisplayNames(newvalue.asBoolean()); + LLVOAvatar::invalidateNameTags(); +} + + /*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -311,6 +326,10 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.MaturitySettings", boost::bind(&LLFloaterPreference::onChangeMaturity, this)); sSkin = gSavedSettings.getString("SkinCurrent"); + + gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); + gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); + gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2)); } BOOL LLFloaterPreference::postBuild() diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 8c219cb3fd..590e14a362 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -49,6 +49,7 @@ #include "llagent.h" #include "llappviewer.h" +#include "llavatarname.h" #include "llfloateravatarpicker.h" #include "llbutton.h" #include "llcheckboxctrl.h" @@ -606,13 +607,13 @@ void LLPanelRegionGeneralInfo::onClickKick() // this depends on the grandparent view being a floater // in order to set up floater dependency LLFloater* parent_floater = gFloaterView->getParentFloater(this); - LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionGeneralInfo::onKickCommit, this, _1,_2), FALSE, TRUE); + LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionGeneralInfo::onKickCommit, this, _1), FALSE, TRUE); parent_floater->addDependentFloater(child_floater); } -void LLPanelRegionGeneralInfo::onKickCommit(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelRegionGeneralInfo::onKickCommit(const uuid_vec_t& ids) { - if (names.empty() || ids.empty()) return; + if (ids.empty()) return; if(ids[0].notNull()) { strings_t strings; @@ -848,11 +849,11 @@ void LLPanelRegionDebugInfo::onClickChooseAvatar() } -void LLPanelRegionDebugInfo::callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelRegionDebugInfo::callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (ids.empty() || names.empty()) return; mTargetAvatar = ids[0]; - childSetValue("target_avatar_name", LLSD(names[0])); + childSetValue("target_avatar_name", names[0].getCompleteName()); refreshFromRegion( gAgent.getRegion() ); } @@ -1527,24 +1528,17 @@ void LLPanelEstateInfo::onClickKickUser() // this depends on the grandparent view being a floater // in order to set up floater dependency LLFloater* parent_floater = gFloaterView->getParentFloater(this); - LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::onKickUserCommit, this, _1, _2), FALSE, TRUE); + LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::onKickUserCommit, this, _1), FALSE, TRUE); parent_floater->addDependentFloater(child_floater); } -void LLPanelEstateInfo::onKickUserCommit(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelEstateInfo::onKickUserCommit(const uuid_vec_t& ids) { - if (names.empty() || ids.empty()) return; + if (ids.empty()) return; - //check to make sure there is one valid user and id - if( (ids[0].isNull()) || - (names[0].length() == 0) ) - { - return; - } - //Bring up a confirmation dialog LLSD args; - args["EVIL_USER"] = names[0]; + args["EVIL_USER"] = LLSLURL("agent", ids[0], "completename").getSLURLString(); LLSD payload; payload["agent_id"] = ids[0]; LLNotificationsUtil::add("EstateKickUser", args, payload, boost::bind(&LLPanelEstateInfo::kickUserConfirm, this, _1, _2)); @@ -1710,12 +1704,12 @@ bool LLPanelEstateInfo::accessAddCore2(const LLSD& notification, const LLSD& res LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo(notification["payload"]); // avatar picker yes multi-select, yes close-on-select - LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::accessAddCore3, _1, _2, (void*)change_info), TRUE, TRUE); + LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::accessAddCore3, _1, (void*)change_info), TRUE, TRUE); return false; } // static -void LLPanelEstateInfo::accessAddCore3(const std::vector<std::string>& names, const uuid_vec_t& ids, void* data) +void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data) { LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data; if (!change_info) return; @@ -1971,8 +1965,15 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) childSetEnabled("remove_allowed_avatar_btn", god || owner || manager); childSetEnabled("add_allowed_group_btn", god || owner || manager); childSetEnabled("remove_allowed_group_btn", god || owner || manager); - childSetEnabled("add_banned_avatar_btn", god || owner || manager); - childSetEnabled("remove_banned_avatar_btn", god || owner || manager); + + // Can't ban people from mainland, orientation islands, etc. because this + // creates much network traffic and server load. + // Disable their accounts in CSR tool instead. + bool linden_estate = (getEstateID() <= ESTATE_LAST_LINDEN); + bool enable_ban = (god || owner || manager) && !linden_estate; + childSetEnabled("add_banned_avatar_btn", enable_ban); + childSetEnabled("remove_banned_avatar_btn", enable_ban); + childSetEnabled("message_estate_btn", god || owner || manager); childSetEnabled("kick_user_from_estate_btn", god || owner || manager); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 482ebb3303..13c2e46639 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -40,6 +40,7 @@ #include "llhost.h" #include "llpanel.h" +class LLAvatarName; class LLDispatcher; class LLLineEditor; class LLMessageSystem; @@ -168,7 +169,7 @@ public: protected: virtual BOOL sendUpdate(); void onClickKick(); - void onKickCommit(const std::vector<std::string>& names, const uuid_vec_t& ids); + void onKickCommit(const uuid_vec_t& ids); static void onClickKickAll(void* userdata); bool onKickAllCommit(const LLSD& notification, const LLSD& response); static void onClickMessage(void* userdata); @@ -193,7 +194,7 @@ protected: virtual BOOL sendUpdate(); void onClickChooseAvatar(); - void callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids); + void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); static void onClickReturn(void *); bool callbackReturn(const LLSD& notification, const LLSD& response); static void onClickTopColliders(void*); @@ -284,7 +285,7 @@ public: // Core methods for all above add/remove button clicks static void accessAddCore(U32 operation_flag, const std::string& dialog_name); static bool accessAddCore2(const LLSD& notification, const LLSD& response); - static void accessAddCore3(const std::vector<std::string>& names, const uuid_vec_t& ids, void* data); + static void accessAddCore3(const uuid_vec_t& ids, void* data); static void accessRemoveCore(U32 operation_flag, const std::string& dialog_name, const std::string& list_ctrl_name); static bool accessRemoveCore2(const LLSD& notification, const LLSD& response); @@ -296,7 +297,7 @@ public: // Send the actual EstateOwnerRequest "estateaccessdelta" message static void sendEstateAccessDelta(U32 flags, const LLUUID& agent_id); - void onKickUserCommit(const std::vector<std::string>& names, const uuid_vec_t& ids); + void onKickUserCommit(const uuid_vec_t& ids); static void onClickMessageEstate(void* data); bool onMessageCommit(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index f7c8855bf6..195b3e22d9 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -39,6 +39,8 @@ // linden library includes #include "llassetstorage.h" +#include "llavatarnamecache.h" +#include "llcachename.h" #include "llfontgl.h" #include "llimagej2c.h" #include "llinventory.h" @@ -182,9 +184,8 @@ BOOL LLFloaterReporter::postBuild() childSetAction("cancel_btn", onClickCancel, this); // grab the user's name - std::string fullname; - LLAgentUI::buildFullname(fullname); - childSetText("reporter_field", fullname); + std::string reporter = LLSLURL("agent", gAgent.getID(), "inspect").getSLURLString(); + childSetText("reporter_field", reporter); center(); @@ -267,22 +268,7 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) if (objectp->isAvatar()) { - // we have the information we need - std::string object_owner; - - LLNameValue* firstname = objectp->getNVPair("FirstName"); - LLNameValue* lastname = objectp->getNVPair("LastName"); - if (firstname && lastname) - { - object_owner.append(firstname->getString()); - object_owner.append(1, ' '); - object_owner.append(lastname->getString()); - } - else - { - object_owner.append("Unknown"); - } - setFromAvatar(mObjectID, object_owner); + setFromAvatarID(mObjectID); } else { @@ -309,11 +295,11 @@ void LLFloaterReporter::onClickSelectAbuser() gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE )); } -void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLFloaterReporter::callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (ids.empty() || names.empty()) return; - childSetText("abuser_name_edit", names[0] ); + childSetText("abuser_name_edit", names[0].getCompleteName()); mAbuserID = ids[0]; @@ -321,18 +307,27 @@ void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, } -void LLFloaterReporter::setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name) +void LLFloaterReporter::setFromAvatarID(const LLUUID& avatar_id) { mAbuserID = mObjectID = avatar_id; - mOwnerName = avatar_name; - - std::string avatar_link = - LLSLURL("agent", mObjectID, "inspect").getSLURLString(); + std::string avatar_link = LLSLURL("agent", mObjectID, "inspect").getSLURLString(); childSetText("owner_name", avatar_link); - childSetText("object_name", avatar_name); - childSetText("abuser_name_edit", avatar_name); + + LLAvatarNameCache::get(avatar_id, boost::bind(&LLFloaterReporter::onAvatarNameCache, this, _1, _2)); } +void LLFloaterReporter::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name) +{ + if (mObjectID == avatar_id) + { + mOwnerName = av_name.getCompleteName(); + childSetText("object_name", av_name.getCompleteName()); + childSetToolTip("object_name", av_name.getCompleteName()); + childSetText("abuser_name_edit", av_name.getCompleteName()); + } +} + + // static void LLFloaterReporter::onClickSend(void *userdata) { @@ -472,16 +467,11 @@ void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_ { LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter"); - // grab the user's name - std::string fullname; - LLAgentUI::buildFullname(fullname); - f->childSetText("reporter_field", fullname); - if (avatar_name.empty()) // Request info for this object f->getObjectInfo(object_id); else - f->setFromAvatar(object_id, avatar_name); + f->setFromAvatarID(object_id); // Need to deselect on close f->mDeselectOnClose = TRUE; diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index 23784b7650..1f8526b1de 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -38,6 +38,7 @@ #include "lluuid.h" #include "v3math.h" +class LLAvatarName; class LLMessageSystem; class LLViewerTexture; class LLInventoryItem; @@ -123,8 +124,9 @@ private: void setPosBox(const LLVector3d &pos); void enableControls(BOOL own_avatar); void getObjectInfo(const LLUUID& object_id); - void callbackAvatarID(const std::vector<std::string>& names, const uuid_vec_t& ids); - void setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name = LLStringUtil::null); + void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); + void setFromAvatarID(const LLUUID& avatar_id); + void onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name); private: EReportType mReportType; diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 4792d761d8..f8a5e57389 100644 --- a/indra/newview/llfloaterscriptlimits.cpp +++ b/indra/newview/llfloaterscriptlimits.cpp @@ -34,6 +34,8 @@ #include "llviewerprecompiledheaders.h" #include "llfloaterscriptlimits.h" +// library includes +#include "llavatarnamecache.h" #include "llsdutil.h" #include "llsdutil_math.h" #include "message.h" @@ -295,7 +297,7 @@ void fetchScriptLimitsRegionSummaryResponder::result(const LLSD& content_ref) LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); if(tab) { - LLPanelScriptLimitsRegionMemory* panel_memory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); + LLPanelScriptLimitsRegionMemory* panel_memory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); if(panel_memory) { panel_memory->childSetValue("loading_text", LLSD(std::string(""))); @@ -306,9 +308,9 @@ void fetchScriptLimitsRegionSummaryResponder::result(const LLSD& content_ref) btn->setEnabled(true); } - panel_memory->setRegionSummary(content); - } - } + panel_memory->setRegionSummary(content); + } +} } } @@ -599,17 +601,24 @@ void LLPanelScriptLimitsRegionMemory::setErrorStatus(U32 status, const std::stri // callback from the name cache with an owner name to add to the list void LLPanelScriptLimitsRegionMemory::onNameCache( const LLUUID& id, - const std::string& first_name, - const std::string& last_name) + const std::string& full_name) { - std::string name = first_name + " " + last_name; - LLScrollListCtrl *list = getChild<LLScrollListCtrl>("scripts_list"); if(!list) { return; } + std::string name; + if (LLAvatarNameCache::useDisplayNames()) + { + name = LLCacheName::buildUsername(full_name); + } + else + { + name = full_name; + } + std::vector<LLSD>::iterator id_itor; for (id_itor = mObjectListItems.begin(); id_itor != mObjectListItems.end(); ++id_itor) { @@ -675,6 +684,9 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content) std::string name_buf = content["parcels"][i]["objects"][j]["name"].asString(); LLUUID task_id = content["parcels"][i]["objects"][j]["id"].asUUID(); LLUUID owner_id = content["parcels"][i]["objects"][j]["owner_id"].asUUID(); + // This field may not be sent by all server versions, but it's OK if + // it uses the LLSD default of false + bool is_group_owned = content["parcels"][i]["objects"][j]["is_group_owned"].asBoolean(); F32 location_x = 0.0f; F32 location_y = 0.0f; @@ -700,15 +712,27 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content) // ...and if not use the slightly more painful method of disovery: else { - BOOL name_is_cached = gCacheName->getFullName(owner_id, owner_buf); + BOOL name_is_cached; + if (is_group_owned) + { + name_is_cached = gCacheName->getGroupName(owner_id, owner_buf); + } + else + { + name_is_cached = gCacheName->getFullName(owner_id, owner_buf); // username + if (LLAvatarNameCache::useDisplayNames()) + { + owner_buf = LLCacheName::buildUsername(owner_buf); + } + } if(!name_is_cached) { if(std::find(names_requested.begin(), names_requested.end(), owner_id) == names_requested.end()) { names_requested.push_back(owner_id); - gCacheName->get(owner_id, TRUE, - boost::bind(&LLPanelScriptLimitsRegionMemory::onNameCache, - this, _1, _2, _3)); + gCacheName->get(owner_id, is_group_owned, // username + boost::bind(&LLPanelScriptLimitsRegionMemory::onNameCache, + this, _1, _2)); } } } @@ -1310,7 +1334,7 @@ void LLPanelScriptLimitsAttachment::setAttachmentSummary(LLSD content) // static void LLPanelScriptLimitsAttachment::onClickRefresh(void* userdata) -{ +{ LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); if(instance) { diff --git a/indra/newview/llfloaterscriptlimits.h b/indra/newview/llfloaterscriptlimits.h index 3c32b9f701..ba6f930bee 100644 --- a/indra/newview/llfloaterscriptlimits.h +++ b/indra/newview/llfloaterscriptlimits.h @@ -170,10 +170,8 @@ public: void returnObjects(); private: - void onNameCache(const LLUUID& id, - const std::string& first_name, - const std::string& last_name); + const std::string& name); LLSD mContent; LLUUID mParcelId; diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp index 9dddbd998a..186debe40e 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -33,6 +33,7 @@ #include "llfloatersellland.h" +#include "llavatarnamecache.h" #include "llfloateravatarpicker.h" #include "llfloaterreg.h" #include "llfloaterland.h" @@ -47,6 +48,8 @@ #include "lluictrlfactory.h" #include "llviewerwindow.h" +class LLAvatarName; + // defined in llfloaterland.cpp void send_parcel_select_objects(S32 parcel_local_id, U32 return_type, uuid_list_t* return_ids = NULL); @@ -95,7 +98,9 @@ private: bool onConfirmSale(const LLSD& notification, const LLSD& response); static void doShowObjects(void *userdata); - void callbackAvatarPick(const std::vector<std::string>& names, const uuid_vec_t& ids); + void callbackAvatarPick(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); + + void onBuyerNameCache(const LLAvatarName& av_name); public: virtual BOOL postBuild(); @@ -230,12 +235,17 @@ void LLFloaterSellLandUI::updateParcelInfo() if(mSellToBuyer) { - std::string name; - gCacheName->getFullName(mAuthorizedBuyer, name); - childSetText("sell_to_agent", name); + LLAvatarNameCache::get(mAuthorizedBuyer, + boost::bind(&LLFloaterSellLandUI::onBuyerNameCache, this, _2)); } } +void LLFloaterSellLandUI::onBuyerNameCache(const LLAvatarName& av_name) +{ + childSetText("sell_to_agent", av_name.getCompleteName()); + childSetToolTip("sell_to_agent", av_name.mUsername); +} + void LLFloaterSellLandUI::setBadge(const char* id, Badge badge) { static std::string badgeOK("badge_ok.j2c"); @@ -391,7 +401,7 @@ void LLFloaterSellLandUI::doSelectAgent() addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterSellLandUI::callbackAvatarPick, this, _1, _2), FALSE, TRUE)); } -void LLFloaterSellLandUI::callbackAvatarPick(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLFloaterSellLandUI::callbackAvatarPick(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { LLParcel* parcel = mParcelSelection->getParcel(); @@ -402,7 +412,7 @@ void LLFloaterSellLandUI::callbackAvatarPick(const std::vector<std::string>& nam mAuthorizedBuyer = ids[0]; - childSetText("sell_to_agent", names[0]); + childSetText("sell_to_agent", names[0].getCompleteName()); refreshUI(); } diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index 84ea353dab..c7c3c2f7da 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -34,7 +34,9 @@ #include "llfloatertopobjects.h" +// library includes #include "message.h" +#include "llavatarnamecache.h" #include "llfontgl.h" #include "llagent.h" @@ -196,37 +198,53 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) LLSD element; element["id"] = task_id; - element["object_name"] = name_buf; - element["owner_name"] = owner_buf; - element["columns"][0]["column"] = "score"; - element["columns"][0]["value"] = llformat("%0.3f", score); - element["columns"][0]["font"] = "SANSSERIF"; + + LLSD columns; + columns[0]["column"] = "score"; + columns[0]["value"] = llformat("%0.3f", score); + columns[0]["font"] = "SANSSERIF"; + + columns[1]["column"] = "name"; + columns[1]["value"] = name_buf; + columns[1]["font"] = "SANSSERIF"; + + // Owner names can have trailing spaces sent from server + LLStringUtil::trim(owner_buf); - element["columns"][1]["column"] = "name"; - element["columns"][1]["value"] = name_buf; - element["columns"][1]["font"] = "SANSSERIF"; - element["columns"][2]["column"] = "owner"; - element["columns"][2]["value"] = owner_buf; - element["columns"][2]["font"] = "SANSSERIF"; - element["columns"][3]["column"] = "location"; - element["columns"][3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); - element["columns"][3]["font"] = "SANSSERIF"; - element["columns"][4]["column"] = "time"; - element["columns"][4]["value"] = formatted_time((time_t)time_stamp); - element["columns"][4]["font"] = "SANSSERIF"; + if (LLAvatarNameCache::useDisplayNames()) + { + // ...convert hard-coded name from server to a username + // *TODO: Send owner_id from server and look up display name + owner_buf = LLCacheName::buildUsername(owner_buf); + } + else + { + // ...just strip out legacy "Resident" name + owner_buf = LLCacheName::cleanFullName(owner_buf); + } + columns[2]["column"] = "owner"; + columns[2]["value"] = owner_buf; + columns[2]["font"] = "SANSSERIF"; + + columns[3]["column"] = "location"; + columns[3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); + columns[3]["font"] = "SANSSERIF"; + columns[4]["column"] = "time"; + columns[4]["value"] = formatted_time((time_t)time_stamp); + columns[4]["font"] = "SANSSERIF"; if (mCurrentMode == STAT_REPORT_TOP_SCRIPTS && have_extended_data) { - element["columns"][5]["column"] = "mono_time"; - element["columns"][5]["value"] = llformat("%0.3f", mono_score); - element["columns"][5]["font"] = "SANSSERIF"; + columns[5]["column"] = "mono_time"; + columns[5]["value"] = llformat("%0.3f", mono_score); + columns[5]["font"] = "SANSSERIF"; - element["columns"][6]["column"] = "URLs"; - element["columns"][6]["value"] = llformat("%d", public_urls); - element["columns"][6]["font"] = "SANSSERIF"; + columns[6]["column"] = "URLs"; + columns[6]["value"] = llformat("%d", public_urls); + columns[6]["font"] = "SANSSERIF"; } - + element["columns"] = columns; list->addElement(element); mObjectListData.append(element); diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 0dfdbd364b..8efd645521 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -120,7 +120,7 @@ public: const std::string getFilterSubString(BOOL trim = FALSE); U32 getFilterObjectTypes() const; PermissionMask getFilterPermissions() const; - // JAMESDEBUG use getFilter()->getShowFolderState(); + // *NOTE: use getFilter()->getShowFolderState(); //LLInventoryFilter::EFolderShow getShowFolderState(); U32 getSortOrder() const; BOOL isFilterModified(); diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index a2ef8c1d12..0e2185bde5 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -31,7 +31,7 @@ #ifndef LLFOLDERVIEWEVENTLISTENER_H #define LLFOLDERVIEWEVENTLISTENER_H -#include "lldarray.h" // JAMESDEBUG convert to std::vector +#include "lldarray.h" // *TODO: convert to std::vector #include "llfoldertype.h" #include "llfontgl.h" // just for StyleFlags enum #include "llinventorytype.h" diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 57c722afa4..894b9df967 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -57,7 +57,7 @@ enum EInventorySortGroup SG_ITEM }; -// JAMESDEBUG *TODO: do we really need one sort object per folder? +// *TODO: do we really need one sort object per folder? // can we just have one of these per LLFolderView ? class LLInventorySort { diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 7f28e09933..0a1b0b5df0 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -32,13 +32,14 @@ #include "llviewerprecompiledheaders.h" +#include "llfriendcard.h" + +#include "llavatarnamecache.h" #include "llinventory.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "lltrans.h" -#include "llfriendcard.h" - #include "llcallingcard.h" // for LLAvatarTracker #include "llviewerinventory.h" #include "llinventorymodel.h" @@ -536,8 +537,9 @@ void LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID) { bool shouldBeAdded = true; - std::string name; - gCacheName->getFullName(avatarID, name); + LLAvatarName av_name; + LLAvatarNameCache::get(avatarID, &av_name); + const std::string& name = av_name.mUsername; lldebugs << "Processing buddy name: " << name << ", id: " << avatarID diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 2aba0b5c09..eec08301a8 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -54,6 +54,7 @@ #include "lltoolmgr.h" #include "llselectmgr.h" #include "llhudmanager.h" +#include "llhudtext.h" #include "llrendersphere.h" #include "llviewerobjectlist.h" #include "lltoolselectrect.h" @@ -894,7 +895,7 @@ void LLViewerObjectList::renderObjectBeacons() color = debug_beacon.mTextColor; color.mV[3] *= 1.f; - hud_textp->setString(utf8str_to_wstring(debug_beacon.mString)); + hud_textp->setString(debug_beacon.mString); hud_textp->setColor(color); hud_textp->setPositionAgent(debug_beacon.mPositionAgent); debug_beacon.mHUDObject = hud_textp; diff --git a/indra/newview/llhudicon.h b/indra/newview/llhudicon.h index 770e3bbcd0..91b3a64250 100644 --- a/indra/newview/llhudicon.h +++ b/indra/newview/llhudicon.h @@ -50,6 +50,7 @@ // Renders a 2D icon billboard floating at the location specified. class LLDrawable; class LLViewerObject; +class LLViewerTexture; class LLHUDIcon : public LLHUDObject { diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp new file mode 100644 index 0000000000..303f048dfc --- /dev/null +++ b/indra/newview/llhudnametag.cpp @@ -0,0 +1,1072 @@ +/** + * @file llhudnametag.cpp + * @brief Name tags for avatars + * @author James Cook + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llhudnametag.h" + +#include "llrender.h" + +#include "llagent.h" +#include "llviewercontrol.h" +#include "llcriticaldamp.h" +#include "lldrawable.h" +#include "llfontgl.h" +#include "llglheaders.h" +#include "llhudrender.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewertexturelist.h" +#include "llviewerobject.h" +#include "llvovolume.h" +#include "llviewerwindow.h" +#include "llstatusbar.h" +#include "llmenugl.h" +#include "pipeline.h" +#include <boost/tokenizer.hpp> + + +const F32 SPRING_STRENGTH = 0.7f; +const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f; +const F32 HORIZONTAL_PADDING = 16.f; +const F32 VERTICAL_PADDING = 12.f; +const F32 LINE_PADDING = 3.f; // aka "leading" +const F32 BUFFER_SIZE = 2.f; +const F32 MIN_EDGE_OVERLAP = 3.f; +const F32 HUD_TEXT_MAX_WIDTH = 190.f; +const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f; +const F32 RESIZE_TIME = 0.f; +const S32 NUM_OVERLAP_ITERATIONS = 10; +const F32 NEIGHBOR_FORCE_FRACTION = 1.f; +const F32 POSITION_DAMPING_TC = 0.2f; +const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f; +const F32 LOD_0_SCREEN_COVERAGE = 0.15f; +const F32 LOD_1_SCREEN_COVERAGE = 0.30f; +const F32 LOD_2_SCREEN_COVERAGE = 0.40f; + +std::set<LLPointer<LLHUDNameTag> > LLHUDNameTag::sTextObjects; +std::vector<LLPointer<LLHUDNameTag> > LLHUDNameTag::sVisibleTextObjects; +BOOL LLHUDNameTag::sDisplayText = TRUE ; + +bool llhudnametag_further_away::operator()(const LLPointer<LLHUDNameTag>& lhs, const LLPointer<LLHUDNameTag>& rhs) const +{ + return lhs->getDistance() > rhs->getDistance(); +} + + +LLHUDNameTag::LLHUDNameTag(const U8 type) +: LLHUDObject(type), + mDoFade(TRUE), + mFadeDistance(8.f), + mFadeRange(4.f), + mLastDistance(0.f), + mZCompare(TRUE), + mVisibleOffScreen(FALSE), + mOffscreen(FALSE), + mColor(1.f, 1.f, 1.f, 1.f), +// mScale(), + mWidth(0.f), + mHeight(0.f), + mFontp(LLFontGL::getFontSansSerifSmall()), + mBoldFontp(LLFontGL::getFontSansSerifBold()), + mSoftScreenRect(), + mPositionAgent(), + mPositionOffset(), + mMass(10.f), + mMaxLines(10), + mOffsetY(0), + mRadius(0.1f), + mTextSegments(), + mLabelSegments(), + mTextAlignment(ALIGN_TEXT_CENTER), + mVertAlignment(ALIGN_VERT_CENTER), + mLOD(0), + mHidden(FALSE) +{ + LLPointer<LLHUDNameTag> ptr(this); + sTextObjects.insert(ptr); +} + +LLHUDNameTag::~LLHUDNameTag() +{ +} + + +BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render) +{ + if (!mVisible || mHidden) + { + return FALSE; + } + + // don't pick text that isn't bound to a viewerobject + if (!mSourceObject || mSourceObject->mDrawable.isNull()) + { + return FALSE; + } + + F32 alpha_factor = 1.f; + LLColor4 text_color = mColor; + if (mDoFade) + { + if (mLastDistance > mFadeDistance) + { + alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange); + text_color.mV[3] = text_color.mV[3]*alpha_factor; + } + } + if (text_color.mV[3] < 0.01f) + { + return FALSE; + } + + mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f)); + + // scale screen size of borders down + //RN: for now, text on hud objects is never occluded + + LLVector3 x_pixel_vec; + LLVector3 y_pixel_vec; + + LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); + + LLVector3 width_vec = mWidth * x_pixel_vec; + LLVector3 height_vec = mHeight * y_pixel_vec; + + LLCoordGL screen_pos; + LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); + + LLVector2 screen_offset; + screen_offset = updateScreenPos(mPositionOffset); + + LLVector3 render_position = mPositionAgent + + (x_pixel_vec * screen_offset.mV[VX]) + + (y_pixel_vec * screen_offset.mV[VY]); + + + //if (mUseBubble) + { + LLVector3 bg_pos = render_position + + (F32)mOffsetY * y_pixel_vec + - (width_vec / 2.f) + - (height_vec); + //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); + + LLVector3 v[] = + { + bg_pos, + bg_pos + width_vec, + bg_pos + width_vec + height_vec, + bg_pos + height_vec, + }; + + if (debug_render) + { + gGL.begin(LLRender::LINE_STRIP); + gGL.vertex3fv(v[0].mV); + gGL.vertex3fv(v[1].mV); + gGL.vertex3fv(v[2].mV); + gGL.vertex3fv(v[3].mV); + gGL.vertex3fv(v[0].mV); + gGL.vertex3fv(v[2].mV); + gGL.end(); + } + + LLVector3 dir = end-start; + F32 t = 0.f; + + if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, NULL, NULL, &t, FALSE) || + LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, NULL, NULL, &t, FALSE) ) + { + if (t <= 1.f) + { + intersection = start + dir*t; + return TRUE; + } + } + } + + return FALSE; +} + +void LLHUDNameTag::render() +{ + if (sDisplayText) + { + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + renderText(FALSE); + } +} + +void LLHUDNameTag::renderForSelect() +{ + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + renderText(TRUE); +} + +void LLHUDNameTag::renderText(BOOL for_select) +{ + if (!mVisible || mHidden) + { + return; + } + + // don't pick text that isn't bound to a viewerobject + if (for_select && + (!mSourceObject || mSourceObject->mDrawable.isNull())) + { + return; + } + + if (for_select) + { + gGL.getTexUnit(0)->disable(); + } + else + { + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + } + + LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE); + LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE); + + LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f); + F32 alpha_factor = 1.f; + LLColor4 text_color = mColor; + if (mDoFade) + { + if (mLastDistance > mFadeDistance) + { + alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange); + text_color.mV[3] = text_color.mV[3]*alpha_factor; + } + } + if (text_color.mV[3] < 0.01f) + { + return; + } + shadow_color.mV[3] = text_color.mV[3]; + + mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f)); + + // *TODO: cache this image + LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Rect"); + + // *TODO: make this a per-text setting + LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground"); + bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); + + // maybe a no-op? + //const S32 border_height = 16; + //const S32 border_width = 16; + const S32 border_height = 8; + const S32 border_width = 8; + + // *TODO move this into helper function + F32 border_scale = 1.f; + + if (border_height * 2 > mHeight) + { + border_scale = (F32)mHeight / ((F32)border_height * 2.f); + } + if (border_width * 2 > mWidth) + { + border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f)); + } + + // scale screen size of borders down + //RN: for now, text on hud objects is never occluded + + LLVector3 x_pixel_vec; + LLVector3 y_pixel_vec; + + LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); + + LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight()); + LLVector3 width_vec = mWidth * x_pixel_vec; + LLVector3 height_vec = mHeight * y_pixel_vec; + LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec; + LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec; + + mRadius = (width_vec + height_vec).magVec() * 0.5f; + + LLCoordGL screen_pos; + LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); + + LLVector2 screen_offset; +// if (!mUseBubble) +// { +// screen_offset = mPositionOffset; +// } +// else +// { + screen_offset = updateScreenPos(mPositionOffset); +// } + + LLVector3 render_position = mPositionAgent + + (x_pixel_vec * screen_offset.mV[VX]) + + (y_pixel_vec * screen_offset.mV[VY]); + +// if (mUseBubble) + { + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + LLUI::pushMatrix(); + { + LLVector3 bg_pos = render_position + + (F32)mOffsetY * y_pixel_vec + - (width_vec / 2.f) + - (height_vec); + LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); + + if (for_select) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + S32 name = mSourceObject->mGLName; + LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name); + gGL.color4ubv(coloru.mV); + gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); + LLUI::popMatrix(); + return; + } + else + { + gGL.getTexUnit(0)->bind(imagep->getImage()); + + gGL.color4fv(bg_color.mV); + gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); + + if ( mLabelSegments.size()) + { + LLUI::pushMatrix(); + { + gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); + LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec; + LLVector3 label_offset = height_vec - label_height; + LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]); + gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height); + } + LLUI::popMatrix(); + } + } + + BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f; + BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f); + + // draw line segments pointing to parent object + if (!mOffscreen && (outside_width || outside_height)) + { + LLUI::pushMatrix(); + { + gGL.color4fv(bg_color.mV); + LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec); + target_pos += (width_vec / 2.f); + target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero; + target_pos -= 3.f * x_pixel_vec; + target_pos -= 6.f * y_pixel_vec; + LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]); + gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec); + } + LLUI::popMatrix(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE); + + LLVector3 box_center_offset; + box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f); + LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]); + gGL.color4fv(bg_color.mV); + LLUI::setLineWidth(2.0); + gGL.begin(LLRender::LINES); + { + if (outside_width) + { + LLVector3 vert; + // draw line in x then y + if (mPositionOffset.mV[VX] < 0.f) + { + // start at right edge + vert = width_vec * 0.5f; + gGL.vertex3fv(vert.mV); + } + else + { + // start at left edge + vert = width_vec * -0.5f; + gGL.vertex3fv(vert.mV); + } + vert = -mPositionOffset.mV[VX] * x_pixel_vec; + gGL.vertex3fv(vert.mV); + gGL.vertex3fv(vert.mV); + vert -= mPositionOffset.mV[VY] * y_pixel_vec; + vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); + gGL.vertex3fv(vert.mV); + } + else + { + LLVector3 vert; + // draw line in y then x + if (mPositionOffset.mV[VY] < 0.f) + { + // start at top edge + vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); + gGL.vertex3fv(vert.mV); + } + else + { + // start at bottom edge + vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); + gGL.vertex3fv(vert.mV); + } + vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec; + vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); + gGL.vertex3fv(vert.mV); + } + } + gGL.end(); + LLUI::setLineWidth(1.0); + + } + } + LLUI::popMatrix(); + } + + F32 y_offset = (F32)mOffsetY; + + // Render label + { + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); + + for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin(); + segment_iter != mLabelSegments.end(); ++segment_iter ) + { + // Label segments use default font + const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp; + y_offset -= fontp->getLineHeight(); + + F32 x_offset; + if (mTextAlignment == ALIGN_TEXT_CENTER) + { + x_offset = -0.5f*segment_iter->getWidth(fontp); + } + else // ALIGN_LEFT + { + x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f); + } + + LLColor4 label_color(0.f, 0.f, 0.f, 1.f); + label_color.mV[VALPHA] = alpha_factor; + hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, LLFontGL::NO_SHADOW, x_offset, y_offset, label_color, FALSE); + } + } + + // Render text + { + // -1 mMaxLines means unlimited lines. + S32 start_segment; + S32 max_lines = getMaxLines(); + + if (max_lines < 0) + { + start_segment = 0; + } + else + { + start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines); + } + + for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment; + segment_iter != mTextSegments.end(); ++segment_iter ) + { + const LLFontGL* fontp = segment_iter->mFont; + y_offset -= fontp->getLineHeight(); + y_offset -= LINE_PADDING; + + U8 style = segment_iter->mStyle; + LLFontGL::ShadowType shadow = LLFontGL::DROP_SHADOW; + + F32 x_offset; + if (mTextAlignment== ALIGN_TEXT_CENTER) + { + x_offset = -0.5f*segment_iter->getWidth(fontp); + } + else // ALIGN_LEFT + { + x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f); + + // *HACK + x_offset += 1; + } + + text_color = segment_iter->mColor; + text_color.mV[VALPHA] *= alpha_factor; + + hud_render_text(segment_iter->getText(), render_position, *fontp, style, shadow, x_offset, y_offset, text_color, FALSE); + } + } + /// Reset the default color to white. The renderer expects this to be the default. + gGL.color4f(1.0f, 1.0f, 1.0f, 1.0f); + if (for_select) + { + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + } +} + +void LLHUDNameTag::setString(const std::string &text_utf8) +{ + mTextSegments.clear(); + addLine(text_utf8, mColor); +} + +void LLHUDNameTag::clearString() +{ + mTextSegments.clear(); +} + + +void LLHUDNameTag::addLine(const std::string &text_utf8, + const LLColor4& color, + const LLFontGL::StyleFlags style, + const LLFontGL* font) +{ + LLWString wline = utf8str_to_wstring(text_utf8); + if (!wline.empty()) + { + // use default font for segment if custom font not specified + if (!font) + { + font = mFontp; + } + typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; + LLWString seps(utf8str_to_wstring("\r\n")); + boost::char_separator<llwchar> sep(seps.c_str()); + + tokenizer tokens(wline, sep); + tokenizer::iterator iter = tokens.begin(); + + while (iter != tokens.end()) + { + U32 line_length = 0; + do + { + F32 max_pixels = HUD_TEXT_MAX_WIDTH; + S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); + mTextSegments.push_back(segment); + line_length += segment_length; + } + while (line_length != iter->size()); + ++iter; + } + } +} + +void LLHUDNameTag::setLabel(const std::string &label_utf8) +{ + mLabelSegments.clear(); + addLabel(label_utf8); +} + +void LLHUDNameTag::addLabel(const std::string& label_utf8) +{ + LLWString wstr = utf8string_to_wstring(label_utf8); + if (!wstr.empty()) + { + LLWString seps(utf8str_to_wstring("\r\n")); + LLWString empty; + + typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; + boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens); + + tokenizer tokens(wstr, sep); + tokenizer::iterator iter = tokens.begin(); + + while (iter != tokens.end()) + { + U32 line_length = 0; + do + { + S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), + HUD_TEXT_MAX_WIDTH, wstr.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + LLHUDTextSegment segment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor, mFontp); + mLabelSegments.push_back(segment); + line_length += segment_length; + } + while (line_length != iter->size()); + ++iter; + } + } +} + +void LLHUDNameTag::setZCompare(const BOOL zcompare) +{ + mZCompare = zcompare; +} + +void LLHUDNameTag::setFont(const LLFontGL* font) +{ + mFontp = font; +} + + +void LLHUDNameTag::setColor(const LLColor4 &color) +{ + mColor = color; + for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin(); + segment_iter != mTextSegments.end(); ++segment_iter ) + { + segment_iter->mColor = color; + } +} + +void LLHUDNameTag::setAlpha(F32 alpha) +{ + mColor.mV[VALPHA] = alpha; + for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin(); + segment_iter != mTextSegments.end(); ++segment_iter ) + { + segment_iter->mColor.mV[VALPHA] = alpha; + } +} + + +void LLHUDNameTag::setDoFade(const BOOL do_fade) +{ + mDoFade = do_fade; +} + +void LLHUDNameTag::updateVisibility() +{ + if (mSourceObject) + { + mSourceObject->updateText(); + } + + mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal); + + if (!mSourceObject) + { + //llwarns << "LLHUDNameTag::updateScreenPos -- mSourceObject is NULL!" << llendl; + mVisible = TRUE; + sVisibleTextObjects.push_back(LLPointer<LLHUDNameTag> (this)); + return; + } + + // Not visible if parent object is dead + if (mSourceObject->isDead()) + { + mVisible = FALSE; + return; + } + + // push text towards camera by radius of object, but not past camera + LLVector3 vec_from_camera = mPositionAgent - LLViewerCamera::getInstance()->getOrigin(); + LLVector3 dir_from_camera = vec_from_camera; + dir_from_camera.normVec(); + + if (dir_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= 0.f) + { //text is behind camera, don't render + mVisible = FALSE; + return; + } + + if (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= LLViewerCamera::getInstance()->getNear() + 0.1f + mSourceObject->getVObjRadius()) + { + mPositionAgent = LLViewerCamera::getInstance()->getOrigin() + vec_from_camera * ((LLViewerCamera::getInstance()->getNear() + 0.1f) / (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis())); + } + else + { + mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius(); + } + + mLastDistance = (mPositionAgent - LLViewerCamera::getInstance()->getOrigin()).magVec(); + + if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange))) + { + mVisible = FALSE; + return; + } + + LLVector3 x_pixel_vec; + LLVector3 y_pixel_vec; + + LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); + + LLVector3 render_position = mPositionAgent + + (x_pixel_vec * mPositionOffset.mV[VX]) + + (y_pixel_vec * mPositionOffset.mV[VY]); + + mOffscreen = FALSE; + if (!LLViewerCamera::getInstance()->sphereInFrustum(render_position, mRadius)) + { + if (!mVisibleOffScreen) + { + mVisible = FALSE; + return; + } + else + { + mOffscreen = TRUE; + } + } + + mVisible = TRUE; + sVisibleTextObjects.push_back(LLPointer<LLHUDNameTag> (this)); +} + +LLVector2 LLHUDNameTag::updateScreenPos(LLVector2 &offset) +{ + LLCoordGL screen_pos; + LLVector2 screen_pos_vec; + LLVector3 x_pixel_vec; + LLVector3 y_pixel_vec; + LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); + LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec); + if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen) + { + // bubble off-screen, so find a spot for it along screen edge + LLViewerCamera::getInstance()->projectPosAgentToScreenEdge(world_pos, screen_pos); + } + + screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY); + + LLRect world_rect = gViewerWindow->getWorldViewRectScaled(); + S32 bottom = world_rect.mBottom + STATUS_BAR_HEIGHT; + + LLVector2 screen_center; + screen_center.mV[VX] = llclamp((F32)screen_pos_vec.mV[VX], (F32)world_rect.mLeft + mWidth * 0.5f, (F32)world_rect.mRight - mWidth * 0.5f); + + if(mVertAlignment == ALIGN_VERT_TOP) + { + screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], + (F32)bottom, + (F32)world_rect.mTop - mHeight - (F32)MENU_BAR_HEIGHT); + mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f, + screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE); + } + else + { + screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], + (F32)bottom + mHeight * 0.5f, + (F32)world_rect.mTop - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT); + mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE); + } + + return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY)); +} + +void LLHUDNameTag::updateSize() +{ + F32 height = 0.f; + F32 width = 0.f; + + S32 max_lines = getMaxLines(); + //S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines); + //F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size()); + + S32 start_segment; + if (max_lines < 0) start_segment = 0; + else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines); + + std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment; + while (iter != mTextSegments.end()) + { + const LLFontGL* fontp = iter->mFont; + height += fontp->getLineHeight(); + height += LINE_PADDING; + width = llmax(width, llmin(iter->getWidth(fontp), HUD_TEXT_MAX_WIDTH)); + ++iter; + } + + // Don't want line spacing under the last line + if (height > 0.f) + { + height -= LINE_PADDING; + } + + iter = mLabelSegments.begin(); + while (iter != mLabelSegments.end()) + { + height += mFontp->getLineHeight(); + width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH)); + ++iter; + } + + if (width == 0.f) + { + return; + } + + width += HORIZONTAL_PADDING; + height += VERTICAL_PADDING; + + // *TODO: Could do a timer-based resize here + //mWidth = llmax(width, lerp(mWidth, (F32)width, u)); + //mHeight = llmax(height, lerp(mHeight, (F32)height, u)); + mWidth = width; + mHeight = height; +} + +void LLHUDNameTag::updateAll() +{ + // iterate over all text objects, calculate their restoration forces, + // and add them to the visible set if they are on screen and close enough + sVisibleTextObjects.clear(); + + TextObjectIterator text_it; + for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it) + { + LLHUDNameTag* textp = (*text_it); + textp->mTargetPositionOffset.clearVec(); + textp->updateSize(); + textp->updateVisibility(); + } + + // sort back to front for rendering purposes + std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), llhudnametag_further_away()); + + // iterate from front to back, and set LOD based on current screen coverage + F32 screen_area = (F32)(gViewerWindow->getWindowWidthScaled() * gViewerWindow->getWindowHeightScaled()); + F32 current_screen_area = 0.f; + std::vector<LLPointer<LLHUDNameTag> >::reverse_iterator r_it; + for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it) + { + LLHUDNameTag* textp = (*r_it); +// if (textp->mUseBubble) +// { + if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) + { + textp->setLOD(3); + } + else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) + { + textp->setLOD(2); + } + else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) + { + textp->setLOD(1); + } + else + { + textp->setLOD(0); + } + textp->updateSize(); + // find on-screen position and initialize collision rectangle + textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); + current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); +// } + } + + LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat(); + F32 camera_vel = camera_vel_stat->getCurrent(); + if (camera_vel > MAX_STABLE_CAMERA_VELOCITY) + { + return; + } + + VisibleTextObjectIterator src_it; + + for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++) + { + for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it) + { + LLHUDNameTag* src_textp = (*src_it); + +// if (!src_textp->mUseBubble) +// { +// continue; +// } + VisibleTextObjectIterator dst_it = src_it; + ++dst_it; + for (; dst_it != sVisibleTextObjects.end(); ++dst_it) + { + LLHUDNameTag* dst_textp = (*dst_it); + +// if (!dst_textp->mUseBubble) +// { +// continue; +// } + if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) + { + LLRectf intersect_rect = src_textp->mSoftScreenRect; + intersect_rect.intersectWith(dst_textp->mSoftScreenRect); + intersect_rect.stretch(-BUFFER_SIZE * 0.5f); + + F32 src_center_x = src_textp->mSoftScreenRect.getCenterX(); + F32 src_center_y = src_textp->mSoftScreenRect.getCenterY(); + F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX(); + F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY(); + F32 intersect_center_x = intersect_rect.getCenterX(); + F32 intersect_center_y = intersect_rect.getCenterY(); + LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y), + LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y), + 0.5f); + force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y); + force.normVec(); + + LLVector2 src_force = -1.f * force; + LLVector2 dst_force = force; + + LLVector2 force_strength; + F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass); + F32 dst_mult = 1.f - src_mult; + F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight(); + F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight(); + src_force.mV[VY] *= src_aspect_ratio; + src_force.normVec(); + dst_force.mV[VY] *= dst_aspect_ratio; + dst_force.normVec(); + + src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH); + src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH); + dst_force.mV[VX] *= llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH); + dst_force.mV[VY] *= llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH); + + src_textp->mTargetPositionOffset += src_force; + dst_textp->mTargetPositionOffset += dst_force; + src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset); + dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset); + } + } + } + } + + VisibleTextObjectIterator this_object_it; + for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it) + { +// if (!(*this_object_it)->mUseBubble) +// { +// continue; +// } + (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC)); + } +} + +void LLHUDNameTag::setLOD(S32 lod) +{ + mLOD = lod; + //RN: uncomment this to visualize LOD levels + //std::string label = llformat("%d", lod); + //setLabel(label); +} + +S32 LLHUDNameTag::getMaxLines() +{ + switch(mLOD) + { + case 0: + return mMaxLines; + case 1: + return mMaxLines > 0 ? mMaxLines / 2 : 5; + case 2: + return mMaxLines > 0 ? mMaxLines / 3 : 2; + default: + // label only + return 0; + } +} + +void LLHUDNameTag::markDead() +{ + sTextObjects.erase(LLPointer<LLHUDNameTag>(this)); + LLHUDObject::markDead(); +} + +void LLHUDNameTag::shiftAll(const LLVector3& offset) +{ + TextObjectIterator text_it; + for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it) + { + LLHUDNameTag *textp = text_it->get(); + textp->shift(offset); + } +} + +void LLHUDNameTag::shift(const LLVector3& offset) +{ + mPositionAgent += offset; +} + +//static +void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list) +{ + //this might put an object on the pick list a second time, overriding it's mGLName, which is ok + // *FIX: we should probably cull against pick frustum + VisibleTextObjectIterator text_it; + for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it) + { +// if (!(*text_it)->mUseBubble) +// { +// continue; +// } + pick_list.insert((*text_it)->mSourceObject); + } +} + +//static +// called when UI scale changes, to flush font width caches +void LLHUDNameTag::reshape() +{ + TextObjectIterator text_it; + for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it) + { + LLHUDNameTag* textp = (*text_it); + std::vector<LLHUDTextSegment>::iterator segment_iter; + for (segment_iter = textp->mTextSegments.begin(); + segment_iter != textp->mTextSegments.end(); ++segment_iter ) + { + segment_iter->clearFontWidthMap(); + } + for(segment_iter = textp->mLabelSegments.begin(); + segment_iter != textp->mLabelSegments.end(); ++segment_iter ) + { + segment_iter->clearFontWidthMap(); + } + } +} + +//============================================================================ + +F32 LLHUDNameTag::LLHUDTextSegment::getWidth(const LLFontGL* font) +{ + std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font); + if (iter != mFontWidthMap.end()) + { + return iter->second; + } + else + { + F32 width = font->getWidthF32(mText.c_str()); + mFontWidthMap[font] = width; + return width; + } +} diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h new file mode 100644 index 0000000000..9a92307009 --- /dev/null +++ b/indra/newview/llhudnametag.h @@ -0,0 +1,191 @@ +/** + * @file llhudnametag.h + * @brief Name tags for avatars + * @author James Cook + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLHUDNAMETAG_H +#define LLHUDNAMETAG_H + +#include "llpointer.h" + +#include "llhudobject.h" +#include "v4color.h" +//#include "v4coloru.h" +#include "v2math.h" +#include "llrect.h" +//#include "llframetimer.h" +#include "llfontgl.h" +#include <set> +#include <vector> + +class LLDrawable; +class LLHUDNameTag; + +struct llhudnametag_further_away +{ + bool operator()(const LLPointer<LLHUDNameTag>& lhs, const LLPointer<LLHUDNameTag>& rhs) const; +}; + +class LLHUDNameTag : public LLHUDObject +{ +protected: + class LLHUDTextSegment + { + public: + LLHUDTextSegment(const LLWString& text, const LLFontGL::StyleFlags style, const LLColor4& color, const LLFontGL* font) + : mColor(color), + mStyle(style), + mText(text), + mFont(font) + {} + F32 getWidth(const LLFontGL* font); + const LLWString& getText() const { return mText; } + void clearFontWidthMap() { mFontWidthMap.clear(); } + + LLColor4 mColor; + LLFontGL::StyleFlags mStyle; + const LLFontGL* mFont; + private: + LLWString mText; + std::map<const LLFontGL*, F32> mFontWidthMap; + }; + +public: + typedef enum e_text_alignment + { + ALIGN_TEXT_LEFT, + ALIGN_TEXT_CENTER + } ETextAlignment; + + typedef enum e_vert_alignment + { + ALIGN_VERT_TOP, + ALIGN_VERT_CENTER + } EVertAlignment; + +public: + // Set entire string, eliminating existing lines + void setString(const std::string& text_utf8); + + void clearString(); + + // Add text a line at a time, allowing custom formatting + void addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL, const LLFontGL* font = NULL); + + // For bubble chat, set the part above the chat text + void setLabel(const std::string& label_utf8); + void addLabel(const std::string& label_utf8); + + // Sets the default font for lines with no font specified + void setFont(const LLFontGL* font); + void setColor(const LLColor4 &color); + void setAlpha(F32 alpha); + void setZCompare(const BOOL zcompare); + void setDoFade(const BOOL do_fade); + void setVisibleOffScreen(BOOL visible) { mVisibleOffScreen = visible; } + + // mMaxLines of -1 means unlimited lines. + void setMaxLines(S32 max_lines) { mMaxLines = max_lines; } + void setFadeDistance(F32 fade_distance, F32 fade_range) { mFadeDistance = fade_distance; mFadeRange = fade_range; } + void updateVisibility(); + LLVector2 updateScreenPos(LLVector2 &offset_target); + void updateSize(); +// void setMass(F32 mass) { mMass = llmax(0.1f, mass); } + void setTextAlignment(ETextAlignment alignment) { mTextAlignment = alignment; } + void setVertAlignment(EVertAlignment alignment) { mVertAlignment = alignment; } + /*virtual*/ void markDead(); + friend class LLHUDObject; + /*virtual*/ F32 getDistance() const { return mLastDistance; } + //void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; } + S32 getLOD() { return mLOD; } + BOOL getVisible() { return mVisible; } + BOOL getHidden() const { return mHidden; } + void setHidden( BOOL hide ) { mHidden = hide; } + void shift(const LLVector3& offset); + + BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render = FALSE); + + static void shiftAll(const LLVector3& offset); + static void addPickable(std::set<LLViewerObject*> &pick_list); + static void reshape(); + static void setDisplayText(BOOL flag) { sDisplayText = flag ; } + +protected: + LLHUDNameTag(const U8 type); + + /*virtual*/ void render(); + /*virtual*/ void renderForSelect(); + void renderText(BOOL for_select); + static void updateAll(); + void setLOD(S32 lod); + S32 getMaxLines(); + +private: + ~LLHUDNameTag(); + BOOL mDoFade; + F32 mFadeRange; + F32 mFadeDistance; + F32 mLastDistance; + BOOL mZCompare; + BOOL mVisibleOffScreen; + BOOL mOffscreen; + LLColor4 mColor; +// LLVector3 mScale; + F32 mWidth; + F32 mHeight; +// LLColor4U mPickColor; + const LLFontGL* mFontp; + const LLFontGL* mBoldFontp; + LLRectf mSoftScreenRect; + LLVector3 mPositionAgent; + LLVector2 mPositionOffset; + LLVector2 mTargetPositionOffset; + F32 mMass; + S32 mMaxLines; + S32 mOffsetY; + F32 mRadius; + std::vector<LLHUDTextSegment> mTextSegments; + std::vector<LLHUDTextSegment> mLabelSegments; +// LLFrameTimer mResizeTimer; + ETextAlignment mTextAlignment; + EVertAlignment mVertAlignment; + S32 mLOD; + BOOL mHidden; + + static BOOL sDisplayText ; + static std::set<LLPointer<LLHUDNameTag> > sTextObjects; + static std::vector<LLPointer<LLHUDNameTag> > sVisibleTextObjects; +// static std::vector<LLPointer<LLHUDNameTag> > sVisibleHUDTextObjects; + typedef std::set<LLPointer<LLHUDNameTag> >::iterator TextObjectIterator; + typedef std::vector<LLPointer<LLHUDNameTag> >::iterator VisibleTextObjectIterator; +}; + +#endif diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 2b73ed1dcd..d66ac6c5b2 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -30,9 +30,6 @@ * $/LicenseInfo$ */ -// llhudobject.cpp -// Copyright 2002, Linden Research, Inc. - #include "llviewerprecompiledheaders.h" #include "llhudobject.h" @@ -44,7 +41,7 @@ #include "llhudeffecttrail.h" #include "llhudeffectlookat.h" #include "llhudeffectpointat.h" - +#include "llhudnametag.h" #include "llvoicevisualizer.h" #include "llagent.h" @@ -72,7 +69,6 @@ LLHUDObject::LLHUDObject(const U8 type) : mVisible = TRUE; mType = type; mDead = FALSE; - mOnHUDAttachment = FALSE; } LLHUDObject::~LLHUDObject() @@ -151,6 +147,9 @@ LLHUDObject *LLHUDObject::addHUDObject(const U8 type) case LL_HUD_ICON: hud_objectp = new LLHUDIcon(type); break; + case LL_HUD_NAME_TAG: + hud_objectp = new LLHUDNameTag(type); + break; default: llwarns << "Unknown type of hud object:" << (U32) type << llendl; } @@ -263,6 +262,7 @@ void LLHUDObject::updateAll() LLFastTimer ftm(FTM_HUD_UPDATE); LLHUDText::updateAll(); LLHUDIcon::updateAll(); + LLHUDNameTag::updateAll(); sortObjects(); } @@ -311,6 +311,14 @@ void LLHUDObject::renderAllForSelect() } // static +void LLHUDObject::reshapeAll() +{ + // only hud objects that use fonts care about window size/scale changes + LLHUDText::reshape(); + LLHUDNameTag::reshape(); +} + +// static void LLHUDObject::sortObjects() { sHUDObjects.sort(hud_object_further_away()); diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index d304ac41af..a370b31a20 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -42,7 +42,7 @@ #include "v4color.h" #include "v3math.h" #include "v3dmath.h" -#include "lldrawpool.h" +#include "lldrawpool.h" // TODO: eliminate, unused below #include <list> class LLViewerCamera; @@ -76,6 +76,9 @@ public: static void renderAll(); static void renderAllForSelect(); + // Some objects may need to update when window shape changes + static void reshapeAll(); + static void cleanupHUDObjects(); enum @@ -96,7 +99,8 @@ public: LL_HUD_EFFECT_EDIT, LL_HUD_EFFECT_LOOKAT, LL_HUD_EFFECT_POINTAT, - LL_HUD_EFFECT_VOICE_VISUALIZER // Ventrella + LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella + LL_HUD_NAME_TAG }; protected: static void sortObjects(); @@ -112,7 +116,6 @@ protected: BOOL mDead; BOOL mVisible; LLVector3d mPositionGlobal; - BOOL mOnHUDAttachment; LLPointer<LLViewerObject> mSourceObject; LLPointer<LLViewerObject> mTargetObject; diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 8d1d27444b..31522f6efb 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -1,7 +1,6 @@ - /** * @file llhudtext.cpp - * @brief LLHUDText class implementation + * @brief Floating text above objects, set via script with llSetText() * * $LicenseInfo:firstyear=2002&license=viewergpl$ * @@ -62,16 +61,16 @@ const F32 HORIZONTAL_PADDING = 15.f; const F32 VERTICAL_PADDING = 12.f; const F32 BUFFER_SIZE = 2.f; const F32 MIN_EDGE_OVERLAP = 3.f; -F32 HUD_TEXT_MAX_WIDTH = 190.f; +const F32 HUD_TEXT_MAX_WIDTH = 190.f; const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f; const F32 RESIZE_TIME = 0.f; const S32 NUM_OVERLAP_ITERATIONS = 10; const F32 NEIGHBOR_FORCE_FRACTION = 1.f; const F32 POSITION_DAMPING_TC = 0.2f; const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f; -const F32 LOD_0_SCREEN_COVERAGE = 0.15f; -const F32 LOD_1_SCREEN_COVERAGE = 0.30f; -const F32 LOD_2_SCREEN_COVERAGE = 0.40f; +//const F32 LOD_0_SCREEN_COVERAGE = 0.15f; +//const F32 LOD_1_SCREEN_COVERAGE = 0.30f; +//const F32 LOD_2_SCREEN_COVERAGE = 0.40f; std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects; std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects; @@ -80,15 +79,14 @@ BOOL LLHUDText::sDisplayText = TRUE ; bool lltextobject_further_away::operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const { - return (lhs->getDistance() > rhs->getDistance()) ? true : false; + return lhs->getDistance() > rhs->getDistance(); } LLHUDText::LLHUDText(const U8 type) : LLHUDObject(type), - mUseBubble(FALSE), - mUsePixelSize(TRUE), - mVisibleOffScreen(FALSE), + mOnHUDAttachment(FALSE), +// mVisibleOffScreen(FALSE), mWidth(0.f), mHeight(0.f), mFontp(LLFontGL::getFontSansSerifSmall()), @@ -98,7 +96,7 @@ LLHUDText::LLHUDText(const U8 type) : mOffsetY(0), mTextAlignment(ALIGN_TEXT_CENTER), mVertAlignment(ALIGN_VERT_CENTER), - mLOD(0), +// mLOD(0), mHidden(FALSE) { mColor = LLColor4(1.f, 1.f, 1.f, 1.f); @@ -106,7 +104,6 @@ LLHUDText::LLHUDText(const U8 type) : mFadeDistance = 8.f; mFadeRange = 4.f; mZCompare = TRUE; - mDropShadow = TRUE; mOffscreen = FALSE; mRadius = 0.1f; LLPointer<LLHUDText> ptr(this); @@ -117,112 +114,6 @@ LLHUDText::~LLHUDText() { } - -BOOL LLHUDText::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render) -{ - if (!mVisible || mHidden) - { - return FALSE; - } - - // don't pick text that isn't bound to a viewerobject or isn't in a bubble - if (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble) - { - return FALSE; - } - - F32 alpha_factor = 1.f; - LLColor4 text_color = mColor; - if (mDoFade) - { - if (mLastDistance > mFadeDistance) - { - alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange); - text_color.mV[3] = text_color.mV[3]*alpha_factor; - } - } - if (text_color.mV[3] < 0.01f) - { - return FALSE; - } - - mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f)); - - // scale screen size of borders down - //RN: for now, text on hud objects is never occluded - - LLVector3 x_pixel_vec; - LLVector3 y_pixel_vec; - - if (mOnHUDAttachment) - { - x_pixel_vec = LLVector3::y_axis / (F32)gViewerWindow->getWindowWidthScaled(); - y_pixel_vec = LLVector3::z_axis / (F32)gViewerWindow->getWindowHeightScaled(); - } - else - { - LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); - } - - LLVector3 width_vec = mWidth * x_pixel_vec; - LLVector3 height_vec = mHeight * y_pixel_vec; - - LLCoordGL screen_pos; - LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); - - LLVector2 screen_offset; - screen_offset = updateScreenPos(mPositionOffset); - - LLVector3 render_position = mPositionAgent - + (x_pixel_vec * screen_offset.mV[VX]) - + (y_pixel_vec * screen_offset.mV[VY]); - - - if (mUseBubble) - { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); - - LLVector3 v[] = - { - bg_pos, - bg_pos + width_vec, - bg_pos + width_vec + height_vec, - bg_pos + height_vec, - }; - - if (debug_render) - { - gGL.begin(LLRender::LINE_STRIP); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[1].mV); - gGL.vertex3fv(v[2].mV); - gGL.vertex3fv(v[3].mV); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[2].mV); - gGL.end(); - } - - LLVector3 dir = end-start; - F32 t = 0.f; - - if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, NULL, NULL, &t, FALSE) || - LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, NULL, NULL, &t, FALSE) ) - { - if (t <= 1.f) - { - intersection = start + dir*t; - return TRUE; - } - } - } - - return FALSE; -} - void LLHUDText::render() { if (!mOnHUDAttachment && sDisplayText) @@ -248,21 +139,13 @@ void LLHUDText::renderText(BOOL for_select) return; } - // don't pick text that isn't bound to a viewerobject or isn't in a bubble - if (for_select && - (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble)) + // don't pick text + if (for_select) { return; } - if (for_select) - { - gGL.getTexUnit(0)->disable(); - } - else - { - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - } + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE); LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE); @@ -290,7 +173,7 @@ void LLHUDText::renderText(BOOL for_select) LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square"); // *TODO: make this a per-text setting - LLColor4 bg_color = LLUIColorTable::instance().getColor("BackgroundChatColor"); + LLColor4 bg_color = LLUIColorTable::instance().getColor("ObjectBubbleColor"); bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); const S32 border_height = 16; @@ -336,178 +219,17 @@ void LLHUDText::renderText(BOOL for_select) LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); LLVector2 screen_offset; - if (!mUseBubble) - { - screen_offset = mPositionOffset; - } - else - { - screen_offset = updateScreenPos(mPositionOffset); - } + screen_offset = mPositionOffset; LLVector3 render_position = mPositionAgent + (x_pixel_vec * screen_offset.mV[VX]) + (y_pixel_vec * screen_offset.mV[VY]); - //if (mOnHUD) - //{ - // render_position.mV[VY] -= fmodf(render_position.mV[VY], 1.f / (F32)gViewerWindow->getWindowWidthScaled()); - // render_position.mV[VZ] -= fmodf(render_position.mV[VZ], 1.f / (F32)gViewerWindow->getWindowHeightScaled()); - //} - //else - //{ - // render_position = LLViewerCamera::getInstance()->roundToPixel(render_position); - //} - - if (mUseBubble) - { - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLUI::pushMatrix(); - { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); - - if (for_select) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - S32 name = mSourceObject->mGLName; - LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name); - gGL.color4ubv(coloru.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - LLUI::popMatrix(); - return; - } - else - { - gGL.getTexUnit(0)->bind(imagep->getImage()); - - gGL.color4fv(bg_color.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - - if ( mLabelSegments.size()) - { - LLUI::pushMatrix(); - { - gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec; - LLVector3 label_offset = height_vec - label_height; - LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]); - gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height); - } - LLUI::popMatrix(); - } - } - - BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f; - BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f); - - // draw line segments pointing to parent object - if (!mOffscreen && (outside_width || outside_height)) - { - LLUI::pushMatrix(); - { - gGL.color4fv(bg_color.mV); - LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec); - target_pos += (width_vec / 2.f); - target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero; - target_pos -= 3.f * x_pixel_vec; - target_pos -= 6.f * y_pixel_vec; - LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]); - gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec); - } - LLUI::popMatrix(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE); - - LLVector3 box_center_offset; - box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f); - LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]); - gGL.color4fv(bg_color.mV); - LLUI::setLineWidth(2.0); - gGL.begin(LLRender::LINES); - { - if (outside_width) - { - LLVector3 vert; - // draw line in x then y - if (mPositionOffset.mV[VX] < 0.f) - { - // start at right edge - vert = width_vec * 0.5f; - gGL.vertex3fv(vert.mV); - } - else - { - // start at left edge - vert = width_vec * -0.5f; - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VX] * x_pixel_vec; - gGL.vertex3fv(vert.mV); - gGL.vertex3fv(vert.mV); - vert -= mPositionOffset.mV[VY] * y_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - else - { - LLVector3 vert; - // draw line in y then x - if (mPositionOffset.mV[VY] < 0.f) - { - // start at top edge - vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - else - { - // start at bottom edge - vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - } - gGL.end(); - LLUI::setLineWidth(1.0); - - } - } - LLUI::popMatrix(); - } - F32 y_offset = (F32)mOffsetY; // Render label { gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - - for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin(); - segment_iter != mLabelSegments.end(); ++segment_iter ) - { - const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp; - y_offset -= fontp->getLineHeight(); - - F32 x_offset; - if (mTextAlignment == ALIGN_TEXT_CENTER) - { - x_offset = -0.5f*segment_iter->getWidth(fontp); - } - else // ALIGN_LEFT - { - x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f); - } - - LLColor4 label_color(0.f, 0.f, 0.f, 1.f); - label_color.mV[VALPHA] = alpha_factor; - hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, LLFontGL::NO_SHADOW, x_offset, y_offset, label_color, mOnHUDAttachment); - } } // Render text @@ -528,15 +250,11 @@ void LLHUDText::renderText(BOOL for_select) for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment; segment_iter != mTextSegments.end(); ++segment_iter ) { - const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp; + const LLFontGL* fontp = segment_iter->mFont; y_offset -= fontp->getLineHeight(); U8 style = segment_iter->mStyle; - LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW; - if (mDropShadow) - { - shadow = LLFontGL::DROP_SHADOW; - } + LLFontGL::ShadowType shadow = LLFontGL::DROP_SHADOW; F32 x_offset; if (mTextAlignment== ALIGN_TEXT_CENTER) @@ -556,21 +274,12 @@ void LLHUDText::renderText(BOOL for_select) } /// Reset the default color to white. The renderer expects this to be the default. gGL.color4f(1.0f, 1.0f, 1.0f, 1.0f); - if (for_select) - { - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - } -} - -void LLHUDText::setStringUTF8(const std::string &wtext) -{ - setString(utf8str_to_wstring(wtext)); } -void LLHUDText::setString(const LLWString &wtext) +void LLHUDText::setString(const std::string &text_utf8) { mTextSegments.clear(); - addLine(wtext, mColor); + addLine(text_utf8, mColor); } void LLHUDText::clearString() @@ -579,21 +288,19 @@ void LLHUDText::clearString() } -void LLHUDText::addLine(const std::string &str, const LLColor4& color, const LLFontGL::StyleFlags style) +void LLHUDText::addLine(const std::string &text_utf8, + const LLColor4& color, + const LLFontGL::StyleFlags style, + const LLFontGL* font) { - addLine(utf8str_to_wstring(str), color, style); -} - - -void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFontGL::StyleFlags style) -{ - if (gNoRender) - { - return; - } - if (!wstr.empty()) + LLWString wline = utf8str_to_wstring(text_utf8); + if (!wline.empty()) { - LLWString wline(wstr); + // use default font for segment if custom font not specified + if (!font) + { + font = mFontp; + } typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; LLWString seps(utf8str_to_wstring("\r\n")); boost::char_separator<llwchar> sep(seps.c_str()); @@ -606,8 +313,10 @@ void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFo U32 line_length = 0; do { - S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); - mTextSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), style, color)); + F32 max_pixels = HUD_TEXT_MAX_WIDTH_NO_BUBBLE; + S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); + mTextSegments.push_back(segment); line_length += segment_length; } while (line_length != iter->size()); @@ -616,47 +325,6 @@ void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFo } } -void LLHUDText::setLabel(const std::string &label) -{ - setLabel(utf8str_to_wstring(label)); -} - -void LLHUDText::setLabel(const LLWString &wlabel) -{ - mLabelSegments.clear(); - - if (!wlabel.empty()) - { - LLWString wstr(wlabel); - LLWString seps(utf8str_to_wstring("\r\n")); - LLWString empty; - - typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; - boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens); - - tokenizer tokens(wstr, sep); - tokenizer::iterator iter = tokens.begin(); - - while (iter != tokens.end()) - { - U32 line_length = 0; - do - { - S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); - mLabelSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor)); - line_length += segment_length; - } - while (line_length != iter->size()); - ++iter; - } - } -} - -void LLHUDText::setDropShadow(const BOOL do_shadow) -{ - mDropShadow = do_shadow; -} - void LLHUDText::setZCompare(const BOOL zcompare) { mZCompare = zcompare; @@ -678,12 +346,17 @@ void LLHUDText::setColor(const LLColor4 &color) } } - -void LLHUDText::setUsePixelSize(const BOOL use_pixel_size) +void LLHUDText::setAlpha(F32 alpha) { - mUsePixelSize = use_pixel_size; + mColor.mV[VALPHA] = alpha; + for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin(); + segment_iter != mTextSegments.end(); ++segment_iter ) + { + segment_iter->mColor.mV[VALPHA] = alpha; + } } + void LLHUDText::setDoFade(const BOOL do_fade) { mDoFade = do_fade; @@ -751,7 +424,7 @@ void LLHUDText::updateVisibility() mLastDistance = (mPositionAgent - LLViewerCamera::getInstance()->getOrigin()).magVec(); - if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange))) + if (!mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange))) { mVisible = FALSE; return; @@ -769,15 +442,15 @@ void LLHUDText::updateVisibility() mOffscreen = FALSE; if (!LLViewerCamera::getInstance()->sphereInFrustum(render_position, mRadius)) { - if (!mVisibleOffScreen) - { +// if (!mVisibleOffScreen) +// { mVisible = FALSE; return; - } - else - { - mOffscreen = TRUE; - } +// } +// else +// { +// mOffscreen = TRUE; +// } } mVisible = TRUE; @@ -792,11 +465,11 @@ LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset) LLVector3 y_pixel_vec; LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec); - if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen) - { - // bubble off-screen, so find a spot for it along screen edge - LLViewerCamera::getInstance()->projectPosAgentToScreenEdge(world_pos, screen_pos); - } +// if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen) +// { +// // bubble off-screen, so find a spot for it along screen edge +// LLViewerCamera::getInstance()->projectPosAgentToScreenEdge(world_pos, screen_pos); +// } screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY); @@ -827,12 +500,12 @@ LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset) void LLHUDText::updateSize() { + F32 height = 0.f; F32 width = 0.f; S32 max_lines = getMaxLines(); - S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines); - - F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size()); + //S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines); + //F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size()); S32 start_segment; if (max_lines < 0) start_segment = 0; @@ -841,17 +514,12 @@ void LLHUDText::updateSize() std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment; while (iter != mTextSegments.end()) { - width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH)); + const LLFontGL* fontp = iter->mFont; + height += fontp->getLineHeight(); + width = llmax(width, llmin(iter->getWidth(fontp), HUD_TEXT_MAX_WIDTH)); ++iter; } - iter = mLabelSegments.begin(); - while (iter != mLabelSegments.end()) - { - width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH)); - ++iter; - } - if (width == 0.f) { return; @@ -860,18 +528,8 @@ void LLHUDText::updateSize() width += HORIZONTAL_PADDING; height += VERTICAL_PADDING; - if (!mResizeTimer.getStarted() && (width != mWidth || height != mHeight)) - { - mResizeTimer.start(); - } - - // *NOTE: removed logic which did a divide by zero. - F32 u = 1.f;//llclamp(mResizeTimer.getElapsedTimeF32() / RESIZE_TIME, 0.f, 1.f); - if (u == 1.f) - { - mResizeTimer.stop(); - } - + // *TODO: Could do some sort of timer-based resize logic here + F32 u = 1.f; mWidth = llmax(width, lerp(mWidth, (F32)width, u)); mHeight = llmax(height, lerp(mHeight, (F32)height, u)); } @@ -895,146 +553,31 @@ void LLHUDText::updateAll() // sort back to front for rendering purposes std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), lltextobject_further_away()); std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(), lltextobject_further_away()); - - // iterate from front to back, and set LOD based on current screen coverage - F32 screen_area = (F32)(gViewerWindow->getWindowWidthScaled() * gViewerWindow->getWindowHeightScaled()); - F32 current_screen_area = 0.f; - std::vector<LLPointer<LLHUDText> >::reverse_iterator r_it; - for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it) - { - LLHUDText* textp = (*r_it); - if (textp->mUseBubble) - { - if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) - { - textp->setLOD(3); - } - else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) - { - textp->setLOD(2); - } - else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) - { - textp->setLOD(1); - } - else - { - textp->setLOD(0); - } - textp->updateSize(); - // find on-screen position and initialize collision rectangle - textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); - current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); - } - } - - LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat(); - F32 camera_vel = camera_vel_stat->getCurrent(); - if (camera_vel > MAX_STABLE_CAMERA_VELOCITY) - { - return; - } - - VisibleTextObjectIterator src_it; - - for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++) - { - for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it) - { - LLHUDText* src_textp = (*src_it); - - if (!src_textp->mUseBubble) - { - continue; - } - VisibleTextObjectIterator dst_it = src_it; - ++dst_it; - for (; dst_it != sVisibleTextObjects.end(); ++dst_it) - { - LLHUDText* dst_textp = (*dst_it); - - if (!dst_textp->mUseBubble) - { - continue; - } - if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) - { - LLRectf intersect_rect = src_textp->mSoftScreenRect; - intersect_rect.intersectWith(dst_textp->mSoftScreenRect); - intersect_rect.stretch(-BUFFER_SIZE * 0.5f); - - F32 src_center_x = src_textp->mSoftScreenRect.getCenterX(); - F32 src_center_y = src_textp->mSoftScreenRect.getCenterY(); - F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX(); - F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY(); - F32 intersect_center_x = intersect_rect.getCenterX(); - F32 intersect_center_y = intersect_rect.getCenterY(); - LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y), - LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y), - 0.5f); - force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y); - force.normVec(); - - LLVector2 src_force = -1.f * force; - LLVector2 dst_force = force; - - LLVector2 force_strength; - F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass); - F32 dst_mult = 1.f - src_mult; - F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight(); - F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight(); - src_force.mV[VY] *= src_aspect_ratio; - src_force.normVec(); - dst_force.mV[VY] *= dst_aspect_ratio; - dst_force.normVec(); - - src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH); - src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH); - dst_force.mV[VX] *= llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH); - dst_force.mV[VY] *= llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH); - - src_textp->mTargetPositionOffset += src_force; - dst_textp->mTargetPositionOffset += dst_force; - src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset); - dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset); - } - } - } - } - - VisibleTextObjectIterator this_object_it; - for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it) - { - if (!(*this_object_it)->mUseBubble) - { - continue; - } - (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC)); - } } -void LLHUDText::setLOD(S32 lod) -{ - mLOD = lod; - //RN: uncomment this to visualize LOD levels - //std::string label = llformat("%d", lod); - //setLabel(label); -} +//void LLHUDText::setLOD(S32 lod) +//{ +// mLOD = lod; +// //RN: uncomment this to visualize LOD levels +// //std::string label = llformat("%d", lod); +// //setLabel(label); +//} S32 LLHUDText::getMaxLines() { - switch(mLOD) - { - case 0: - return mMaxLines; - case 1: - return mMaxLines > 0 ? mMaxLines / 2 : 5; - case 2: - return mMaxLines > 0 ? mMaxLines / 3 : 2; - default: - // label only - return 0; - } + return mMaxLines; + //switch(mLOD) + //{ + //case 0: + // return mMaxLines; + //case 1: + // return mMaxLines > 0 ? mMaxLines / 2 : 5; + //case 2: + // return mMaxLines > 0 ? mMaxLines / 3 : 2; + //default: + // // label only + // return 0; + //} } void LLHUDText::markDead() @@ -1085,22 +628,6 @@ void LLHUDText::shift(const LLVector3& offset) mPositionAgent += offset; } -//static -void LLHUDText::addPickable(std::set<LLViewerObject*> &pick_list) -{ - //this might put an object on the pick list a second time, overriding it's mGLName, which is ok - // *FIX: we should probably cull against pick frustum - VisibleTextObjectIterator text_it; - for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it) - { - if (!(*text_it)->mUseBubble) - { - continue; - } - pick_list.insert((*text_it)->mSourceObject); - } -} - //static // called when UI scale changes, to flush font width caches void LLHUDText::reshape() @@ -1115,11 +642,6 @@ void LLHUDText::reshape() { segment_iter->clearFontWidthMap(); } - for(segment_iter = textp->mLabelSegments.begin(); - segment_iter != textp->mLabelSegments.end(); ++segment_iter ) - { - segment_iter->clearFontWidthMap(); - } } } diff --git a/indra/newview/llhudtext.h b/indra/newview/llhudtext.h index dc14a8c764..27b8f07cca 100644 --- a/indra/newview/llhudtext.h +++ b/indra/newview/llhudtext.h @@ -34,18 +34,15 @@ #define LL_LLHUDTEXT_H #include "llpointer.h" -#include "lldarrayptr.h" #include "llhudobject.h" #include "v4color.h" #include "v4coloru.h" #include "v2math.h" #include "llrect.h" -#include "llframetimer.h" #include "llfontgl.h" #include <set> #include <vector> -#include "lldarray.h" // Renders a 2D text billboard floating at the location specified. class LLDrawable; @@ -62,14 +59,19 @@ protected: class LLHUDTextSegment { public: - LLHUDTextSegment(const LLWString& text, const LLFontGL::StyleFlags style, const LLColor4& color) - : mColor(color), mStyle(style), mText(text) {} + LLHUDTextSegment(const LLWString& text, const LLFontGL::StyleFlags style, const LLColor4& color, const LLFontGL* font) + : mColor(color), + mStyle(style), + mText(text), + mFont(font) + {} F32 getWidth(const LLFontGL* font); - const LLWString& getText() const { return mText; }; + const LLWString& getText() const { return mText; } void clearFontWidthMap() { mFontWidthMap.clear(); } LLColor4 mColor; LLFontGL::StyleFlags mStyle; + const LLFontGL* mFont; private: LLWString mText; std::map<const LLFontGL*, F32> mFontWidthMap; @@ -89,20 +91,21 @@ public: } EVertAlignment; public: - void setStringUTF8(const std::string &utf8string); - void setString(const LLWString &wstring); + // Set entire string, eliminating existing lines + void setString(const std::string& text_utf8); + void clearString(); - void addLine(const std::string &text, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL); - void addLine(const LLWString &wtext, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL); - void setLabel(const std::string &label); - void setLabel(const LLWString &label); - void setDropShadow(const BOOL do_shadow); + + // Add text a line at a time, allowing custom formatting + void addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL, const LLFontGL* font = NULL); + + // Sets the default font for lines with no font specified void setFont(const LLFontGL* font); void setColor(const LLColor4 &color); - void setUsePixelSize(const BOOL use_pixel_size); + void setAlpha(F32 alpha); void setZCompare(const BOOL zcompare); void setDoFade(const BOOL do_fade); - void setVisibleOffScreen(BOOL visible) { mVisibleOffScreen = visible; } +// void setVisibleOffScreen(BOOL visible) { mVisibleOffScreen = visible; } // mMaxLines of -1 means unlimited lines. void setMaxLines(S32 max_lines) { mMaxLines = max_lines; } @@ -116,21 +119,17 @@ public: /*virtual*/ void markDead(); friend class LLHUDObject; /*virtual*/ F32 getDistance() const { return mLastDistance; } - void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; } - S32 getLOD() { return mLOD; } BOOL getVisible() { return mVisible; } BOOL getHidden() const { return mHidden; } void setHidden( BOOL hide ) { mHidden = hide; } void setOnHUDAttachment(BOOL on_hud) { mOnHUDAttachment = on_hud; } void shift(const LLVector3& offset); - BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render = FALSE); - static void shiftAll(const LLVector3& offset); static void renderAllHUD(); - static void addPickable(std::set<LLViewerObject*> &pick_list); static void reshape(); static void setDisplayText(BOOL flag) { sDisplayText = flag ; } + protected: LLHUDText(const U8 type); @@ -138,21 +137,17 @@ protected: /*virtual*/ void renderForSelect(); void renderText(BOOL for_select); static void updateAll(); - void setLOD(S32 lod); S32 getMaxLines(); private: ~LLHUDText(); - BOOL mOnHUD; - BOOL mUseBubble; - BOOL mDropShadow; + BOOL mOnHUDAttachment; BOOL mDoFade; F32 mFadeRange; F32 mFadeDistance; F32 mLastDistance; - BOOL mUsePixelSize; BOOL mZCompare; - BOOL mVisibleOffScreen; +// BOOL mVisibleOffScreen; BOOL mOffscreen; LLColor4 mColor; LLVector3 mScale; @@ -170,11 +165,8 @@ private: S32 mOffsetY; F32 mRadius; std::vector<LLHUDTextSegment> mTextSegments; - std::vector<LLHUDTextSegment> mLabelSegments; - LLFrameTimer mResizeTimer; ETextAlignment mTextAlignment; EVertAlignment mVertAlignment; - S32 mLOD; BOOL mHidden; static BOOL sDisplayText ; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 3aa9d75bc0..1c1d9343aa 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -38,6 +38,7 @@ #include "llagent.h" #include "llappviewer.h" +#include "llavatarnamecache.h" #include "llbutton.h" #include "llbottomtray.h" #include "llchannelmanager.h" @@ -275,12 +276,6 @@ BOOL LLIMFloater::postBuild() mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); mInputEditor->setPassDelete( TRUE ); - std::string session_name(LLIMModel::instance().getName(mSessionID)); - - mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + session_name); - - setTitle(session_name); - childSetCommitCallback("chat_editor", onSendMsg, this); mChatHistory = getChild<LLChatHistory>("chat_history"); @@ -298,6 +293,19 @@ BOOL LLIMFloater::postBuild() mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); } + if ( im_session && im_session->isP2PSessionType()) + { + // look up display name for window title + LLAvatarNameCache::get(im_session->mOtherParticipantID, + boost::bind(&LLIMFloater::onAvatarNameCache, + this, _1, _2)); + } + else + { + std::string session_name(LLIMModel::instance().getName(mSessionID)); + updateSessionName(session_name, session_name); + } + //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" //see LLFloaterIMPanel for how it is done (IB) @@ -311,6 +319,22 @@ BOOL LLIMFloater::postBuild() } } +void LLIMFloater::updateSessionName(const std::string& ui_title, + const std::string& ui_label) +{ + mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + ui_label); + setTitle(ui_title); +} + +void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + // Use display name only for labels, as the extended name will be in the + // floater title + std::string ui_title = av_name.getCompleteName(); + updateSessionName(ui_title, av_name.mDisplayName); +} + // virtual void LLIMFloater::draw() { diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index fef178e3a2..addb218651 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -38,6 +38,7 @@ #include "lltooldraganddrop.h" #include "lltransientdockablefloater.h" +class LLAvatarName; class LLLineEditor; class LLPanelChatControlPanel; class LLChatHistory; @@ -130,6 +131,12 @@ private: /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); + // Update the window title, input field help text, etc. + void updateSessionName(const std::string& ui_title, const std::string& ui_label); + + // For display name lookups for IM window titles + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + BOOL dropCallingCard(LLInventoryItem* item, BOOL drop); BOOL dropCategory(LLInventoryCategory* category, BOOL drop); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 2c1983b6d2..d0bb98fc8c 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -34,6 +34,7 @@ #include "llimview.h" +#include "llavatarnamecache.h" // IDEVO #include "llfloaterreg.h" #include "llfontgl.h" #include "llgl.h" @@ -70,11 +71,6 @@ #include "llviewerparcelmgr.h" -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); - const static std::string NO_SESSION("(IM Session Doesn't Exist)"); const static std::string ADHOC_NAME_SUFFIX(" Conference"); @@ -108,6 +104,20 @@ BOOL LLSessionTimeoutTimer::tick() return TRUE; } +static void on_avatar_name_cache_toast(const LLUUID& agent_id, + const LLAvatarName& av_name, + LLSD msg) +{ + LLSD args; + args["MESSAGE"] = msg["message"]; + args["TIME"] = msg["time"]; + // *TODO: Can this ever be an object name or group name? + args["FROM"] = av_name.getCompleteName(); + args["FROM_ID"] = msg["from_id"]; + args["SESSION_ID"] = msg["session_id"]; + LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); +} + void toast_callback(const LLSD& msg){ // do not show toast in busy mode or it goes from agent if (gAgent.getBusy() || gAgent.getID() == msg["from_id"]) @@ -135,14 +145,9 @@ void toast_callback(const LLSD& msg){ return; } - LLSD args; - args["MESSAGE"] = msg["message"]; - args["TIME"] = msg["time"]; - args["FROM"] = msg["from"]; - args["FROM_ID"] = msg["from_id"]; - args["SESSION_ID"] = msg["session_id"]; - - LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); + LLAvatarNameCache::get(msg["from_id"].asUUID(), + boost::bind(&on_avatar_name_cache_toast, + _1, _2, msg)); } void LLIMModel::setActiveSessionID(const LLUUID& session_id) @@ -269,7 +274,7 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES // no text notifications break; case P2P_SESSION: - gCacheName->getFullName(mOtherParticipantID, other_avatar_name); + gCacheName->getFullName(mOtherParticipantID, other_avatar_name); // voice if(direction == LLVoiceChannel::INCOMING_CALL) { @@ -404,13 +409,17 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& histo const LLSD& msg = *it; std::string from = msg[IM_FROM]; - LLUUID from_id = LLUUID::null; - if (msg[IM_FROM_ID].isUndefined()) + LLUUID from_id; + if (msg[IM_FROM_ID].isDefined()) { + from_id = msg[IM_FROM_ID].asUUID(); + } + else + { + // Legacy chat logs only wrote the legacy name, not the agent_id gCacheName->getUUID(from, from_id); } - std::string timestamp = msg[IM_TIME]; std::string text = msg[IM_TEXT]; @@ -631,7 +640,7 @@ bool LLIMModel::clearSession(const LLUUID& session_id) void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) { LLIMSession* session = findIMSession(session_id); - if (!session) + if (!session) { llwarns << "session " << session_id << "does not exist " << llendl; return; @@ -639,7 +648,7 @@ void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& m int i = session->mMsgs.size() - start_index; - for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); + for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); iter != session->mMsgs.end() && i > 0; iter++) { @@ -1742,11 +1751,23 @@ void LLOutgoingCallDialog::show(const LLSD& key) callee_name = LLTextUtil::formatPhoneNumber(callee_name); } - setTitle(callee_name); - LLSD callee_id = mPayload["other_user_id"]; - childSetTextArg("calling", "[CALLEE_NAME]", callee_name); - childSetTextArg("connecting", "[CALLEE_NAME]", callee_name); + // Beautification: Since you know who you called, just show display name + std::string title = callee_name; + std::string final_callee_name = callee_name; + if (mPayload["session_type"].asInteger() == LLIMModel::LLIMSession::P2P_SESSION) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(callee_id, &av_name)) + { + final_callee_name = av_name.mDisplayName; + title = av_name.getCompleteName(); + } + } + childSetTextArg("calling", "[CALLEE_NAME]", final_callee_name); + childSetTextArg("connecting", "[CALLEE_NAME]", final_callee_name); + + setTitle(title); // for outgoing group calls callee_id == group id == session id setIcon(callee_id, callee_id); @@ -1903,16 +1924,21 @@ BOOL LLIncomingCallDialog::postBuild() if (caller_name == "anonymous") { caller_name = getString("anonymous"); + setCallerName(caller_name, caller_name, call_type); } else if (!is_avatar) { caller_name = LLTextUtil::formatPhoneNumber(caller_name); + setCallerName(caller_name, caller_name, call_type); + } + else + { + // Get the full name information + LLAvatarNameCache::get(caller_id, + boost::bind(&LLIncomingCallDialog::onAvatarNameCache, + this, _1, _2, call_type)); } - setTitle(caller_name + " " + call_type); - - LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); - caller_name_widget->setValue(caller_name + " " + call_type); setIcon(session_id, caller_id); childSetAction("Accept", onAccept, this); @@ -1936,6 +1962,24 @@ BOOL LLIncomingCallDialog::postBuild() return TRUE; } +void LLIncomingCallDialog::setCallerName(const std::string& ui_title, + const std::string& ui_label, + const std::string& call_type) +{ + setTitle(ui_title); + + // call_type may be a string like " is calling." + LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); + caller_name_widget->setValue(ui_label + " " + call_type); +} + +void LLIncomingCallDialog::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + const std::string& call_type) +{ + std::string title = av_name.getCompleteName(); + setCallerName(title, av_name.mDisplayName, call_type); +} void LLIncomingCallDialog::onOpen(const LLSD& key) { @@ -2043,8 +2087,11 @@ void LLIncomingCallDialog::processCallResponse(S32 response) } else { - if (gCacheName->getFullName(caller_id, correct_session_name)) + // *NOTE: really should be using callbacks here + LLAvatarName av_name; + if (LLAvatarNameCache::get(caller_id, &av_name)) { + correct_session_name = av_name.mDisplayName + " (" + av_name.mUsername + ")"; correct_session_name.append(ADHOC_NAME_SUFFIX); } } @@ -2557,7 +2604,8 @@ void LLIMMgr::inviteToSession( { if (caller_name.empty()) { - gCacheName->get(caller_id, FALSE, boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3, _4)); + gCacheName->get(caller_id, false, // voice + boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3)); } else { @@ -2567,9 +2615,9 @@ void LLIMMgr::inviteToSession( } } -void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group) { - payload["caller_name"] = first + " " + last; + payload["caller_name"] = name; payload["session_name"] = payload["caller_name"].asString(); std::string notify_box_type = payload["notify_box_type"].asString(); @@ -2790,13 +2838,14 @@ void LLIMMgr::noteOfflineUsers( for(S32 i = 0; i < count; ++i) { info = at.getBuddyInfo(ids.get(i)); - std::string first, last; - if(info && !info->isOnline() - && gCacheName->getName(ids.get(i), first, last)) + LLAvatarName av_name; + if (info + && !info->isOnline() + && LLAvatarNameCache::get(ids.get(i), &av_name)) { LLUIString offline = LLTrans::getString("offline_message"); - offline.setArg("[FIRST]", first); - offline.setArg("[LAST]", last); + // Use display name only because this user is your friend + offline.setArg("[NAME]", av_name.mDisplayName); im_model.proccessOnlineOfflineNotification(session_id, offline); } } diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index ffa8a16797..c132ac328f 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -41,7 +41,7 @@ #include "llvoicechannel.h" - +class LLAvatarName; class LLFriendObserver; class LLCallDialogManager; class LLIMSpeakerMgr; @@ -458,7 +458,7 @@ private: void processIMTypingCore(const LLIMInfo* im_info, BOOL typing); - static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group); void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); void notifyObserverSessionRemoved(const LLUUID& session_id); @@ -540,6 +540,13 @@ public: static void onStartIM(void* user_data); private: + void setCallerName(const std::string& ui_title, + const std::string& ui_label, + const std::string& call_type); + void onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + const std::string& call_type); + /*virtual*/ void onLifetimeExpired(); void processCallResponse(S32 response); }; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index d9fdc876db..1cd1dcc7f0 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -37,6 +37,7 @@ #include "llagent.h" #include "llagentdata.h" #include "llavataractions.h" +#include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" #include "llcallingcard.h" #include "lldateutil.h" @@ -143,17 +144,13 @@ private: // Is used to determine if "Add friend" option should be enabled in gear menu bool isNotFriend(); - // Callback for gCacheName to look up avatar name - void nameUpdatedCallback( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group); + void onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name); private: LLUUID mAvatarID; // Need avatar name information to spawn friend add request - std::string mAvatarName; + LLAvatarName mAvatarName; // an in-flight request for avatar properties from LLAvatarPropertiesProcessor // is represented by this object LLFetchAvatarData* mPropertiesRequest; @@ -336,6 +333,7 @@ void LLInspectAvatar::requestUpdate() // Clear out old data so it doesn't flash between old and new getChild<LLUICtrl>("user_name")->setValue(""); + getChild<LLUICtrl>("user_slid")->setValue(""); getChild<LLUICtrl>("user_subtitle")->setValue(""); getChild<LLUICtrl>("user_details")->setValue(""); @@ -373,9 +371,9 @@ void LLInspectAvatar::requestUpdate() childSetValue("avatar_icon", LLSD(mAvatarID) ); - gCacheName->get(mAvatarID, FALSE, - boost::bind(&LLInspectAvatar::nameUpdatedCallback, - this, _1, _2, _3, _4)); + LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLInspectAvatar::onAvatarNameCache, + this, _1, _2)); } void LLInspectAvatar::processAvatarData(LLAvatarData* data) @@ -562,7 +560,7 @@ void LLInspectAvatar::updateVolumeSlider() LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - bool is_linden = LLStringUtil::endsWith(mAvatarName, " Linden"); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); mute_btn->setEnabled( !is_linden); mute_btn->setValue( is_muted ); @@ -593,7 +591,7 @@ void LLInspectAvatar::onClickMuteVolume() LLMuteList* mute_list = LLMuteList::getInstance(); bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT); + LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); if (!is_muted) { mute_list->add(mute, LLMute::flagVoiceChat); @@ -612,22 +610,21 @@ void LLInspectAvatar::onVolumeChange(const LLSD& data) LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); } -void LLInspectAvatar::nameUpdatedCallback( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) +void LLInspectAvatar::onAvatarNameCache( + const LLUUID& agent_id, + const LLAvatarName& av_name) { - if (id == mAvatarID) + if (agent_id == mAvatarID) { - mAvatarName = first + " " + last; - childSetValue("user_name", LLSD(mAvatarName) ); + getChild<LLUICtrl>("user_name")->setValue(av_name.mDisplayName); + getChild<LLUICtrl>("user_slid")->setValue(av_name.mUsername); + mAvatarName = av_name; } } void LLInspectAvatar::onClickAddFriend() { - LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName); + LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName.getLegacyName()); closeFloater(); } @@ -695,7 +692,7 @@ void LLInspectAvatar::onClickShare() void LLInspectAvatar::onToggleMute() { - LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT); + LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) { @@ -712,7 +709,7 @@ void LLInspectAvatar::onToggleMute() void LLInspectAvatar::onClickReport() { - LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName); + LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName.getCompleteName()); closeFloater(); } @@ -736,17 +733,17 @@ void LLInspectAvatar::onClickZoomIn() void LLInspectAvatar::onClickFindOnMap() { - gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName); + gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName.mDisplayName); LLFloaterReg::showInstance("world_map"); } bool LLInspectAvatar::enableMute() { - bool is_linden = LLStringUtil::endsWith(mAvatarName, " Linden"); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); bool is_self = mAvatarID == gAgent.getID(); - if (!is_linden && !is_self && !LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName)) + if (!is_linden && !is_self && !LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) { return true; } @@ -758,10 +755,10 @@ bool LLInspectAvatar::enableMute() bool LLInspectAvatar::enableUnmute() { - bool is_linden = LLStringUtil::endsWith(mAvatarName, " Linden"); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); bool is_self = mAvatarID == gAgent.getID(); - if (!is_linden && !is_self && LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName)) + if (!is_linden && !is_self && LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) { return true; } diff --git a/indra/newview/llinspectgroup.cpp b/indra/newview/llinspectgroup.cpp index 7fd7b69021..fa1640c4c7 100644 --- a/indra/newview/llinspectgroup.cpp +++ b/indra/newview/llinspectgroup.cpp @@ -84,9 +84,8 @@ public: // Callback for gCacheName to look up group name // Faster than waiting for group properties to return void nameUpdatedCallback(const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group); + const std::string& name, + bool is_group); // Button/menu callbacks void onClickViewProfile(); @@ -225,21 +224,19 @@ void LLInspectGroup::requestUpdate() mPropertiesRequest = new LLFetchGroupData(mGroupID, this); // Name lookup will be faster out of cache, use that - gCacheName->get(mGroupID, TRUE, + gCacheName->getGroup(mGroupID, boost::bind(&LLInspectGroup::nameUpdatedCallback, - this, _1, _2, _3, _4)); + this, _1, _2, _3)); } void LLInspectGroup::nameUpdatedCallback( const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) + const std::string& name, + bool is_group) { if (id == mGroupID) { - // group names are returned as a first name - childSetValue("group_name", LLSD(first) ); + childSetValue("group_name", LLSD(name) ); } // Otherwise possibly a request for an older inspector, ignore it diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp index 97ff771658..b651306d96 100644 --- a/indra/newview/llinspectremoteobject.cpp +++ b/indra/newview/llinspectremoteobject.cpp @@ -66,12 +66,12 @@ public: private: void update(); - static void nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data); + void onNameCache(const LLUUID& id, const std::string& name, bool is_group); private: LLUUID mObjectID; LLUUID mOwnerID; - std::string mOwner; + std::string mOwnerLegacyName; std::string mSLurl; std::string mName; bool mGroupOwned; @@ -81,7 +81,7 @@ LLInspectRemoteObject::LLInspectRemoteObject(const LLSD& sd) : LLInspect(LLSD()), mObjectID(NULL), mOwnerID(NULL), - mOwner(""), + mOwnerLegacyName(), mSLurl(""), mName(""), mGroupOwned(false) @@ -118,10 +118,11 @@ void LLInspectRemoteObject::onOpen(const LLSD& data) mSLurl = data["slurl"].asString(); // work out the owner's name - mOwner = ""; + mOwnerLegacyName = ""; if (gCacheName) { - gCacheName->get(mOwnerID, mGroupOwned, nameCallback, this); + gCacheName->get(mOwnerID, mGroupOwned, // muting + boost::bind(&LLInspectRemoteObject::onNameCache, this, _1, _2, _3)); } // update the inspector with the current object state @@ -141,7 +142,7 @@ void LLInspectRemoteObject::onClickMap() void LLInspectRemoteObject::onClickBlock() { LLMute::EType mute_type = mGroupOwned ? LLMute::GROUP : LLMute::AGENT; - LLMute mute(mOwnerID, mOwner, mute_type); + LLMute mute(mOwnerID, mOwnerLegacyName, mute_type); LLMuteList::getInstance()->add(mute); LLPanelBlockedList::showPanelAndSelect(mute.mID); closeFloater(); @@ -152,16 +153,10 @@ void LLInspectRemoteObject::onClickClose() closeFloater(); } -//static -void LLInspectRemoteObject::nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data) +void LLInspectRemoteObject::onNameCache(const LLUUID& id, const std::string& name, bool is_group) { - LLInspectRemoteObject *self = (LLInspectRemoteObject*)data; - self->mOwner = first; - if (!last.empty()) - { - self->mOwner += " " + last; - } - self->update(); + mOwnerLegacyName = name; + update(); } void LLInspectRemoteObject::update() @@ -171,7 +166,7 @@ void LLInspectRemoteObject::update() getChild<LLUICtrl>("object_name")->setValue("<nolink>" + mName + "</nolink>"); // show the object's owner - click it to show profile - std::string owner = mOwner; + std::string owner; if (! mOwnerID.isNull()) { if (mGroupOwned) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b7495f7dbe..bad4697d17 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -35,6 +35,7 @@ // external projects #include "lltransfersourceasset.h" +#include "llavatarnamecache.h" // IDEVO #include "llagent.h" #include "llagentcamera.h" @@ -3494,6 +3495,13 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act { std::string callingcard_name; gCacheName->getFullName(item->getCreatorUUID(), callingcard_name); + // IDEVO + LLAvatarName av_name; + if (LLAvatarNameCache::useDisplayNames() + && LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) + { + callingcard_name = av_name.mDisplayName + " (" + av_name.mUsername + ")"; + } LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); if (session_id != LLUUID::null) { diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 2e1c5238d3..06a688d115 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -721,7 +721,8 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) new_item->setCreator(id); std::string avatar_name; // Fetch the currect name - gCacheName->get(id, FALSE, boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), _1, _2, _3)); + gCacheName->get(id, false, + boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), _1, _2, _3)); } } else if (new_item->getType() == LLAssetType::AT_GESTURE) diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index be8b2363ad..d4ebdd768b 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -32,13 +32,18 @@ #include "llviewerprecompiledheaders.h" +#include "lllogchat.h" + +// viewer includes #include "llagent.h" #include "llagentui.h" -#include "lllogchat.h" #include "lltrans.h" #include "llviewercontrol.h" +// library includes +#include "llchat.h" #include "llinstantmessage.h" +#include "llsdserialize.h" #include "llsingleton.h" // for LLSingleton #include <boost/algorithm/string/trim.hpp> @@ -62,12 +67,13 @@ const S32 LOG_RECALL_SIZE = 2048; -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); -const static std::string IM_SEPARATOR(": "); +const std::string IM_TIME("time"); +const std::string IM_TEXT("message"); +const std::string IM_FROM("from"); +const std::string IM_FROM_ID("from_id"); +const std::string IM_SOURCE_TYPE("source_type"); +const static std::string IM_SEPARATOR(": "); const static std::string NEW_LINE("\n"); const static std::string NEW_LINE_SPACE_PREFIX("\n "); const static std::string TWO_SPACES(" "); @@ -190,7 +196,8 @@ std::string LLLogChat::makeLogFileName(std::string filename) { filename = cleanFileName(filename); filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename); - filename += ".txt"; + // new files are llsd notation format + filename += ".llsd"; return filename; } @@ -240,6 +247,18 @@ void LLLogChat::saveHistory(const std::string& filename, const LLUUID& from_id, const std::string& line) { + LLChat chat; + chat.mText = line; + chat.mFromName = from; + chat.mFromID = from_id; + // default to being from an agent + chat.mSourceType = CHAT_SOURCE_AGENT; + saveHistory(filename, chat); +} + +//static +void LLLogChat::saveHistory(const std::string& filename, const LLChat& chat) +{ std::string tmp_filename = filename; LLStringUtil::trim(tmp_filename); if (tmp_filename.empty()) @@ -260,89 +279,27 @@ void LLLogChat::saveHistory(const std::string& filename, LLSD item; if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) - item["time"] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); + item[IM_TIME] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")); - item["from_id"] = from_id; - item["message"] = line; + item[IM_FROM_ID] = chat.mFromID; + item[IM_TEXT] = chat.mText; + item[IM_SOURCE_TYPE] = chat.mSourceType; //adding "Second Life:" for all system messages to make chat log history parsing more reliable - if (from.empty() && from_id.isNull()) + if (chat.mFromName.empty() && chat.mFromID.isNull()) { - item["from"] = SYSTEM_FROM; + item[IM_FROM] = SYSTEM_FROM; } else { - item["from"] = from; + item[IM_FROM] = chat.mFromName; } - file << LLChatLogFormatter(item) << std::endl; + file << LLSDOStreamer<LLSDNotationFormatter>(item) << std::endl; file.close(); } -void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata) -{ - if(!filename.size()) - { - llwarns << "Filename is Empty!" << llendl; - return ; - } - - LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/ - if (!fptr) - { - callback(LOG_EMPTY, LLSD(), userdata); - return; //No previous conversation with this name. - } - else - { - char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ - char *bptr; - S32 len; - bool firstline=TRUE; - - if ( fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END) ) - { //File is smaller than recall size. Get it all. - firstline = FALSE; - if ( fseek(fptr, 0, SEEK_SET) ) - { - fclose(fptr); - return; - } - } - - while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) ) - { - len = strlen(buffer) - 1; /*Flawfinder: ignore*/ - for ( bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; - - if (!firstline) - { - LLSD item; - std::string line(buffer); - std::istringstream iss(line); - - if (!LLChatLogParser::parse(line, item)) - { - item["message"] = line; - callback(LOG_LINE, item, userdata); - } - else - { - callback(LOG_LLSD, item, userdata); - } - } - else - { - firstline = FALSE; - } - } - callback(LOG_END, LLSD(), userdata); - - fclose(fptr); - } -} - void append_to_last_message(std::list<LLSD>& messages, const std::string& line) { if (!messages.size()) return; @@ -352,6 +309,7 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line) messages.back()[IM_TEXT] = im_text; } +// static void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages) { if (file_name.empty()) @@ -415,53 +373,25 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& me fclose(fptr); } -//*TODO mark object's names in a special way so that they will be distinguishable form avatar name -//which are more strict by its nature (only firstname and secondname) -//Example, an object's name can be writen like "Object <actual_object's_name>" -void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const -{ - if (!im.isMap()) - { - llwarning("invalid LLSD type of an instant message", 0); - return; - } - - if (im[IM_TIME].isDefined()) - { - std::string timestamp = im[IM_TIME].asString(); - boost::trim(timestamp); - ostr << '[' << timestamp << ']' << TWO_SPACES; - } - - //*TODO mark object's names in a special way so that they will be distinguishable form avatar name - //which are more strict by its nature (only firstname and secondname) - //Example, an object's name can be writen like "Object <actual_object's_name>" - if (im[IM_FROM].isDefined()) - { - std::string from = im[IM_FROM].asString(); - boost::trim(from); - if (from.size()) - { - ostr << from << IM_SEPARATOR; - } - } - - if (im[IM_TEXT].isDefined()) - { - std::string im_text = im[IM_TEXT].asString(); - - //multilined text will be saved with prepended spaces - boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); - ostr << im_text; - } -} - -bool LLChatLogParser::parse(std::string& raw, LLSD& im) +// static +bool LLChatLogParser::parse(const std::string& raw, LLSD& im) { if (!raw.length()) return false; im = LLSD::emptyMap(); + // In Viewer 2.1 we added UUID to chat/IM logging so we can look up + // display names + if (raw[0] == '{') + { + // ...this is a viewer 2.1, new-style LLSD notation format log + std::istringstream raw_stream(raw); + LLPointer<LLSDParser> parser = new LLSDNotationParser(); + S32 count = parser->parse(raw_stream, im, raw.length()); + // expect several map items per parsed line + return (count != LLSDParser::PARSE_FAILURE); + } + //matching a timestamp boost::match_results<std::string::const_iterator> matches; if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 4290e4bbc0..674f27407e 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -30,10 +30,11 @@ * $/LicenseInfo$ */ - #ifndef LL_LLLOGCHAT_H #define LL_LLLOGCHAT_H +class LLChat; + class LLLogChat { public: @@ -46,49 +47,22 @@ public: }; static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); + + // Log a single line item to the appropriate chat file + static void saveHistory(const std::string& filename, const LLChat& chat); + + // Prefer the above version - it saves more metadata about the item static void saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, const std::string& line); - /** @deprecated @see loadAllHistory() */ - static void loadHistory(const std::string& filename, - void (*callback)(ELogLineType, const LLSD&, void*), - void* userdata); - static void loadAllHistory(const std::string& file_name, std::list<LLSD>& messages); private: static std::string cleanFileName(std::string filename); }; /** - * Formatter for the plain text chat log files - */ -class LLChatLogFormatter -{ -public: - LLChatLogFormatter(const LLSD& im) : mIM(im) {} - virtual ~LLChatLogFormatter() {}; - - friend std::ostream& operator<<(std::ostream& str, const LLChatLogFormatter& formatter) - { - formatter.format(formatter.mIM, str); - return str; - } - -protected: - - /** - * Format an instant message to a stream - * Timestamps and sender names are required - * New lines of multilined messages are prepended with a space - */ - void format(const LLSD& im, std::ostream& ostr) const; - - LLSD mIM; -}; - -/** * Parser for the plain text chat log files */ class LLChatLogParser @@ -106,11 +80,18 @@ public: * * @return false if failed to parse mandatory data - message text */ - static bool parse(std::string& raw, LLSD& im); + static bool parse(const std::string& raw, LLSD& im); protected: LLChatLogParser(); virtual ~LLChatLogParser() {}; }; +// LLSD map lookup constants +extern const std::string IM_TIME; //("time"); +extern const std::string IM_TEXT; //("message"); +extern const std::string IM_FROM; //("from"); +extern const std::string IM_FROM_ID; //("from_id"); +extern const std::string IM_SOURCE_TYPE; //("source_type"); + #endif diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 71604291e1..11ee95ed6d 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -142,6 +142,7 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia requested_options.append("initial-outfit"); requested_options.append("gestures"); + requested_options.append("display_names"); requested_options.append("event_categories"); requested_options.append("event_notifications"); requested_options.append("classified_categories"); diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 7cb192e026..7126914260 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -118,9 +118,8 @@ LLMute::LLMute(const LLUUID& id, const std::string& name, EType type, U32 flags) LLNameValue* lastname = mute_object->getNVPair("LastName"); if (firstname && lastname) { - mName.assign( firstname->getString() ); - mName.append(" "); - mName.append( lastname->getString() ); + mName = LLCacheName::buildFullName( + firstname->getString(), lastname->getString()); } mType = mute_object->isAvatar() ? AGENT : OBJECT; } @@ -416,7 +415,7 @@ void LLMuteList::updateRemove(const LLMute& mute) gAgent.sendReliableMessage(); } -void notify_automute_callback(const LLUUID& agent_id, const std::string& first_name, const std::string& last_name, BOOL is_group, LLMuteList::EAutoReason reason) +void notify_automute_callback(const LLUUID& agent_id, const std::string& full_name, bool is_group, LLMuteList::EAutoReason reason) { std::string notif_name; switch (reason) @@ -434,8 +433,7 @@ void notify_automute_callback(const LLUUID& agent_id, const std::string& first_n } LLSD args; - args["FIRST"] = first_name; - args["LAST"] = last_name; + args["NAME"] = full_name; LLNotificationPtr notif_ptr = LLNotifications::instance().add(notif_name, args, LLSD()); if (notif_ptr) @@ -450,7 +448,7 @@ void notify_automute_callback(const LLUUID& agent_id, const std::string& first_n } -BOOL LLMuteList::autoRemove(const LLUUID& agent_id, const EAutoReason reason, const std::string& first_name, const std::string& last_name) +BOOL LLMuteList::autoRemove(const LLUUID& agent_id, const EAutoReason reason) { BOOL removed = FALSE; @@ -460,24 +458,17 @@ BOOL LLMuteList::autoRemove(const LLUUID& agent_id, const EAutoReason reason, co removed = TRUE; remove(automute); - if (first_name.empty() && last_name.empty()) - { - std::string cache_first, cache_last; - if (gCacheName->getName(agent_id, cache_first, cache_last)) + std::string full_name; + if (gCacheName->getFullName(agent_id, full_name)) { // name in cache, call callback directly - notify_automute_callback(agent_id, cache_first, cache_last, FALSE, reason); + notify_automute_callback(agent_id, full_name, false, reason); } else { // not in cache, lookup name from cache - gCacheName->get(agent_id, FALSE, boost::bind(¬ify_automute_callback, _1, _2, _3, _4, reason)); - } - } - else - { - // call callback directly - notify_automute_callback(agent_id, first_name, last_name, FALSE, reason); + gCacheName->get(agent_id, false, + boost::bind(¬ify_automute_callback, _1, _2, _3, reason)); } } diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 79b556bdbb..ad4fd2345d 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -69,7 +69,7 @@ public: public: LLUUID mID; // agent or object id - std::string mName; // agent or object name + std::string mName; // agent or object name, does not store last name "Resident" EType mType; // needed for UI display of existing mutes U32 mFlags; // flags pertaining to this mute entry }; @@ -102,7 +102,7 @@ public: // Remove both normal and legacy mutes, for any or all properties. BOOL remove(const LLMute& mute, U32 flags = 0); - BOOL autoRemove(const LLUUID& agent_id, const EAutoReason reason, const std::string& first_name = LLStringUtil::null, const std::string& last_name = LLStringUtil::null); + BOOL autoRemove(const LLUUID& agent_id, const EAutoReason reason); // Name is required to test against legacy text-only mutes. BOOL isMuted(const LLUUID& id, const std::string& name = LLStringUtil::null, U32 flags = 0) const; diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp index cd810b9793..da3e95e947 100644 --- a/indra/newview/llnamebox.cpp +++ b/indra/newview/llnamebox.cpp @@ -87,26 +87,15 @@ void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group) setText(mInitialValue); } -void LLNameBox::refresh(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group) +void LLNameBox::refresh(const LLUUID& id, const std::string& full_name, bool is_group) { if (id == mNameID) { - std::string name; - if (!is_group) - { - name = firstname + " " + lastname; - } - else - { - name = firstname; - } - setName(name, is_group); + setName(full_name, is_group); } } -void LLNameBox::refreshAll(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group) +void LLNameBox::refreshAll(const LLUUID& id, const std::string& full_name, bool is_group) { std::set<LLNameBox*>::iterator it; for (it = LLNameBox::sInstances.begin(); @@ -114,7 +103,7 @@ void LLNameBox::refreshAll(const LLUUID& id, const std::string& firstname, ++it) { LLNameBox* box = *it; - box->refresh(id, firstname, lastname, is_group); + box->refresh(id, full_name, is_group); } } diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h index 48b54faec8..2fe8990653 100644 --- a/indra/newview/llnamebox.h +++ b/indra/newview/llnamebox.h @@ -59,10 +59,9 @@ public: void setNameID(const LLUUID& name_id, BOOL is_group); - void refresh(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void refresh(const LLUUID& id, const std::string& full_name, bool is_group); - static void refreshAll(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group); + static void refreshAll(const LLUUID& id, const std::string& full_name, bool is_group); protected: LLNameBox (const Params&); diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index 65601da7da..0c704a1f56 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -81,26 +81,15 @@ void LLNameEditor::setNameID(const LLUUID& name_id, BOOL is_group) setText(name); } -void LLNameEditor::refresh(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group) +void LLNameEditor::refresh(const LLUUID& id, const std::string& full_name, bool is_group) { if (id == mNameID) { - std::string name; - if (!is_group) - { - name = firstname + " " + lastname; - } - else - { - name = firstname; - } - setText(name); + setText(full_name); } } -void LLNameEditor::refreshAll(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group) +void LLNameEditor::refreshAll(const LLUUID& id, const std::string& full_name, bool is_group) { std::set<LLNameEditor*>::iterator it; for (it = LLNameEditor::sInstances.begin(); @@ -108,7 +97,7 @@ void LLNameEditor::refreshAll(const LLUUID& id, const std::string& firstname, ++it) { LLNameEditor* box = *it; - box->refresh(id, firstname, lastname, is_group); + box->refresh(id, full_name, is_group); } } diff --git a/indra/newview/llnameeditor.h b/indra/newview/llnameeditor.h index 99e03a1166..a75c492aca 100644 --- a/indra/newview/llnameeditor.h +++ b/indra/newview/llnameeditor.h @@ -65,10 +65,9 @@ public: void setNameID(const LLUUID& name_id, BOOL is_group); - void refresh(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void refresh(const LLUUID& id, const std::string& full_name, bool is_group); - static void refreshAll(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group); + static void refreshAll(const LLUUID& id, const std::string& full_name, bool is_group); // Take/return agent UUIDs diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index d605d4430e..a2450fcdd2 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -36,6 +36,7 @@ #include <boost/tokenizer.hpp> +#include "llavatarnamecache.h" #include "llcachename.h" #include "llfloaterreg.h" #include "llinventory.h" @@ -58,7 +59,8 @@ void LLNameListCtrl::NameTypeNames::declareValues() LLNameListCtrl::Params::Params() : name_column(""), - allow_calling_card_drop("allow_calling_card_drop", false) + allow_calling_card_drop("allow_calling_card_drop", false), + short_names("short_names", false) { name = "name_list"; } @@ -67,7 +69,8 @@ LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p) : LLScrollListCtrl(p), mNameColumnIndex(p.name_column.column_index), mNameColumn(p.name_column.column_name), - mAllowCallingCardDrop(p.allow_calling_card_drop) + mAllowCallingCardDrop(p.allow_calling_card_drop), + mShortNames(p.short_names) {} // public @@ -297,10 +300,20 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( break; case INDIVIDUAL: { - std::string name; - if (gCacheName->getFullName(id, name)) + LLAvatarName av_name; + if (LLAvatarNameCache::get(id, &av_name)) { - fullname = name; + if (mShortNames) + fullname = av_name.mDisplayName; + else + fullname = av_name.getCompleteName(); + } + else + { + // ...schedule a callback + LLAvatarNameCache::get(id, + boost::bind(&LLNameListCtrl::onAvatarNameCache, + this, _1, _2)); } break; } @@ -355,34 +368,25 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) } } -// public -void LLNameListCtrl::refresh(const LLUUID& id, const std::string& first, - const std::string& last, BOOL is_group) +void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) { - //llinfos << "LLNameListCtrl::refresh " << id << " '" << first << " " - // << last << "'" << llendl; - - std::string fullname; - if (!is_group) - { - fullname = first + " " + last; - } + std::string name; + if (mShortNames) + name = av_name.mDisplayName; else - { - fullname = first; - } + name = av_name.getCompleteName(); - // TODO: scan items for that ID, fix if necessary item_list::iterator iter; for (iter = getItemList().begin(); iter != getItemList().end(); iter++) { LLScrollListItem* item = *iter; - if (item->getUUID() == id) + if (item->getUUID() == agent_id) { LLScrollListCell* cell = item->getColumn(mNameColumnIndex); if (cell) { - cell->setValue(fullname); + cell->setValue(name); } } } @@ -391,19 +395,6 @@ void LLNameListCtrl::refresh(const LLUUID& id, const std::string& first, } -// static -void LLNameListCtrl::refreshAll(const LLUUID& id, const std::string& first, - const std::string& last, BOOL is_group) -{ - LLInstanceTrackerScopedGuard guard; - LLInstanceTracker<LLNameListCtrl>::instance_iter it; - for (it = guard.beginInstances(); it != guard.endInstances(); ++it) - { - LLNameListCtrl& ctrl = *it; - ctrl.refresh(id, first, last, is_group); - } -} - void LLNameListCtrl::updateColumns() { LLScrollListCtrl::updateColumns(); diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index 1c26ee5db4..54237f4305 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -37,6 +37,7 @@ #include "llscrolllistctrl.h" +class LLAvatarName; class LLNameListCtrl : public LLScrollListCtrl, protected LLInstanceTracker<LLNameListCtrl> @@ -80,6 +81,7 @@ public: { Optional<NameColumn> name_column; Optional<bool> allow_calling_card_drop; + Optional<bool> short_names; Params(); }; @@ -105,11 +107,6 @@ public: void removeNameItem(const LLUUID& agent_id); - void refresh(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); - - static void refreshAll(const LLUUID& id, const std::string& firstname, - const std::string& lastname, BOOL is_group); - // LLView interface /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, @@ -124,11 +121,13 @@ public: /*virtual*/ void mouseOverHighlightNthItem( S32 index ); private: void showInspector(const LLUUID& avatar_id, bool is_group); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); private: S32 mNameColumnIndex; std::string mNameColumn; BOOL mAllowCallingCardDrop; + bool mShortNames; // display name only, no SLID }; /** diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index f1c13de8bb..631d1fcac7 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -62,12 +62,6 @@ static const S32 RESIZE_BAR_THICKNESS = 3; -const static std::string IM_TIME("time"); -const static std::string IM_TEXT("message"); -const static std::string IM_FROM("from"); -const static std::string IM_FROM_ID("from_id"); - - LLNearbyChat::LLNearbyChat(const LLSD& key) : LLDockableFloater(NULL, false, false, key) ,mChatHistory(NULL) @@ -211,7 +205,7 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) { - LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText); + LLLogChat::saveHistory("chat", chat); } } @@ -301,8 +295,12 @@ void LLNearbyChat::loadHistory() const LLSD& msg = *it; std::string from = msg[IM_FROM]; - LLUUID from_id = LLUUID::null; - if (msg[IM_FROM_ID].isUndefined()) + LLUUID from_id; + if (msg[IM_FROM_ID].isDefined()) + { + from_id = msg[IM_FROM_ID].asUUID(); + } + else { gCacheName->getUUID(from, from_id); } @@ -314,11 +312,14 @@ void LLNearbyChat::loadHistory() chat.mTimeStr = msg[IM_TIME].asString(); chat.mChatStyle = CHAT_STYLE_HISTORY; - chat.mSourceType = CHAT_SOURCE_AGENT; - if (from_id.isNull() && SYSTEM_FROM == from) + if (msg.has(IM_SOURCE_TYPE)) + { + S32 source_type = msg[IM_SOURCE_TYPE].asInteger(); + chat.mSourceType = (EChatSourceType)source_type; + } + else if (from_id.isNull() && SYSTEM_FROM == from) { chat.mSourceType = CHAT_SOURCE_SYSTEM; - } else if (from_id.isNull()) { diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index a8dee8a24a..67b238ad58 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -37,6 +37,7 @@ // Library includes (should move below) #include "indra_constants.h" +#include "llavatarnamecache.h" #include "llmath.h" #include "llfloaterreg.h" #include "llfocusmgr.h" @@ -555,11 +556,14 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask ) // mToolTipMsg = "[AGENT][REGION](Double-click to open Map)" + bool have_agent = false; LLStringUtil::format_map_t args; - std::string fullname; - if(mClosestAgentToCursor.notNull() && gCacheName->getFullName(mClosestAgentToCursor, fullname)) + LLAvatarName av_name; + if(mClosestAgentToCursor.notNull() + && LLAvatarNameCache::get(mClosestAgentToCursor, &av_name)) { - args["[AGENT]"] = fullname + "\n"; + args["[AGENT]"] = av_name.getCompleteName() + "\n"; + have_agent = true; } else { @@ -567,7 +571,7 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask ) } LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( viewPosToGlobal( x, y ) ); - if( region ) + if( region && !have_agent) { args["[REGION]"] = region->getName() + "\n"; } diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 95b946f307..deb0cb6cfa 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -113,8 +113,11 @@ void LLSysHandler::removeExclusiveNotifications(const LLNotificationPtr& notif) } const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"), - REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), OBJECT_GIVE_ITEM( - "ObjectGiveItem"), PAYMENT_RECIVED("PaymentRecived"), + REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), + OBJECT_GIVE_ITEM("ObjectGiveItem"), + OBJECT_GIVE_ITEM_UNKNOWN_USER("ObjectGiveItemUnknownUser"), + PAYMENT_RECEIVED("PaymentReceived"), + PAYMENT_SENT("PaymentSent"), ADD_FRIEND_WITH_MESSAGE("AddFriendWithMessage"), USER_GIVE_ITEM("UserGiveItem"), INVENTORY_ACCEPTED("InventoryAccepted"), @@ -136,7 +139,8 @@ bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) { return GRANTED_MODIFY_RIGHTS == notification->getName() || REVOKED_MODIFY_RIGHTS == notification->getName() - || PAYMENT_RECIVED == notification->getName() + || PAYMENT_RECEIVED == notification->getName() + || PAYMENT_SENT == notification->getName() || OFFER_FRIENDSHIP == notification->getName() || FRIENDSHIP_OFFERED == notification->getName() || FRIENDSHIP_ACCEPTED == notification->getName() @@ -317,34 +321,35 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification) logToIMP2P(notification, false); } +void log_name_callback(const std::string& full_name, const std::string& from_name, + const std::string& message, const LLUUID& from_id) + +{ + LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, full_name, from_name, message, + from_id, LLUUID()); +} + // static void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - const std::string& session_name = notification->getPayload().has( - "SESSION_NAME") ? notification->getPayload()["SESSION_NAME"].asString() : name; - // don't create IM p2p session with objects, it's necessary condition to log if (notification->getName() != OBJECT_GIVE_ITEM) { LLUUID from_id = notification->getPayload()["from_id"]; - //there still appears a log history file with weird name " .txt" - if (" " == session_name || "{waiting}" == session_name || "{nobody}" == session_name) + if (from_id.isNull()) { - llwarning("Weird session name (" + session_name + ") for notification " + notification->getName(), 666) + llwarns << " from_id for notification " << notification->getName() << " is null " << llendl; + return; } if(to_file_only) { - logToIM(IM_NOTHING_SPECIAL, session_name, "", notification->getMessage(), - LLUUID(), LLUUID()); + gCacheName->get(from_id, false, boost::bind(&log_name_callback, _2, "", notification->getMessage(), LLUUID())); } else { - logToIM(IM_NOTHING_SPECIAL, session_name, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), - from_id, LLUUID()); + gCacheName->get(from_id, false, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), from_id)); } } } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 0b31ffc9a0..3b43d24ea0 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -50,7 +50,7 @@ #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llvoiceclient.h" -#include "llnamebox.h" +#include "lltextbox.h" #include "lltrans.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -224,13 +224,8 @@ void LLPanelAvatarNotes::rightsConfirmationCallback(const LLSD& notification, void LLPanelAvatarNotes::confirmModifyRights(bool grant, S32 rights) { - std::string first, last; LLSD args; - if (gCacheName->getName(getAvatarId(), first, last)) - { - args["FIRST_NAME"] = first; - args["LAST_NAME"] = last; - } + args["NAME"] = LLSLURL("agent", getAvatarId(), "displayname").getSLURLString(); if (grant) { @@ -574,8 +569,7 @@ void LLPanelAvatarProfile::resetData() childSetValue("homepage_edit",LLStringUtil::null); childSetValue("register_date",LLStringUtil::null); childSetValue("acc_status_text",LLStringUtil::null); - childSetTextArg("partner_text", "[FIRST]", LLStringUtil::null); - childSetTextArg("partner_text", "[LAST]", LLStringUtil::null); + childSetValue("partner_text", LLStringUtil::null); } void LLPanelAvatarProfile::processProperties(void* data, EAvatarProcessorType type) @@ -666,15 +660,14 @@ void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) void LLPanelAvatarProfile::fillPartnerData(const LLAvatarData* avatar_data) { - LLNameBox* name_box = getChild<LLNameBox>("partner_text"); + LLTextBox* partner_text = getChild<LLTextBox>("partner_text"); if (avatar_data->partner_id.notNull()) { - name_box->setNameID(avatar_data->partner_id, FALSE); + partner_text->setText(LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString()); } else { - name_box->setNameID(LLUUID::null, FALSE); - name_box->setText(getString("no_partner_text")); + partner_text->setText(getString("no_partner_text")); } } diff --git a/indra/newview/llpanelavatartag.cpp b/indra/newview/llpanelavatartag.cpp index 7563cc7f61..173fb851ce 100644 --- a/indra/newview/llpanelavatartag.cpp +++ b/indra/newview/llpanelavatartag.cpp @@ -86,7 +86,7 @@ void LLPanelAvatarTag::setAvatarId(const LLUUID& avatar_id) { mIcon->setValue(avatar_id); } - setName(std::string(mIcon->getFirstName()+ " "+ mIcon->getLastName())); + setName(std::string(mIcon->getFullName())); } boost::signals2::connection LLPanelAvatarTag::setLeftButtonClickCallback( diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index c72f0f8012..c3c0af6d9e 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -35,6 +35,7 @@ #include "llpanelblockedlist.h" // library include +#include "llavatarname.h" #include "llfloater.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" @@ -186,10 +187,10 @@ void LLPanelBlockedList::onBlockByNameClick() LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); } -void LLPanelBlockedList::callbackBlockPicked(const std::vector<std::string>& names, const uuid_vec_t& ids) +void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (names.empty() || ids.empty()) return; - LLMute mute(ids[0], names[0], LLMute::AGENT); + LLMute mute(ids[0], names[0].getLegacyName(), LLMute::AGENT); LLMuteList::getInstance()->add(mute); showPanelAndSelect(mute.mID); } diff --git a/indra/newview/llpanelblockedlist.h b/indra/newview/llpanelblockedlist.h index a100577e43..88c1edb1f8 100644 --- a/indra/newview/llpanelblockedlist.h +++ b/indra/newview/llpanelblockedlist.h @@ -42,7 +42,8 @@ // class LLLineEditor; // class LLMessageSystem; // class LLUUID; - class LLScrollListCtrl; +class LLAvatarName; +class LLScrollListCtrl; class LLPanelBlockedList : public LLPanel, public LLMuteListObserver @@ -78,7 +79,7 @@ private: void onPickBtnClick(); void onBlockByNameClick(); - void callbackBlockPicked(const std::vector<std::string>& names, const uuid_vec_t& ids); + void callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); static void callbackBlockByName(const std::string& text); private: diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 555248e31a..d0dd4785ed 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -46,7 +46,6 @@ #include "llavataractions.h" #include "llgroupactions.h" #include "lllineeditor.h" -#include "llnamebox.h" #include "llnamelistctrl.h" #include "llnotificationsutil.h" #include "llscrolllistitem.h" @@ -213,7 +212,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) { mInsignia->setCommitCallback(onCommitAny, this); } - mFounderName = getChild<LLNameBox>("founder_name"); + mFounderName = getChild<LLTextBox>("founder_name"); mGroupNameEditor = panel_group->getChild<LLLineEditor>("group_name_editor"); @@ -646,7 +645,7 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mEditCharter) mEditCharter->setEnabled(mAllowEdit && can_change_ident); if (mGroupNameEditor) mGroupNameEditor->setVisible(FALSE); - if (mFounderName) mFounderName->setNameID(gdatap->mFounderID,FALSE); + if (mFounderName) mFounderName->setText(LLSLURL("agent", gdatap->mFounderID, "inspect").getSLURLString()); if (mInsignia) { if (gdatap->mInsigniaID.notNull()) diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index 6f4fa994da..066c002634 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -43,7 +43,6 @@ class LLButton; class LLNameListCtrl; class LLCheckBoxCtrl; class LLComboBox; -class LLNameBox; class LLSpinCtrl; class LLPanelGroupGeneral : public LLPanelGroupTab @@ -98,7 +97,7 @@ private: // Group information (include any updates in updateChanged) LLLineEditor *mGroupNameEditor; - LLNameBox *mFounderName; + LLTextBox *mFounderName; LLTextureCtrl *mInsignia; LLTextEditor *mEditCharter; diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index 11d3768a3d..a28e0c2123 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -34,6 +34,7 @@ #include "llpanelgroupinvite.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llfloateravatarpicker.h" #include "llbutton.h" #include "llcallingcard.h" @@ -68,9 +69,13 @@ public: static void callbackClickAdd(void* userdata); static void callbackClickRemove(void* userdata); static void callbackSelect(LLUICtrl* ctrl, void* userdata); - static void callbackAddUsers(const std::vector<std::string>& names, - const uuid_vec_t& agent_ids, + static void callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data); + + static void onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + void* user_data); + bool inviteOwnerCallback(const LLSD& notification, const LLSD& response); public: @@ -293,7 +298,7 @@ void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata) LLFloater* parentp; parentp = gFloaterView->getParentFloater(panelp); - parentp->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(impl::callbackAddUsers, _1, _2, + parentp->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), TRUE)); } @@ -359,16 +364,38 @@ void LLPanelGroupInvite::impl::callbackClickOK(void* userdata) if ( selfp ) selfp->submitInvitations(); } + + //static -void LLPanelGroupInvite::impl::callbackAddUsers(const std::vector<std::string>& names, - const uuid_vec_t& ids, - void* user_data) +void LLPanelGroupInvite::impl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data) +{ + std::vector<std::string> names; + for (S32 i = 0; i < (S32)agent_ids.size(); i++) + { + LLAvatarNameCache::get(agent_ids[i], + boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data)); + } + +} + +void LLPanelGroupInvite::impl::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + void* user_data) { impl* selfp = (impl*) user_data; - if ( selfp) selfp->addUsers(names, ids); + if (selfp) + { + std::vector<std::string> names; + uuid_vec_t agent_ids; + agent_ids.push_back(agent_id); + names.push_back(av_name.getCompleteName()); + + selfp->addUsers(names, agent_ids); + } } + LLPanelGroupInvite::LLPanelGroupInvite(const LLUUID& group_id) : LLPanel(), mImplementation(new impl(group_id)), @@ -404,16 +431,18 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) std::vector<std::string> names; for (S32 i = 0; i < (S32)agent_ids.size(); i++) { + std::string fullname; LLUUID agent_id = agent_ids[i]; LLViewerObject* dest = gObjectList.findObject(agent_id); - std::string fullname; if(dest && dest->isAvatar()) { LLNameValue* nvfirst = dest->getNVPair("FirstName"); LLNameValue* nvlast = dest->getNVPair("LastName"); if(nvfirst && nvlast) { - fullname = std::string(nvfirst->getString()) + " " + std::string(nvlast->getString()); + fullname = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); + } if (!fullname.empty()) { @@ -436,8 +465,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) { // actually it should happen, just in case gCacheName->get(LLUUID(agent_id), false, boost::bind( - &LLPanelGroupInvite::addUserCallback, this, _1, _2, - _3)); + &LLPanelGroupInvite::addUserCallback, this, _1, _2)); // for this special case! //when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence // removed id will be added in callback @@ -453,16 +481,16 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) mImplementation->addUsers(names, agent_ids); } -void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const std::string& first_name, const std::string& last_name) +void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const std::string& full_name) { std::vector<std::string> names; uuid_vec_t agent_ids; - std::string full_name = first_name + " " + last_name; agent_ids.push_back(id); - names.push_back(first_name + " " + last_name); + names.push_back(full_name); mImplementation->addUsers(names, agent_ids); } + void LLPanelGroupInvite::draw() { LLPanel::draw(); diff --git a/indra/newview/llpanelgroupinvite.h b/indra/newview/llpanelgroupinvite.h index 2ed443ed46..520107c33a 100644 --- a/indra/newview/llpanelgroupinvite.h +++ b/indra/newview/llpanelgroupinvite.h @@ -46,7 +46,7 @@ public: /** * this callback is being used to add a user whose fullname isn't been loaded before invoking of addUsers(). */ - void addUserCallback(const LLUUID& id, const std::string& first_name, const std::string& last_name); + void addUserCallback(const LLUUID& id, const std::string& full_name); void clear(); void update(); diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 42ff514f09..4e8f0b5448 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -36,6 +36,7 @@ #include "llview.h" +#include "llavatarnamecache.h" #include "llinventory.h" #include "llviewerinventory.h" #include "llinventorydefines.h" @@ -547,6 +548,12 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) msg->getU8("Data","AssetType",asset_type,i); msg->getU32("Data","Timestamp",timestamp,i); + // we only have the legacy name here, convert it to a username + if (LLAvatarNameCache::useDisplayNames()) + { + name = LLCacheName::buildUsername(name); + } + LLSD row; row["id"] = id; diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index b356fe6bfd..7adabba2de 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1113,11 +1113,7 @@ void LLPanelGroupMembersSubTab::sendEjectNotifications(const LLUUID& group_id, c for (uuid_vec_t::const_iterator i = selected_members.begin(); i != selected_members.end(); ++i) { LLSD args; - std::string name; - - gCacheName->getFullName(*i, name); - - args["AVATAR_NAME"] = name; + args["AVATAR_NAME"] = LLSLURL("agent", *i, "displayname").getSLURLString(); args["GROUP_NAME"] = group_data->mName; LLNotifications::instance().add(LLNotification::Params("EjectAvatarFromGroup").substitutions(args)); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 709bb83fe4..89eef8b8d3 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -185,7 +185,7 @@ void LLPanelIMControlPanel::onViewProfileButtonClicked() void LLPanelIMControlPanel::onAddFriendButtonClicked() { LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - std::string full_name = avatar_icon->getFirstName() + " " + avatar_icon->getLastName(); + std::string full_name = avatar_icon->getFullName(); LLAvatarActions::requestFriendshipDialog(mAvatarID, full_name); } @@ -227,6 +227,15 @@ void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) childSetEnabled("share_btn", FALSE); childSetEnabled("teleport_btn", FALSE); childSetEnabled("pay_btn", FALSE); + + getChild<LLTextBox>("avatar_name")->setValue(im_session->mName); + getChild<LLTextBox>("avatar_name")->setToolTip(im_session->mName); + } + else + { + // If the participant is an avatar, fetch the currect name + gCacheName->get(mAvatarID, false, + boost::bind(&LLPanelIMControlPanel::onNameCache, this, _1, _2, _3)); } } @@ -242,6 +251,16 @@ void LLPanelIMControlPanel::changed(U32 mask) } } +void LLPanelIMControlPanel::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group) +{ + if ( id == mAvatarID ) + { + std::string avatar_name = full_name; + getChild<LLTextBox>("avatar_name")->setValue(avatar_name); + getChild<LLTextBox>("avatar_name")->setToolTip(avatar_name); + } +} + LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): mParticipantList(NULL) { diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index ce8fc58e56..45f4b95903 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -89,6 +89,9 @@ public: // LLFriendObserver trigger virtual void changed(U32 mask); +protected: + void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group); + private: void onViewProfileButtonClicked(); void onAddFriendButtonClicked(); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 4ffd43cb0f..f6c70c82da 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -45,6 +45,7 @@ #include "llagent.h" #include "llagentui.h" #include "lllandmarkactions.h" +#include "llslurl.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -231,13 +232,10 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) ////////////////// if (pItem->getCreatorUUID().notNull()) { - std::string name; + // IDEVO LLUUID creator_id = pItem->getCreatorUUID(); - if (!gCacheName->getFullName(creator_id, name)) - { - gCacheName->get(creator_id, FALSE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mCreator, _2, _3)); - } + std::string name = + LLSLURL("agent", creator_id, "inspect").getSLURLString(); mCreator->setText(name); } else @@ -254,20 +252,12 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) if (perm.isGroupOwned()) { LLUUID group_id = perm.getGroup(); - if (!gCacheName->getGroupName(group_id, name)) - { - gCacheName->get(group_id, TRUE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mOwner, _2, _3)); - } + name = LLSLURL("group", group_id, "inspect").getSLURLString(); } else { LLUUID owner_id = perm.getOwner(); - if (!gCacheName->getFullName(owner_id, name)) - { - gCacheName->get(owner_id, FALSE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mOwner, _2, _3)); - } + name = LLSLURL("agent", owner_id, "inspect").getSLURLString(); } mOwner->setText(name); } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 1cb0f4601b..aa8dd4ef45 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -211,7 +211,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, } #if !USE_VIEWER_AUTH - childSetPrevalidate("username_edit", LLTextValidate::validateASCIIPrintableNoPipe); getChild<LLLineEditor>("password_edit")->setKeystrokeCallback(onPassKey, this); // change z sort of clickable text to be behind buttons @@ -514,8 +513,16 @@ void LLPanelLogin::setFields(LLPointer<LLCredential> credential, LLSD identifier = credential->getIdentifier(); if((std::string)identifier["type"] == "agent") { - sInstance->childSetText("username_edit", (std::string)identifier["first_name"] + " " + - (std::string)identifier["last_name"]); + std::string firstname = identifier["first_name"].asString(); + std::string lastname = identifier["last_name"].asString(); + std::string login_id = firstname; + if (!lastname.empty() && lastname != "Resident") + { + // support traditional First Last name SLURLs + login_id += " "; + login_id += lastname; + } + sInstance->childSetText("username_edit", login_id); } else if((std::string)identifier["type"] == "account") { @@ -579,7 +586,8 @@ void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, LL_INFOS2("Credentials", "Authentication") << "retrieving username:" << username << LL_ENDL; // determine if the username is a first/last form or not. size_t separator_index = username.find_first_of(' '); - if (separator_index == username.npos) + if (separator_index == username.npos + && !LLGridManager::getInstance()->isSystemGrid()) { LL_INFOS2("Credentials", "Authentication") << "account: " << username << LL_ENDL; // single username, so this is a 'clear' identifier @@ -596,9 +604,23 @@ void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, } else { + // Be lenient in terms of what separators we allow for two-word names + // and allow legacy users to login with firstname.lastname + separator_index = username.find_first_of(" ._"); std::string first = username.substr(0, separator_index); - std::string last = username.substr(separator_index, username.npos); + std::string last; + if (separator_index != username.npos) + { + last = username.substr(separator_index+1, username.npos); LLStringUtil::trim(last); + } + else + { + // ...on Linden grids, single username users as considered to have + // last name "Resident" + // *TODO: Make login.cgi support "account_name" like above + last = "Resident"; + } if (last.find_first_of(' ') == last.npos) { diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp index 3f620869e0..0fc2a78bcd 100644 --- a/indra/newview/llpanelme.cpp +++ b/indra/newview/llpanelme.cpp @@ -32,17 +32,25 @@ #include "llviewerprecompiledheaders.h" +#include "llpanelme.h" + +// Viewer includes #include "llpanelprofile.h" #include "llavatarconstants.h" -#include "llpanelme.h" #include "llagent.h" #include "llagentcamera.h" #include "llagentwearables.h" -#include "lliconctrl.h" #include "llsidetray.h" +#include "llviewercontrol.h" +#include "llviewerdisplayname.h" + +// Linden libraries +#include "llavatarnamecache.h" // IDEVO +#include "lliconctrl.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" // IDEVO #include "lltabcontainer.h" #include "lltexturectrl.h" -#include "llviewercontrol.h" #define PICKER_SECOND_LIFE "2nd_life_pic" #define PICKER_FIRST_LIFE "real_world_pic" @@ -180,6 +188,13 @@ void LLPanelMyProfileEdit::onOpen(const LLSD& key) // Disable editing until data is loaded, or edited fields will be overwritten when data // is loaded. enableEditing(false); + + // Only allow changing name if this region/grid supports it + bool use_display_names = LLAvatarNameCache::useDisplayNames(); + LLUICtrl* set_name = getChild<LLUICtrl>("set_name"); + set_name->setVisible(use_display_names); + set_name->setEnabled(use_display_names); + LLPanelMyProfile::onOpen(getAvatarId()); } @@ -213,13 +228,14 @@ void LLPanelMyProfileEdit::processProfileProperties(const LLAvatarData* avatar_d childSetValue("show_in_search_checkbox", (BOOL)(avatar_data->flags & AVATAR_ALLOW_PUBLISH)); - std::string first, last; - BOOL found = gCacheName->getName(avatar_data->avatar_id, first, last); - if (found) - { - childSetTextArg("name_text", "[FIRST]", first); - childSetTextArg("name_text", "[LAST]", last); - } + LLAvatarNameCache::get(avatar_data->avatar_id, + boost::bind(&LLPanelMyProfileEdit::onNameCache, this, _1, _2)); +} + +void LLPanelMyProfileEdit::onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + getChild<LLUICtrl>("user_name")->setValue( av_name.mDisplayName ); + getChild<LLUICtrl>("user_slid")->setValue( av_name.mUsername ); } BOOL LLPanelMyProfileEdit::postBuild() @@ -229,6 +245,9 @@ BOOL LLPanelMyProfileEdit::postBuild() childSetTextArg("partner_edit_link", "[URL]", getString("partner_edit_link_url")); childSetTextArg("my_account_link", "[URL]", getString("my_account_link_url")); + getChild<LLUICtrl>("set_name")->setCommitCallback( + boost::bind(&LLPanelMyProfileEdit::onClickSetName, this)); + return LLPanelAvatarProfile::postBuild(); } /** @@ -256,8 +275,10 @@ void LLPanelMyProfileEdit::resetData() { LLPanelMyProfile::resetData(); - childSetTextArg("name_text", "[FIRST]", LLStringUtil::null); - childSetTextArg("name_text", "[LAST]", LLStringUtil::null); + //childSetTextArg("name_text", "[FIRST]", LLStringUtil::null); + //childSetTextArg("name_text", "[LAST]", LLStringUtil::null); + getChild<LLUICtrl>("user_name")->setValue( LLSD() ); + getChild<LLUICtrl>("user_slid")->setValue( LLSD() ); } void LLPanelMyProfileEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl) @@ -269,6 +290,134 @@ void LLPanelMyProfileEdit::onTexturePickerMouseLeave(LLUICtrl* ctrl) mTextureEditIconMap[ctrl->getName()]->setVisible(FALSE); } +void LLPanelMyProfileEdit::onCacheSetName(bool success, + const std::string& reason, + const LLSD& content) +{ + if (success) + { + // Inform the user that the change took place, but will take a while + // to percolate. + LLSD args; + args["DISPLAY_NAME"] = content["display_name"]; + LLNotificationsUtil::add("SetDisplayNameSuccess", args); + + // Re-fetch my name, as it may have been sanitized by the service + LLAvatarNameCache::get(getAvatarId(), + boost::bind(&LLPanelMyProfileEdit::onNameCache, this, _1, _2)); + return; + } + + // Request failed, notify the user + std::string error_tag = content["error_tag"].asString(); + llinfos << "set name failure error_tag " << error_tag << llendl; + + // We might have a localized string for this message + // error_args will usually be empty from the server. + if (!error_tag.empty() + && LLNotifications::getInstance()->templateExists(error_tag)) + { + LLNotificationsUtil::add(error_tag); + return; + } + + // The server error might have a localized message for us + std::string lang_code = LLUI::getLanguage(); + LLSD error_desc = content["error_description"]; + if (error_desc.has( lang_code )) + { + LLSD args; + args["MESSAGE"] = error_desc[lang_code].asString(); + LLNotificationsUtil::add("GenericAlert", args); + return; + } + + // No specific error, throw a generic one + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); +} + +void LLPanelMyProfileEdit::onDialogSetName(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0 || option == 1) + { + LLUUID agent_id = notification["payload"]["agent_id"]; + if (agent_id.isNull()) return; + + std::string display_name_utf8; + if (option == 0) + { + // user gave us a name + display_name_utf8 = response["display_name"].asString(); + } + else + { + // reset back to People API default + display_name_utf8 = ""; + } + + const U32 DISPLAY_NAME_MAX_LENGTH = 31; // characters, not bytes + LLWString display_name_wstr = utf8string_to_wstring(display_name_utf8); + if (display_name_wstr.size() > DISPLAY_NAME_MAX_LENGTH) + { + LLSD args; + args["LENGTH"] = llformat("%d", DISPLAY_NAME_MAX_LENGTH); + LLNotificationsUtil::add("SetDisplayNameFailedLength", args); + return; + } + + LLViewerDisplayName::set(display_name_utf8, + boost::bind(&LLPanelMyProfileEdit::onCacheSetName, this, + _1, _2, _3)); + } +} + +void LLPanelMyProfileEdit::onClickSetName() +{ + LLUUID agent_id = getAvatarId(); + std::string display_name; + LLAvatarName av_name; + if (!LLAvatarNameCache::get(agent_id, &av_name)) + { + // something is wrong, tell user to try again later + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); + return; + } + + display_name = av_name.mDisplayName; + if (display_name.empty()) + { + // something is wrong, tell user to try again later + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); + return; + } + + llinfos << "name-change now " << LLDate::now() << " next_update " + << LLDate(av_name.mNextUpdate) << llendl; + F64 now_secs = LLDate::now().secondsSinceEpoch(); + if (now_secs < av_name.mNextUpdate) + { + // ...can't update until some time in the future + F64 next_update_local_secs = + av_name.mNextUpdate - LLStringOps::getLocalTimeOffset(); + LLDate next_update_local(next_update_local_secs); + // display as "July 18 12:17 PM" + std::string next_update_string = + next_update_local.toHTTPDateString("%B %d %I:%M %p"); + LLSD args; + args["TIME"] = next_update_string; + LLNotificationsUtil::add("SetDisplayNameFailedLockout", args); + return; + } + + LLSD args; + args["DISPLAY_NAME"] = display_name; + LLSD payload; + payload["agent_id"] = agent_id; + LLNotificationsUtil::add("SetDisplayName", args, payload, + boost::bind(&LLPanelMyProfileEdit::onDialogSetName, this, _1, _2)); +} + void LLPanelMyProfileEdit::enableEditing(bool enable) { childSetEnabled("2nd_life_pic", enable); diff --git a/indra/newview/llpanelme.h b/indra/newview/llpanelme.h index f2b38de3d6..f3caf026dc 100644 --- a/indra/newview/llpanelme.h +++ b/indra/newview/llpanelme.h @@ -34,8 +34,9 @@ #define LL_LLPANELMEPROFILE_H #include "llpanel.h" -#include "llpanelavatar.h" +#include "llpanelprofile.h" +class LLAvatarName; class LLPanelMyProfileEdit; class LLPanelProfile; class LLIconCtrl; @@ -89,11 +90,15 @@ protected: /*virtual*/void resetData(); void processProfileProperties(const LLAvatarData* avatar_data); + void onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); private: void initTexturePickerMouseEvents(); void onTexturePickerMouseEnter(LLUICtrl* ctrl); void onTexturePickerMouseLeave(LLUICtrl* ctrl); + void onClickSetName(); + void onDialogSetName(const LLSD& notification, const LLSD& response); + void onCacheSetName(bool success, const std::string& reason, const LLSD& content); /** * Enabled/disables controls to prevent overwriting edited data upon receiving diff --git a/indra/newview/llpanelmediasettingspermissions.cpp b/indra/newview/llpanelmediasettingspermissions.cpp index e5caaaaffc..227dbb3da7 100644 --- a/indra/newview/llpanelmediasettingspermissions.cpp +++ b/indra/newview/llpanelmediasettingspermissions.cpp @@ -115,7 +115,7 @@ void LLPanelMediaSettingsPermissions::draw() if(mPermsGroupName) { mPermsGroupName->setNameID(LLUUID::null, TRUE); - mPermsGroupName->refresh(LLUUID::null, LLStringUtil::null, LLStringUtil::null, true); + mPermsGroupName->refresh(LLUUID::null, std::string(), true); mPermsGroupName->setEnabled(false); }; }; @@ -194,7 +194,7 @@ void LLPanelMediaSettingsPermissions::initValues( void* userdata, const LLSD& me data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); }; }; - + // *NOTE: If any of a particular flavor is tentative, we have to disable // them all because of an architectural issue: namely that we represent // these as a bit field, and we can't selectively apply only one bit to all selected @@ -238,21 +238,21 @@ void LLPanelMediaSettingsPermissions::getValues( LLSD &fill_me_in, bool include_ { // moved over from the 'General settings' tab if (include_tentative || !mControls->getTentative()) fill_me_in[LLMediaEntry::CONTROLS_KEY] = (LLSD::Integer)mControls->getCurrentIndex(); - - // *NOTE: For some reason, gcc does not like these symbol references in the - // expressions below (inside the static_casts). I have NO idea why :(. - // For some reason, assigning them to const temp vars here fixes the link - // error. Bizarre. - const U8 none = LLMediaEntry::PERM_NONE; - const U8 owner = LLMediaEntry::PERM_OWNER; - const U8 group = LLMediaEntry::PERM_GROUP; - const U8 anyone = LLMediaEntry::PERM_ANYONE; - const LLSD::Integer control = static_cast<LLSD::Integer>( + + // *NOTE: For some reason, gcc does not like these symbol references in the + // expressions below (inside the static_casts). I have NO idea why :(. + // For some reason, assigning them to const temp vars here fixes the link + // error. Bizarre. + const U8 none = LLMediaEntry::PERM_NONE; + const U8 owner = LLMediaEntry::PERM_OWNER; + const U8 group = LLMediaEntry::PERM_GROUP; + const U8 anyone = LLMediaEntry::PERM_ANYONE; + const LLSD::Integer control = static_cast<LLSD::Integer>( (mPermsOwnerControl->getValue() ? owner : none ) | (mPermsGroupControl->getValue() ? group: none ) | (mPermsWorldControl->getValue() ? anyone : none )); - const LLSD::Integer interact = static_cast<LLSD::Integer>( - (mPermsOwnerInteract->getValue() ? owner: none ) | + const LLSD::Integer interact = static_cast<LLSD::Integer>( + (mPermsOwnerInteract->getValue() ? owner: none ) | (mPermsGroupInteract->getValue() ? group : none ) | (mPermsWorldInteract->getValue() ? anyone : none )); @@ -266,15 +266,15 @@ void LLPanelMediaSettingsPermissions::getValues( LLSD &fill_me_in, bool include_ !mPermsGroupControl->getTentative() || !mPermsWorldControl->getTentative()) { - fill_me_in[LLMediaEntry::PERMS_CONTROL_KEY] = control; + fill_me_in[LLMediaEntry::PERMS_CONTROL_KEY] = control; } if (include_tentative || !mPermsOwnerInteract->getTentative() || !mPermsGroupInteract->getTentative() || !mPermsWorldInteract->getTentative()) { - fill_me_in[LLMediaEntry::PERMS_INTERACT_KEY] = interact; - } + fill_me_in[LLMediaEntry::PERMS_INTERACT_KEY] = interact; +} } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 0a4af00f78..7f20079d14 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" // libs +#include "llavatarname.h" #include "llfloaterreg.h" #include "llmenugl.h" #include "llnotificationsutil.h" @@ -1161,12 +1162,10 @@ void LLPanelPeople::onActivateButtonClicked() } // static -void LLPanelPeople::onAvatarPicked( - const std::vector<std::string>& names, - const uuid_vec_t& ids) +void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (!names.empty() && !ids.empty()) - LLAvatarActions::requestFriendshipDialog(ids[0], names[0]); + LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); } void LLPanelPeople::onGroupPlusButtonClicked() diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 17c45a034b..54b53fc12c 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -38,10 +38,11 @@ #include "llcallingcard.h" // for avatar tracker #include "llvoiceclient.h" -class LLFilterEditor; -class LLTabContainer; class LLAvatarList; +class LLAvatarName; +class LLFilterEditor; class LLGroupList; +class LLTabContainer; class LLPanelPeople : public LLPanel @@ -133,9 +134,7 @@ private: bool onNearbyViewSortMenuItemCheck(const LLSD& userdata); // misc callbacks - static void onAvatarPicked( - const std::vector<std::string>& names, - const uuid_vec_t& ids); + static void onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); void onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 71d16a08b4..6bac4ae196 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -385,7 +385,7 @@ void LLPanelPermissions::refresh() if (mLabelGroupName) { mLabelGroupName->setNameID(LLUUID::null, TRUE); - mLabelGroupName->refresh(LLUUID::null,LLStringUtil::null, LLStringUtil::null, TRUE); + mLabelGroupName->refresh(LLUUID::null, std::string(), true); mLabelGroupName->setEnabled(FALSE); } } diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index bde8d02885..291da5fdd2 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -235,9 +235,9 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) LLAvatarPicks* avatar_picks = static_cast<LLAvatarPicks*>(data); if(avatar_picks && getAvatarId() == avatar_picks->target_id) { - std::string name, second_name; - gCacheName->getName(getAvatarId(),name,second_name); - childSetTextArg("pick_title", "[NAME]",name); + std::string full_name; + gCacheName->getFullName(getAvatarId(), full_name); + childSetTextArg("pick_title", "[NAME]", full_name); // Save selection, to be able to edit same item after saving changes. See EXT-3023. LLUUID selected_id = mPicksList->getSelectedValue()[PICK_ID]; diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index f6133d4446..3b0dcad679 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -34,6 +34,7 @@ #include "llpanelplaceinfo.h" +#include "llavatarname.h" #include "llsdutil.h" #include "llsdutil_math.h" @@ -281,9 +282,15 @@ void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* } // static -void LLPanelPlaceInfo::nameUpdatedCallback(LLTextBox* text, - const std::string& first, - const std::string& last) +void LLPanelPlaceInfo::onNameCache(LLTextBox* text, const std::string& full_name) { - text->setText(first + " " + last); + text->setText(full_name); +} + +// static +void LLPanelPlaceInfo::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + LLTextBox* text) +{ + text->setText( av_name.getCompleteName() ); } diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index deedbd2b0f..0c08c5059b 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -40,6 +40,7 @@ #include "llremoteparcelrequest.h" +class LLAvatarName; class LLExpandableTextBox; class LLIconCtrl; class LLInventoryItem; @@ -102,9 +103,10 @@ public: void createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel); protected: - static void nameUpdatedCallback(LLTextBox* text, - const std::string& first, - const std::string& last); + static void onNameCache(LLTextBox* text, const std::string& full_name); + static void onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + LLTextBox* text); /** * mParcelID is valid only for remote places, in other cases it's null. See resetLocation() diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index c8abcc83c4..2a8249f4b6 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -34,6 +34,7 @@ #include "llpanelplaceprofile.h" +#include "llavatarnamecache.h" #include "llparcel.h" #include "message.h" @@ -51,6 +52,7 @@ #include "llappviewer.h" #include "llcallbacklist.h" #include "llbuycurrencyhtml.h" +#include "llslurl.h" #include "llstatusbar.h" #include "llviewercontrol.h" #include "llviewerparcelmgr.h" @@ -427,11 +429,11 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, if(!parcel->getGroupID().isNull()) { // FIXME: Using parcel group as region group. - gCacheName->get(parcel->getGroupID(), TRUE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mRegionGroupText, _2, _3)); + gCacheName->getGroup(parcel->getGroupID(), + boost::bind(&LLPanelPlaceInfo::onNameCache, mRegionGroupText, _2)); - gCacheName->get(parcel->getGroupID(), TRUE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mParcelOwner, _2, _3)); + gCacheName->getGroup(parcel->getGroupID(), + boost::bind(&LLPanelPlaceInfo::onNameCache, mParcelOwner, _2)); } else { @@ -443,10 +445,12 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, else { // Figure out the owner's name - gCacheName->get(parcel->getOwnerID(), FALSE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mParcelOwner, _2, _3)); - gCacheName->get(region->getOwner(), FALSE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mRegionOwnerText, _2, _3)); + std::string parcel_owner = + LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString(); + mParcelOwner->setText(parcel_owner); + LLAvatarNameCache::get(region->getOwner(), + boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, + _1, _2, mRegionOwnerText)); } if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus()) @@ -468,9 +472,10 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID(); if(auth_buyer_id.notNull()) { - gCacheName->get(auth_buyer_id, TRUE, - boost::bind(&LLPanelPlaceInfo::nameUpdatedCallback, mSaleToText, _2, _3)); - + LLAvatarNameCache::get(auth_buyer_id, + boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, + _1, _2, mSaleToText)); + // Show sales info to a specific person or a group he belongs to. if (auth_buyer_id != gAgent.getID() && !gAgent.isInGroup(auth_buyer_id)) { diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index f1aa3f10f8..be5b440050 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -33,7 +33,6 @@ #ifndef LL_LLPANELPROFILE_H #define LL_LLPANELPROFILE_H -#include "llviewerprecompiledheaders.h" #include "llpanel.h" #include "llpanelavatar.h" diff --git a/indra/newview/llpanelprofileview.cpp b/indra/newview/llpanelprofileview.cpp index 044036ea50..d22d8d2718 100644 --- a/indra/newview/llpanelprofileview.cpp +++ b/indra/newview/llpanelprofileview.cpp @@ -32,11 +32,12 @@ #include "llviewerprecompiledheaders.h" +#include "llpanelprofileview.h" + #include "llavatarconstants.h" +#include "llavatarnamecache.h" // IDEVO #include "lluserrelations.h" -#include "llpanelprofileview.h" - #include "llavatarpropertiesprocessor.h" #include "llcallingcard.h" #include "llpanelavatar.h" @@ -104,11 +105,15 @@ void LLPanelProfileView::onOpen(const LLSD& key) if(id.notNull() && getAvatarId() != id) { setAvatarId(id); + + // clear name fields, which might have old data + getChild<LLUICtrl>("user_name")->setValue( LLSD() ); + getChild<LLUICtrl>("user_slid")->setValue( LLSD() ); } // Update the avatar name. - gCacheName->get(getAvatarId(), FALSE, - boost::bind(&LLPanelProfileView::onAvatarNameCached, this, _1, _2, _3, _4)); + LLAvatarNameCache::get(getAvatarId(), + boost::bind(&LLPanelProfileView::onAvatarNameCache, this, _1, _2)); updateOnlineStatus(); @@ -198,10 +203,11 @@ void LLPanelProfileView::processOnlineStatus(bool online) mStatusText->setValue(status); } -void LLPanelProfileView::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) +void LLPanelProfileView::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) { - llassert(getAvatarId() == id); - getChild<LLUICtrl>("user_name", FALSE)->setValue(first_name + " " + last_name); + getChild<LLUICtrl>("user_name")->setValue( av_name.mDisplayName ); + getChild<LLUICtrl>("user_slid")->setValue( av_name.mUsername ); } // EOF diff --git a/indra/newview/llpanelprofileview.h b/indra/newview/llpanelprofileview.h index 9b87e146a8..dedb617c27 100644 --- a/indra/newview/llpanelprofileview.h +++ b/indra/newview/llpanelprofileview.h @@ -39,6 +39,7 @@ #include "llagent.h" #include "lltooldraganddrop.h" +class LLAvatarName; class LLPanelProfile; class LLPanelProfileTab; class LLTextBox; @@ -99,11 +100,11 @@ protected: private: // LLCacheName will call this function when avatar name is loaded from server. // This is required to display names that have not been cached yet. - void onAvatarNameCached( - const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group); +// void onNameCache( +// const LLUUID& id, +// const std::string& full_name, +// bool is_group); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); LLTextBox* mStatusText; AvatarStatusObserver* mAvatarStatusObserver; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index f020ad9bc2..986f71ae7b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -325,11 +325,18 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) if ( item ) { std::string name = item->getAvatarName(); + std::string tooltip = item->getAvatarToolTip(); size_t found = name.find(moderator_indicator); if (found != std::string::npos) { name.erase(found, moderator_indicator_len); - item->setName(name); + item->setAvatarName(name); + } + found = tooltip.find(moderator_indicator); + if (found != tooltip.npos) + { + tooltip.erase(found, moderator_indicator_len); + item->setAvatarToolTip(tooltip); } } } @@ -345,12 +352,20 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) if ( item ) { std::string name = item->getAvatarName(); + std::string tooltip = item->getAvatarToolTip(); size_t found = name.find(moderator_indicator); if (found == std::string::npos) { name += " "; name += moderator_indicator; - item->setName(name); + item->setAvatarName(name); + } + found = tooltip.find(moderator_indicator); + if (found == std::string::npos) + { + tooltip += " "; + tooltip += moderator_indicator; + item->setAvatarToolTip(tooltip); } } } diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 0ec351965a..9373ff0d18 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -283,11 +283,12 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) if (item->getCreatorUUID().notNull()) { - std::string name; - gCacheName->getFullName(item->getCreatorUUID(), name); + LLUUID creator_id = item->getCreatorUUID(); + std::string name = + LLSLURL("agent", creator_id, "completename").getSLURLString(); childSetEnabled("BtnCreator",TRUE); childSetEnabled("LabelCreatorTitle",TRUE); - childSetEnabled("LabelCreatorName",TRUE); + childSetEnabled("LabelCreatorName",FALSE); childSetText("LabelCreatorName",name); } else @@ -310,11 +311,12 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } else { - gCacheName->getFullName(perm.getOwner(), name); + LLUUID owner_id = perm.getOwner(); + name = LLSLURL("agent", owner_id, "completename").getSLURLString(); } childSetEnabled("BtnOwner",TRUE); childSetEnabled("LabelOwnerTitle",TRUE); - childSetEnabled("LabelOwnerName",TRUE); + childSetEnabled("LabelOwnerName",FALSE); childSetText("LabelOwnerName",name); } else diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index e76397671b..475a6b1da7 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -351,7 +351,7 @@ void LLSidepanelTaskInfo::refresh() if (mLabelGroupName) { mLabelGroupName->setNameID(LLUUID::null, TRUE); - mLabelGroupName->refresh(LLUUID::null,LLStringUtil::null, LLStringUtil::null, TRUE); + mLabelGroupName->refresh(LLUUID::null, std::string(), true); mLabelGroupName->setEnabled(FALSE); } } diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index bf00b47c21..b844d230a1 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -74,12 +74,13 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy void LLSpeaker::lookupName() { - gCacheName->get(mID, FALSE, boost::bind(&LLSpeaker::onAvatarNameLookup, this, _1, _2, _3, _4)); + gCacheName->get(mID, false, + boost::bind(&LLSpeaker::onNameCache, this, _1, _2, _3)); } -void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +void LLSpeaker::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group) { - mDisplayName = first + " " + last; + mDisplayName = full_name; } bool LLSpeaker::isInVoiceChannel() diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 4a250de82f..29638319ad 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -66,7 +66,7 @@ public: ~LLSpeaker() {}; void lookupName(); - void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group); bool isInVoiceChannel(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index e51e6363dd..89178fc30c 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -52,6 +52,7 @@ #endif #include "llares.h" +#include "llavatarnamecache.h" #include "lllandmark.h" #include "llcachename.h" #include "lldir.h" @@ -270,11 +271,10 @@ void apply_udp_blacklist(const std::string& csv); bool process_login_success_response(); void transition_back_to_login_panel(const std::string& emsg); -void callback_cache_name(const LLUUID& id, const std::string& firstname, const std::string& lastname, BOOL is_group) +void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is_group) { - LLNameListCtrl::refreshAll(id, firstname, lastname, is_group); - LLNameBox::refreshAll(id, firstname, lastname, is_group); - LLNameEditor::refreshAll(id, firstname, lastname, is_group); + LLNameBox::refreshAll(id, full_name, is_group); + LLNameEditor::refreshAll(id, full_name, is_group); // TODO: Actually be intelligent about the refresh. // For now, just brute force refresh the dialogs. @@ -1293,16 +1293,7 @@ bool idle_startup() gXferManager->registerCallbacks(gMessageSystem); - if ( gCacheName == NULL ) - { - gCacheName = new LLCacheName(gMessageSystem); - gCacheName->addObserver(&callback_cache_name); - gCacheName->LocalizeCacheName("waiting", LLTrans::getString("AvatarNameWaiting")); - gCacheName->LocalizeCacheName("nobody", LLTrans::getString("AvatarNameNobody")); - gCacheName->LocalizeCacheName("none", LLTrans::getString("GroupNameNone")); - // Load stored cache if possible - LLAppViewer::instance()->loadNameCache(); - } + LLStartUp::initNameCache(); //gCacheName is required for nearby chat history loading //so I just moved nearby history loading a few states further @@ -1640,7 +1631,6 @@ bool idle_startup() LLClassifiedInfo::loadCategories(classified_categories); } - // This method MUST be called before gInventory.findCategoryUUIDForType because of // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. gInventory.buildParentChildMap(); @@ -2671,6 +2661,33 @@ void LLStartUp::fontInit() LLFontGL::loadDefaultFonts(); } +void LLStartUp::initNameCache() +{ + // Can be called multiple times + if ( gCacheName ) return; + + gCacheName = new LLCacheName(gMessageSystem); + gCacheName->addObserver(&callback_cache_name); + gCacheName->localizeCacheName("waiting", LLTrans::getString("AvatarNameWaiting")); + gCacheName->localizeCacheName("nobody", LLTrans::getString("AvatarNameNobody")); + gCacheName->localizeCacheName("none", LLTrans::getString("GroupNameNone")); + // Load stored cache if possible + LLAppViewer::instance()->loadNameCache(); + + // Start cache in not-running state until we figure out if we have + // capabilities for display name lookup + LLAvatarNameCache::initClass(false); + LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames")); +} + +void LLStartUp::cleanupNameCache() +{ + LLAvatarNameCache::cleanupClass(); + + delete gCacheName; + gCacheName = NULL; +} + bool LLStartUp::dispatchURL() { // ok, if we've gotten this far and have a startup URL diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index e1e108ca6a..21d403b453 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -96,6 +96,10 @@ public: // Load default fonts not already loaded at start screen static void fontInit(); + static void initNameCache(); + + static void cleanupNameCache(); + // outfit_folder_name can be a folder anywhere in your inventory, // but the name must be a case-sensitive exact match. // gender_name is either "male" or "female" diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index cb65756764..87481535ff 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -32,14 +32,15 @@ #include "llviewerprecompiledheaders.h" // must be first include +#include "llsyswellwindow.h" + #include "llagent.h" +#include "llavatarnamecache.h" #include "llflatlistview.h" #include "llfloaterreg.h" #include "llnotifications.h" -#include "llsyswellwindow.h" - #include "llbottomtray.h" #include "llscriptfloater.h" #include "llviewercontrol.h" @@ -284,14 +285,31 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& mChiclet->setOtherParticipantId(otherParticipantId); mChiclet->setVisible(true); - LLTextBox* contactName = getChild<LLTextBox>("contact_name"); - contactName->setValue(name); + if (im_chiclet_type == LLIMChiclet::TYPE_IM) + { + LLAvatarNameCache::get(otherParticipantId, + boost::bind(&LLIMWellWindow::RowPanel::onAvatarNameCache, + this, _1, _2)); + } + else + { + LLTextBox* contactName = getChild<LLTextBox>("contact_name"); + contactName->setValue(name); + } mCloseBtn = getChild<LLButton>("hide_btn"); mCloseBtn->setCommitCallback(boost::bind(&LLIMWellWindow::RowPanel::onClosePanel, this)); } //--------------------------------------------------------------------------------- +void LLIMWellWindow::RowPanel::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + LLTextBox* contactName = getChild<LLTextBox>("contact_name"); + contactName->setValue( av_name.getCompleteName() ); +} + +//--------------------------------------------------------------------------------- void LLIMWellWindow::RowPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param) { LLTextBox* text = getChild<LLTextBox>("contact_name"); diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index c8215c71ee..9d8fe536e2 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -43,6 +43,7 @@ #include "boost/shared_ptr.hpp" +class LLAvatarName; class LLFlatListView; class LLChiclet; class LLIMChiclet; @@ -208,6 +209,7 @@ private: private: static const S32 CHICLET_HPAD = 10; + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); void onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param); void onClosePanel(); public: diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 7eac3867d5..ab4071110e 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -42,6 +42,7 @@ #include "llnotifications.h" #include "llviewertexteditor.h" +#include "llavatarnamecache.h" #include "lluiconstants.h" #include "llui.h" #include "llviewercontrol.h" @@ -73,7 +74,11 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification pGroupIcon->setValue(groupData.mInsigniaID); //header title - const std::string& from_name = payload["sender_name"].asString(); + std::string from_name = payload["sender_name"].asString(); + if (LLAvatarNameCache::useDisplayNames()) + { + from_name = LLCacheName::buildUsername(from_name); + } std::stringstream from; from << from_name << "/" << groupData.mName; LLTextBox* pTitleText = getChild<LLTextBox>("title"); diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 26d3bd5192..2c5438c47d 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -147,7 +147,8 @@ void LLToastIMPanel::spawnNameToolTip() { // Spawn at right side of the name textbox. LLRect sticky_rect = mAvatarName->calcScreenRect(); - S32 icon_x = llmin(sticky_rect.mLeft + mAvatarName->getTextPixelWidth() + 3, sticky_rect.mRight - 16); + S32 icon_x = + llmin(sticky_rect.mLeft + mAvatarName->getTextPixelWidth() + 3, sticky_rect.mRight); LLCoordGL pos(icon_x, sticky_rect.mTop); LLToolTip::Params params; diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index ae244cd8a1..7dd68d392e 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -40,6 +40,7 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavatarnamecache.h" #include "llviewercontrol.h" #include "llfocusmgr.h" //#include "llfirstuse.h" @@ -859,23 +860,39 @@ BOOL LLToolPie::handleTooltipObject( LLViewerObject* hover_object, std::string l || !existing_inspector->getVisible() || existing_inspector->getKey()["avatar_id"].asUUID() != hover_object->getID()) { - std::string avatar_name; + // IDEVO: try to get display name + username + std::string final_name; + std::string full_name; + if (!gCacheName->getFullName(hover_object->getID(), full_name)) + { LLNameValue* firstname = hover_object->getNVPair("FirstName"); LLNameValue* lastname = hover_object->getNVPair("LastName"); if (firstname && lastname) { - avatar_name = llformat("%s %s", firstname->getString(), lastname->getString()); + full_name = LLCacheName::buildFullName( + firstname->getString(), lastname->getString()); + } + else + { + full_name = LLTrans::getString("TooltipPerson"); + } + } + LLAvatarName av_name; + if (LLAvatarNameCache::useDisplayNames() + && LLAvatarNameCache::get(hover_object->getID(), &av_name)) + { + final_name = av_name.mDisplayName + " (" + av_name.mUsername + ")"; } else { - avatar_name = LLTrans::getString("TooltipPerson"); + final_name = full_name; } // *HACK: We may select this object, so pretend it was clicked mPick = mHoverPick; LLInspector::Params p; p.fillFrom(LLUICtrlFactory::instance().getDefaultParams<LLInspector>()); - p.message(avatar_name); + p.message(final_name); p.image.name("Inspector_I"); p.click_callback(boost::bind(showAvatarInspector, hover_object->getID())); p.visible_time_near(6.f); diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index cc074287c4..c81a062ade 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -570,16 +570,16 @@ void LLTracker::renderBeacon(LLVector3d pos_global, std::string text; text = llformat( "%.0f m", to_vec.magVec()); - LLWString wstr; - wstr += utf8str_to_wstring(label); - wstr += '\n'; - wstr += utf8str_to_wstring(text); + std::string str; + str += label; + str += '\n'; + str += text; hud_textp->setFont(LLFontGL::getFontSansSerif()); hud_textp->setZCompare(FALSE); hud_textp->setColor(LLColor4(1.f, 1.f, 1.f, llmax(0.2f, llmin(1.f,(dist-FADE_DIST)/FADE_DIST)))); - hud_textp->setString(wstr); + hud_textp->setString(str); hud_textp->setVertAlignment(LLHUDText::ALIGN_VERT_CENTER); hud_textp->setPositionAgent(pos_agent); } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 3482ac508e..3e30911847 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -323,7 +323,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLImageGL::updateStats(gFrameTimeSeconds); LLVOAvatar::sRenderName = gSavedSettings.getS32("AvatarNameTagMode"); - LLVOAvatar::sRenderGroupTitles = (gSavedSettings.getBOOL("RenderShowGroupTitleAll") && gSavedSettings.getS32("AvatarNameTagMode")); + LLVOAvatar::sRenderGroupTitles = (gSavedSettings.getBOOL("NameTagShowGroupTitles") && gSavedSettings.getS32("AvatarNameTagMode")); gPipeline.mBackfaceCull = TRUE; gFrameCount++; diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp new file mode 100644 index 0000000000..bda0fe9737 --- /dev/null +++ b/indra/newview/llviewerdisplayname.cpp @@ -0,0 +1,205 @@ +/** + * @file llviewerdisplayname.cpp + * @brief Wrapper for display name functionality + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "llviewerprecompiledheaders.h" + +#include "llviewerdisplayname.h" + +// viewer includes +#include "llagent.h" +#include "llviewerregion.h" +#include "llvoavatar.h" + +// library includes +#include "llavatarnamecache.h" +#include "llhttpclient.h" +#include "llhttpnode.h" +#include "llnotificationsutil.h" +#include "llui.h" // getLanguage() + +namespace LLViewerDisplayName +{ + // Fired when viewer receives server response to display name change + set_name_signal_t sSetDisplayNameSignal; +} + +class LLSetDisplayNameResponder : public LLHTTPClient::Responder +{ +public: + // only care about errors + /*virtual*/ void error(U32 status, const std::string& reason) + { + LLViewerDisplayName::sSetDisplayNameSignal(false, "", LLSD()); + LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); + } +}; + +void LLViewerDisplayName::set(const std::string& display_name, const set_name_slot_t& slot) +{ + // TODO: simple validation here + + LLViewerRegion* region = gAgent.getRegion(); + llassert(region); + std::string cap_url = region->getCapability("SetDisplayName"); + if (cap_url.empty()) + { + // this server does not support display names, report error + slot(false, "unsupported", LLSD()); + return; + } + + // People API can return localized error messages. Indicate our + // language preference via header. + LLSD headers; + headers["Accept-Language"] = LLUI::getLanguage(); + + // People API requires both the old and new value to change a variable. + // Our display name will be in cache before the viewer's UI is available + // to request a change, so we can use direct lookup without callback. + LLAvatarName av_name; + if (!LLAvatarNameCache::get( gAgent.getID(), &av_name)) + { + slot(false, "name unavailable", LLSD()); + return; + } + + // People API expects array of [ "old value", "new value" ] + LLSD change_array = LLSD::emptyArray(); + change_array.append(av_name.mDisplayName); + change_array.append(display_name); + + llinfos << "Set name POST to " << cap_url << llendl; + + // Record our caller for when the server sends back a reply + sSetDisplayNameSignal.connect(slot); + + // POST the requested change. The sim will not send a response back to + // this request directly, rather it will send a separate message after it + // communicates with the back-end. + LLSD body; + body["display_name"] = change_array; + LLHTTPClient::post(cap_url, body, new LLSetDisplayNameResponder, headers); +} + +class LLSetDisplayNameReply : public LLHTTPNode +{ + LOG_CLASS(LLSetDisplayNameReply); +public: + /*virtual*/ void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const + { + LLSD body = input["body"]; + + S32 status = body["status"].asInteger(); + bool success = (status == 200); + std::string reason = body["reason"].asString(); + LLSD content = body["content"]; + + llinfos << "status " << status << " reason " << reason << llendl; + + // If viewer's concept of display name is out-of-date, the set request + // will fail with 409 Conflict. If that happens, fetch up-to-date + // name information. + if (status == 409) + { + LLUUID agent_id = gAgent.getID(); + // Flush stale data + LLAvatarNameCache::erase( agent_id ); + // Queue request for new data + LLAvatarName ignored; + LLAvatarNameCache::get( agent_id, &ignored ); + // Kill name tag, as it is wrong + LLVOAvatar::invalidateNameTag( agent_id ); + } + + // inform caller of result + LLViewerDisplayName::sSetDisplayNameSignal(success, reason, content); + LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); + } +}; + + +class LLDisplayNameUpdate : public LLHTTPNode +{ + /*virtual*/ void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const + { + LLSD body = input["body"]; + LLUUID agent_id = body["agent_id"]; + std::string old_display_name = body["old_display_name"]; + // By convention this record is called "agent" in the People API + LLSD name_data = body["agent"]; + + // Inject the new name data into cache + LLAvatarName av_name; + av_name.fromLLSD( name_data ); + + llinfos << "name-update now " << LLDate::now() + << " next_update " << LLDate(av_name.mNextUpdate) + << llendl; + + // Name expiration time may be provided in headers, or we may use a + // default value + // *TODO: get actual headers out of ResponsePtr + //LLSD headers = response->mHeaders; + LLSD headers; + av_name.mExpires = + LLAvatarNameCache::nameExpirationFromHeaders(headers); + + LLAvatarNameCache::insert(agent_id, av_name); + + // force name tag to update + LLVOAvatar::invalidateNameTag(agent_id); + + // Don't show a notification for my name, because we'll show a nicer + // dialog + if (agent_id != gAgent.getID()) + { + LLSD args; + args["OLD_NAME"] = old_display_name; + args["SLID"] = av_name.mUsername; + args["NEW_NAME"] = av_name.mDisplayName; + LLNotificationsUtil::add("DisplayNameUpdate", args); + } + } +}; + +LLHTTPRegistration<LLSetDisplayNameReply> + gHTTPRegistrationMessageSetDisplayNameReply( + "/message/SetDisplayNameReply"); + +LLHTTPRegistration<LLDisplayNameUpdate> + gHTTPRegistrationMessageDisplayNameUpdate( + "/message/DisplayNameUpdate"); diff --git a/indra/newview/llviewerdisplayname.h b/indra/newview/llviewerdisplayname.h new file mode 100644 index 0000000000..c77388531b --- /dev/null +++ b/indra/newview/llviewerdisplayname.h @@ -0,0 +1,53 @@ +/** + * @file llviewerdisplayname.h + * @brief Wrapper for display name functionality + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#ifndef LLVIEWERDISPLAYNAME_H +#define LLVIEWERDISPLAYNAME_H + +#include <boost/signals2.hpp> + +class LLSD; +class LLUUID; + +namespace LLViewerDisplayName +{ + typedef boost::signals2::signal< + void (bool success, const std::string& reason, const LLSD& content)> + set_name_signal_t; + typedef set_name_signal_t::slot_type set_name_slot_t; + + // Sends an update to the server to change a display name + // and call back when done. May not succeed due to service + // unavailable or name not available. + void set(const std::string& display_name, const set_name_slot_t& slot); +} + +#endif // LLVIEWERDISPLAYNAME_H diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d745356dcd..7ba54b5069 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1714,9 +1714,9 @@ PermissionMask LLViewerInventoryItem::getPermissionMask() const //---------- -void LLViewerInventoryItem::onCallingCardNameLookup(const LLUUID& id, const std::string& first_name, const std::string& last_name) +void LLViewerInventoryItem::onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group) { - rename(first_name + " " + last_name); + rename(name); gInventory.addChangedMask(LLInventoryObserver::LABEL, getUUID()); gInventory.notifyObservers(); } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 8ab7c9710d..3c841d93c6 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -162,7 +162,7 @@ public: PermissionMask getPermissionMask() const; // callback - void onCallingCardNameLookup(const LLUUID& id, const std::string& first_name, const std::string& last_name); + void onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group); // If this is a broken link, try to fix it and any other identical link. BOOL regenerateLink(); diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp index 2b4b78d82d..b230203561 100644 --- a/indra/newview/llviewerjointattachment.cpp +++ b/indra/newview/llviewerjointattachment.cpp @@ -39,6 +39,7 @@ #include "llviewercontrol.h" #include "lldrawable.h" #include "llgl.h" +#include "llhudtext.h" #include "llrender.h" #include "llvoavatar.h" #include "llvolume.h" diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a1b909c609..23e12d4896 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -34,6 +34,7 @@ #include "llviewermenu.h" // linden library includes +#include "llavatarnamecache.h" // IDEVO #include "llfloaterreg.h" #include "llcombobox.h" #include "llinventorypanel.h" @@ -2777,9 +2778,8 @@ class LLObjectMute : public view_listener_t LLNameValue *lastname = avatar->getNVPair("LastName"); if (firstname && lastname) { - name = firstname->getString(); - name += " "; - name += lastname->getString(); + name = LLCacheName::buildFullName( + firstname->getString(), lastname->getString()); } type = LLMute::AGENT; @@ -3125,58 +3125,6 @@ bool enable_freeze_eject(const LLSD& avatar_id) return new_value; } -class LLAvatarGiveCard : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - llinfos << "handle_give_card()" << llendl; - LLViewerObject* dest = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if(dest && dest->isAvatar()) - { - bool found_name = false; - LLSD args; - LLSD old_args; - LLNameValue* nvfirst = dest->getNVPair("FirstName"); - LLNameValue* nvlast = dest->getNVPair("LastName"); - if(nvfirst && nvlast) - { - args["FIRST"] = nvfirst->getString(); - args["LAST"] = nvlast->getString(); - old_args["FIRST"] = nvfirst->getString(); - old_args["LAST"] = nvlast->getString(); - found_name = true; - } - LLViewerRegion* region = dest->getRegion(); - LLHost dest_host; - if(region) - { - dest_host = region->getHost(); - } - if(found_name && dest_host.isOk()) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("OfferCallingCard"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_AgentBlock); - msg->addUUIDFast(_PREHASH_DestID, dest->getID()); - LLUUID transaction_id; - transaction_id.generate(); - msg->addUUIDFast(_PREHASH_TransactionID, transaction_id); - msg->sendReliable(dest_host); - LLNotificationsUtil::add("OfferedCard", args); - } - else - { - LLNotificationsUtil::add("CantOfferCallingCard", old_args); - } - } - return true; - } -}; - - void login_done(S32 which, void *user) { @@ -3596,21 +3544,17 @@ void request_friendship(const LLUUID& dest_id) LLViewerObject* dest = gObjectList.findObject(dest_id); if(dest && dest->isAvatar()) { - std::string fullname; - LLSD args; + std::string full_name; LLNameValue* nvfirst = dest->getNVPair("FirstName"); LLNameValue* nvlast = dest->getNVPair("LastName"); if(nvfirst && nvlast) { - args["FIRST"] = nvfirst->getString(); - args["LAST"] = nvlast->getString(); - fullname = nvfirst->getString(); - fullname += " "; - fullname += nvlast->getString(); + full_name = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); } - if (!fullname.empty()) + if (!full_name.empty()) { - LLAvatarActions::requestFriendshipDialog(dest_id, fullname); + LLAvatarActions::requestFriendshipDialog(dest_id, full_name); } else { @@ -7652,6 +7596,16 @@ class LLWorldToggleCameraControls : public view_listener_t } }; +void handle_flush_name_caches() +{ + // Toggle display names on and off to flush + bool use_display_names = LLAvatarNameCache::useDisplayNames(); + LLAvatarNameCache::setUseDisplayNames(!use_display_names); + LLAvatarNameCache::setUseDisplayNames(use_display_names); + + if (gCacheName) gCacheName->clear(); +} + class LLUploadCostCalculator : public view_listener_t { std::string mCostStr; @@ -7859,6 +7813,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedToggleConsole(), "Advanced.ToggleConsole"); view_listener_t::addMenu(new LLAdvancedCheckConsole(), "Advanced.CheckConsole"); view_listener_t::addMenu(new LLAdvancedDumpInfoToConsole(), "Advanced.DumpInfoToConsole"); + // Advanced > HUD Info view_listener_t::addMenu(new LLAdvancedToggleHUDInfo(), "Advanced.ToggleHUDInfo"); view_listener_t::addMenu(new LLAdvancedCheckHUDInfo(), "Advanced.CheckHUDInfo"); @@ -7939,6 +7894,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedToggleXUINames(), "Advanced.ToggleXUINames"); view_listener_t::addMenu(new LLAdvancedCheckXUINames(), "Advanced.CheckXUINames"); view_listener_t::addMenu(new LLAdvancedSendTestIms(), "Advanced.SendTestIMs"); + commit.add("Advanced.FlushNameCaches", boost::bind(&handle_flush_name_caches)); // Advanced > Character > Grab Baked Texture view_listener_t::addMenu(new LLAdvancedGrabBakedTexture(), "Advanced.GrabBakedTexture"); @@ -8037,7 +7993,6 @@ void initialize_menus() view_listener_t::addMenu(new LLAvatarDebug(), "Avatar.Debug"); view_listener_t::addMenu(new LLAvatarVisibleDebug(), "Avatar.VisibleDebug"); view_listener_t::addMenu(new LLAvatarInviteToGroup(), "Avatar.InviteToGroup"); - view_listener_t::addMenu(new LLAvatarGiveCard(), "Avatar.GiveCard"); commit.add("Avatar.Eject", boost::bind(&handle_avatar_eject, LLSD())); view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5836aff4e7..4d3a6c24e2 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -34,9 +34,11 @@ #include "llviewermessage.h" #include "boost/lexical_cast.hpp" +// Linden libraries #include "llanimationstates.h" #include "llaudioengine.h" #include "llavataractions.h" +#include "llavatarnamecache.h" // IDEVO HACK #include "lscript_byteformat.h" #include "lleconomy.h" #include "lleventtimer.h" @@ -84,6 +86,7 @@ #include "llspeakers.h" #include "lltrans.h" #include "llviewerfoldertype.h" +#include "llvoavatar.h" // IDEVO HACK #include "lluri.h" #include "llviewergenericmessage.h" #include "llviewermenu.h" @@ -136,6 +139,7 @@ extern BOOL gDebugClicks; // function prototypes bool check_offer_throttle(const std::string& from_name, bool check_only); +static void process_money_balance_reply_extended(LLMessageSystem* msg); //inventory offer throttle globals LLFrameTimer gThrottleTimer; @@ -1197,27 +1201,24 @@ bool highlight_offered_item(const LLUUID& item_id) } void inventory_offer_mute_callback(const LLUUID& blocked_id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group, LLOfferInfo* offer = NULL) + const std::string& full_name, + bool is_group, + LLOfferInfo* offer = NULL) { - std::string from_name; + std::string from_name = full_name; LLMute::EType type; if (is_group) { type = LLMute::GROUP; - from_name = first_name; } else if(offer && offer->mFromObject) { //we have to block object by name because blocked_id is an id of owner type = LLMute::BY_NAME; - from_name = offer->mFromName; } else { type = LLMute::AGENT; - from_name = first_name + " " + last_name; } // id should be null for BY_NAME mute, see LLMuteList::add for details @@ -1390,7 +1391,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& // * we can't build two messages at once. if (2 == button) // Block { - gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,_4,this)); + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,this)); } std::string from_string; // Used in the pop-up. @@ -1524,7 +1525,7 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const // * we can't build two messages at once. if (2 == button) { - gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,_4,this)); + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,this)); } LLMessageSystem* msg = gMessageSystem; @@ -1572,12 +1573,12 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const } else { - std::string first_name, last_name; - if (gCacheName->getName(mFromID, first_name, last_name)) + std::string full_name; + if (gCacheName->getFullName(mFromID, full_name)) { from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+ LLTrans::getString("'") + mFromName - + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedBy") + first_name + " " + last_name; - chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedBy") + " " + first_name + " " + last_name; + + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedBy") + full_name; + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedBy") + " " + full_name; } else { @@ -1781,7 +1782,14 @@ void inventory_offer_handler(LLOfferInfo* info) payload["give_inventory_notification"] = FALSE; args["OBJECTFROMNAME"] = info->mFromName; args["NAME"] = info->mFromName; - args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); + if (info->mFromGroup) + { + args["NAME_SLURL"] = LLSLURL("group", info->mFromID, "about").getSLURLString(); + } + else + { + args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); + } std::string verb = "select?name=" + LLURI::escape(msg); args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); @@ -1901,7 +1909,6 @@ protected: void modifyNotificationParams() { LLSD payload = mParams.payload; - payload["SESSION_NAME"] = mName; mParams.payload = payload; } }; @@ -1964,6 +1971,99 @@ static bool parse_lure_bucket(const std::string& bucket, return true; } +// Strip out "Resident" for display, but only if the message came from a user +// (rather than a script) +static std::string clean_name_from_im(const std::string& name, EInstantMessage type) +{ + switch(type) + { + case IM_NOTHING_SPECIAL: + case IM_MESSAGEBOX: + case IM_GROUP_INVITATION: + case IM_INVENTORY_OFFERED: + case IM_INVENTORY_ACCEPTED: + case IM_INVENTORY_DECLINED: + case IM_GROUP_VOTE: + case IM_GROUP_MESSAGE_DEPRECATED: + //IM_TASK_INVENTORY_OFFERED + //IM_TASK_INVENTORY_ACCEPTED + //IM_TASK_INVENTORY_DECLINED + case IM_NEW_USER_DEFAULT: + case IM_SESSION_INVITE: + case IM_SESSION_P2P_INVITE: + case IM_SESSION_GROUP_START: + case IM_SESSION_CONFERENCE_START: + case IM_SESSION_SEND: + case IM_SESSION_LEAVE: + //IM_FROM_TASK + case IM_BUSY_AUTO_RESPONSE: + case IM_CONSOLE_AND_CHAT_HISTORY: + case IM_LURE_USER: + case IM_LURE_ACCEPTED: + case IM_LURE_DECLINED: + case IM_GODLIKE_LURE_USER: + case IM_YET_TO_BE_USED: + case IM_GROUP_ELECTION_DEPRECATED: + //IM_GOTO_URL + //IM_FROM_TASK_AS_ALERT + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_INVENTORY_ACCEPTED: + case IM_GROUP_NOTICE_INVENTORY_DECLINED: + case IM_GROUP_INVITATION_ACCEPT: + case IM_GROUP_INVITATION_DECLINE: + case IM_GROUP_NOTICE_REQUESTED: + case IM_FRIENDSHIP_OFFERED: + case IM_FRIENDSHIP_ACCEPTED: + case IM_FRIENDSHIP_DECLINED_DEPRECATED: + //IM_TYPING_START + //IM_TYPING_STOP + return LLCacheName::cleanFullName(name); + default: + return name; + } +} + +static std::string clean_name_from_task_im(const std::string& msg, + BOOL from_group) +{ + boost::smatch match; + static const boost::regex returned_exp( + "(.*been returned to your inventory lost and found folder by )(.+)( (from|near).*)"); + if (boost::regex_match(msg, match, returned_exp)) + { + // match objects are 1-based for groups + std::string final = match[1].str(); + std::string name = match[2].str(); + // Don't try to clean up group names + if (!from_group) + { + if (LLAvatarNameCache::useDisplayNames()) + { + // ...just convert to username + final += LLCacheName::buildUsername(name); + } + else + { + // ...strip out legacy "Resident" name + final += LLCacheName::cleanFullName(name); + } + } + final += match[3].str(); + return final; + } + return msg; +} + +void notification_display_name_callback(const LLUUID& id, + const LLAvatarName& av_name, + const std::string& name, + LLSD& substitutions, + const LLSD& payload) +{ + substitutions["NAME"] = av_name.mDisplayName; + LLNotificationsUtil::add(name, substitutions, payload); +} + void process_improved_im(LLMessageSystem *msg, void **user_data) { if (gNoRender) @@ -2011,6 +2111,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { name = LLTrans::getString("Unnamed"); } + // IDEVO convert new-style "Resident" names for display + name = clean_name_from_im(name, dialog); BOOL is_busy = gAgent.getBusy(); BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); @@ -2039,7 +2141,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_CONSOLE_AND_CHAT_HISTORY: // *TODO: Translate args["MESSAGE"] = message; - payload["SESSION_NAME"] = name; payload["from_id"] = from_id; LLNotificationsUtil::add("IMSystemMessageTip",args, payload); break; @@ -2317,6 +2418,15 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; S32 membership_fee = ntohl(invite_bucket->membership_fee); + // IDEVO Clean up legacy name "Resident" in message constructed in + // lldatagroups.cpp + U32 pos = message.find(" has invited you to join a group.\n"); + if (pos != std::string::npos) + { + // use cleaned-up name from above + message = name + message.substr(pos); + } + LLSD payload; payload["transaction_id"] = session_id; payload["group_id"] = from_id; @@ -2504,6 +2614,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) chat.mSourceType = CHAT_SOURCE_SYSTEM; } + // IDEVO Some messages have embedded resident names + message = clean_name_from_task_im(message, from_group); + LLSD query_string; query_string["owner"] = from_id; query_string["slurl"] = location; @@ -2712,7 +2825,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) args["NAME"] = name; LLSD payload; payload["from_id"] = from_id; - LLNotificationsUtil::add("FriendshipAccepted", args, payload); + LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback, + _1, + _2, + "FriendshipAccepted", + args, + payload)); } break; @@ -2816,9 +2934,8 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) LLNameValue* nvlast = source->getNVPair("LastName"); if (nvfirst && nvlast) { - args["FIRST"] = nvfirst->getString(); - args["LAST"] = nvlast->getString(); - source_name = std::string(nvfirst->getString()) + " " + nvlast->getString(); + source_name = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); } } @@ -2851,7 +2968,6 @@ void process_decline_callingcard(LLMessageSystem* msg, void**) LLNotificationsUtil::add("CallingCardDeclined"); } - void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { LLChat chat; @@ -2867,7 +2983,6 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) LLViewerObject* chatter; msg->getString("ChatData", "FromName", from_name); - chat.mFromName = from_name; msg->getUUID("ChatData", "SourceID", from_id); chat.mFromID = from_id; @@ -2886,6 +3001,27 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) chat.mTime = LLFrameTimer::getElapsedSeconds(); + // IDEVO Correct for new-style "Resident" names + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // I don't know if it's OK to change this here, if + // anything downstream does lookups by name, for instance + LLAvatarName av_name; + if (LLAvatarNameCache::useDisplayNames() + && LLAvatarNameCache::get(from_id, &av_name)) + { + chat.mFromName = av_name.mDisplayName; + } + else + { + chat.mFromName = LLCacheName::cleanFullName(from_name); + } + } + else + { + chat.mFromName = from_name; + } + BOOL is_busy = gAgent.getBusy(); BOOL is_muted = FALSE; @@ -4742,168 +4878,271 @@ void process_time_dilation(LLMessageSystem *msg, void **user_data) */ - void process_money_balance_reply( LLMessageSystem* msg, void** ) { S32 balance = 0; S32 credit = 0; S32 committed = 0; std::string desc; + LLUUID tid; + msg->getUUID("MoneyData", "TransactionID", tid); msg->getS32("MoneyData", "MoneyBalance", balance); msg->getS32("MoneyData", "SquareMetersCredit", credit); msg->getS32("MoneyData", "SquareMetersCommitted", committed); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, desc); LL_INFOS("Messaging") << "L$, credit, committed: " << balance << " " << credit << " " << committed << LL_ENDL; - + if (gStatusBar) { - // S32 old_balance = gStatusBar->getBalance(); - - // This is an update, not the first transmission of balance - /* if (old_balance != 0) - { - // this is actually an update - if (balance > old_balance) - { - LLFirstUse::useBalanceIncrease(balance - old_balance); - } - else if (balance < old_balance) - { - LLFirstUse::useBalanceDecrease(balance - old_balance); - } - } - */ gStatusBar->setBalance(balance); gStatusBar->setLandCredit(credit); gStatusBar->setLandCommitted(committed); } - LLUUID tid; - msg->getUUID("MoneyData", "TransactionID", tid); + if (desc.empty() + || !gSavedSettings.getBOOL("NotifyMoneyChange")) + { + // ...nothing to display + return; + } + + // Suppress duplicate messages about the same transaction static std::deque<LLUUID> recent; - if(!desc.empty() && gSavedSettings.getBOOL("NotifyMoneyChange") - && (std::find(recent.rbegin(), recent.rend(), tid) == recent.rend())) + if (std::find(recent.rbegin(), recent.rend(), tid) != recent.rend()) { - // Make the user confirm the transaction, since they might - // have missed something during an event. - // *TODO: Translate - LLSD args; - + return; + } - // this is a marker to retrieve avatar name from server message: - // "<avatar name> paid you L$" - const std::string marker = "paid you L$"; + // Once the 'recent' container gets large enough, chop some + // off the beginning. + const U32 MAX_LOOKBACK = 30; + const S32 POP_FRONT_SIZE = 12; + if(recent.size() > MAX_LOOKBACK) + { + LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; + recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); + } + //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; + recent.push_back(tid); + if (msg->has("TransactionInfo")) + { + // ...message has extended info for localization + process_money_balance_reply_extended(msg); + } + else + { + // Only old dev grids will not supply the TransactionInfo block, + // so we can just use the hard-coded English string. + LLSD args; args["MESSAGE"] = desc; + LLNotificationsUtil::add("SystemMessage", args); + } +} - // extract avatar name from system message - S32 marker_pos = desc.find(marker, 0); +static std::string reason_from_transaction_type(S32 transaction_type, + const std::string& item_desc) +{ + // *NOTE: The keys for the reason strings are unusual because + // an earlier version of the code used English language strings + // extracted from hard-coded server English descriptions. + // Keeping them so we don't have to re-localize them. + switch (transaction_type) + { + case TRANS_OBJECT_SALE: + { + LLStringUtil::format_map_t arg; + arg["ITEM"] = item_desc; + return LLTrans::getString("for item", arg); + } + case TRANS_LAND_SALE: + return LLTrans::getString("for a parcel of land"); + + case TRANS_LAND_PASS_SALE: + return LLTrans::getString("for a land access pass"); + + case TRANS_GROUP_LAND_DEED: + return LLTrans::getString("for deeding land"); + + case TRANS_GROUP_CREATE: + return LLTrans::getString("to create a group"); + + case TRANS_GROUP_JOIN: + return LLTrans::getString("to join a group"); + + case TRANS_UPLOAD_CHARGE: + return LLTrans::getString("to upload"); - std::string base_name = desc.substr(0, marker_pos); - - std::string name = base_name; - LLStringUtil::trim(name); + case TRANS_CLASSIFIED_CHARGE: + return LLTrans::getString("to publish a classified ad"); + + // These have no reason to display, but are expected and should not + // generate warnings + case TRANS_GIFT: + case TRANS_PAY_OBJECT: + case TRANS_OBJECT_PAYS: + return std::string(); - // if name extracted and name cache contains avatar id send loggable notification - LLUUID from_id; - if(name.size() > 0 && gCacheName->getUUID(name, from_id)) - { - //description always comes not localized. lets fix this + default: + llwarns << "Unknown transaction type " + << transaction_type << llendl; + return std::string(); + } +} - //ammount paid - std::string ammount = desc.substr(marker_pos + marker.length(),desc.length() - marker.length() - marker_pos); - - //reform description - LLStringUtil::format_map_t str_args; - str_args["NAME"] = base_name; - str_args["AMOUNT"] = ammount; - std::string new_description = LLTrans::getString("paid_you_ldollars", str_args); +static void money_balance_group_notify(const LLUUID& group_id, + const std::string& name, + bool is_group, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} +static void money_balance_avatar_notify(const LLUUID& agent_id, + const LLAvatarName& av_name, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} - args["MESSAGE"] = new_description; - args["NAME"] = name; - LLSD payload; - payload["from_id"] = from_id; - LLNotificationsUtil::add("PaymentRecived", args, payload); +static void process_money_balance_reply_extended(LLMessageSystem* msg) +{ + // Added in server 1.40 and viewer 2.1, support for localization + // and agent ids for name lookup. + S32 transaction_type = 0; + LLUUID source_id; + BOOL is_source_group = FALSE; + LLUUID dest_id; + BOOL is_dest_group = FALSE; + S32 amount = 0; + std::string item_description; + + msg->getS32("TransactionInfo", "TransactionType", transaction_type); + msg->getUUID("TransactionInfo", "SourceID", source_id); + msg->getBOOL("TransactionInfo", "IsSourceGroup", is_source_group); + msg->getUUID("TransactionInfo", "DestID", dest_id); + msg->getBOOL("TransactionInfo", "IsDestGroup", is_dest_group); + msg->getS32("TransactionInfo", "Amount", amount); + msg->getString("TransactionInfo", "ItemDescription", item_description); + LL_INFOS("Money") << "MoneyBalanceReply source " << source_id + << " dest " << dest_id + << " type " << transaction_type + << " item " << item_description << LL_ENDL; + + if (source_id.isNull() && dest_id.isNull()) + { + // this is a pure balance update, no notification required + return; + } + + const char* source_type = (is_source_group ? "group" : "agent"); + std::string source_slurl = + LLSLURL( source_type, source_id, "inspect").getSLURLString(); + + const char* dest_type = (is_dest_group ? "group" : "agent"); + std::string dest_slurl = + LLSLURL( dest_type, dest_id, "completename").getSLURLString(); + + std::string reason = + reason_from_transaction_type(transaction_type, item_description); + + LLStringUtil::format_map_t args; + args["REASON"] = reason; // could be empty + args["AMOUNT"] = llformat("%d", amount); + + // Need to delay until name looked up, so need to know whether or not + // is group + bool is_name_group = false; + LLUUID name_id; + std::string message; + std::string notification; + LLSD final_args; + LLSD payload; + + bool you_paid_someone = (source_id == gAgentID); + if (you_paid_someone) + { + args["NAME"] = dest_slurl; + is_name_group = is_dest_group; + name_id = dest_id; + if (!reason.empty()) + { + if (dest_id.notNull()) + { + message = LLTrans::getString("you_paid_ldollars", args); + } + else + { + // transaction fee to the system, eg, to create a group + message = LLTrans::getString("you_paid_ldollars_no_name", args); + } } - //AD *HACK: Parsing incoming string to localize messages that come from server! EXT-5986 - // It's only a temporarily and ineffective measure. It doesn't affect performance much - // because we get here only for specific type of messages, but anyway it is not right to do it! - // *TODO: Server-side changes should be made and this code removed. else { - if(desc.find("You paid")==0) + if (dest_id.notNull()) { - // Regular expression for message parsing- change it in case of server-side changes. - // Each set of parenthesis will later be used to find arguments of message we generate - // in the end of this if- (.*) gives us name of money receiver, (\\d+)-amount of money we pay - // and ([^$]*)- reason of payment - boost::regex expr("You paid (?:.{0}|(.*) )L\\$(\\d+)\\s?([^$]*)\\."); - boost::match_results <std::string::const_iterator> matches; - if(boost::regex_match(desc, matches, expr)) - { - // Name of full localizable notification string - // there are four types of this string- with name of receiver and reason of payment, - // without name and without reason (both may also be absent simultaneously). - // example of string without name - You paid L$100 to create a group. - // example of string without reason - You paid Smdby Linden L$100. - // example of string with reason and name - You paid Smbdy Linden L$100 for a land access pass. - // example of string with no info - You paid L$50. - std::string line = "you_paid_ldollars_no_name"; - - // arguments of string which will be in notification - LLStringUtil::format_map_t str_args; - - // extracting amount of money paid (without L$ symbols). It is always present. - str_args["[AMOUNT]"] = std::string(matches[2]); - - // extracting name of person/group you are paying (it may be absent) - std::string name = std::string(matches[1]); - if(!name.empty()) - { - str_args["[NAME]"] = name; - line = "you_paid_ldollars"; - } - - // extracting reason of payment (it may be absent) - std::string reason = std::string(matches[3]); - if (reason.empty()) - { - line = name.empty() ? "you_paid_ldollars_no_info" : "you_paid_ldollars_no_reason"; - } - else - { - std::string localized_reason; - // if we haven't found localized string for reason of payment leave it as it was - str_args["[REASON]"] = LLTrans::findString(localized_reason, reason) ? localized_reason : reason; - } - - // forming final message string by retrieving localized version from xml - // and applying previously found arguments - line = LLTrans::getString(line, str_args); - args["MESSAGE"] = line; - } + message = LLTrans::getString("you_paid_ldollars_no_reason", args); + } + else + { + // no target, no reason, you just paid money + message = LLTrans::getString("you_paid_ldollars_no_info", args); } - - LLNotificationsUtil::add("SystemMessage", args); } - - // Once the 'recent' container gets large enough, chop some - // off the beginning. - const U32 MAX_LOOKBACK = 30; - const S32 POP_FRONT_SIZE = 12; - if(recent.size() > MAX_LOOKBACK) + final_args["MESSAGE"] = message; + notification = "PaymentSent"; + } + else { + // ...someone paid you + args["NAME"] = source_slurl; + is_name_group = is_source_group; + name_id = source_id; + if (!reason.empty()) { - LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; - recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); + message = LLTrans::getString("paid_you_ldollars", args); + } + else { + message = LLTrans::getString("paid_you_ldollars_no_reason", args); } - //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; - recent.push_back(tid); + final_args["MESSAGE"] = message; + + // make notification loggable + payload["from_id"] = source_id; + notification = "PaymentReceived"; + } + + // Despite using SLURLs, wait until the name is available before + // showing the notification, otherwise the UI layout is strange and + // the user sees a "Loading..." message + if (is_name_group) + { + gCacheName->getGroup(name_id, + boost::bind(&money_balance_group_notify, + _1, _2, _3, + notification, final_args, payload)); + } + else { + LLAvatarNameCache::get(name_id, + boost::bind(&money_balance_avatar_notify, + _1, _2, + notification, final_args, payload)); } } + + bool handle_special_notification_callback(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -5145,7 +5384,7 @@ void handle_show_mean_events(void *) //LLFloaterBump::showInstance(); } -void mean_name_callback(const LLUUID &id, const std::string& first, const std::string& last, BOOL always_false) +void mean_name_callback(const LLUUID &id, const std::string& full_name, bool is_group) { if (gNoRender) { @@ -5167,8 +5406,7 @@ void mean_name_callback(const LLUUID &id, const std::string& first, const std::s LLMeanCollisionData *mcd = *iter; if (mcd->mPerp == id) { - mcd->mFirstName = first; - mcd->mLastName = last; + mcd->mFullName = full_name; } } } @@ -5222,8 +5460,7 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use { LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag); gMeanCollisionList.push_front(mcd); - const BOOL is_group = FALSE; - gCacheName->get(perp, is_group, &mean_name_callback); + gCacheName->get(perp, false, boost::bind(&mean_name_callback, _1, _2, _3)); } } } @@ -5445,7 +5682,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) // so we'll reuse the same namespace for both throttle types. std::string throttle_name = owner_name; std::string self_name; - LLAgentUI::buildName( self_name ); + LLAgentUI::buildFullname( self_name ); if( owner_name == self_name ) { throttle_name = taskid.getString(); @@ -5481,7 +5718,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) S32 count = 0; LLSD args; args["OBJECTNAME"] = object_name; - args["NAME"] = owner_name; + args["NAME"] = LLCacheName::cleanFullName(owner_name); // check the received permission flags against each permission for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) @@ -5874,15 +6111,14 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) // Record the offer. { std::string target_name; - gCacheName->getFullName(target_id, target_name); + gCacheName->getFullName(target_id, target_name); // for im log filenames LLSD args; - args["TO_NAME"] = target_name; + args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; LLSD payload; //*TODO please rewrite all keys to the same case, lower or upper payload["from_id"] = target_id; - payload["SESSION_NAME"] = target_name; payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("TeleportOfferSent", args, payload); } @@ -6096,8 +6332,7 @@ void process_script_dialog(LLMessageSystem* msg, void**) LLNotificationPtr notification; if (!first_name.empty()) { - args["FIRST"] = first_name; - args["LAST"] = last_name; + args["NAME"] = LLCacheName::buildFullName(first_name, last_name); notification = LLNotifications::instance().add( LLNotification::Params("ScriptDialog").substitutions(args).payload(payload).form_elements(form.asLLSD())); } @@ -6130,7 +6365,7 @@ static LLNotificationFunctorRegistration callback_load_url_reg("LoadWebPage", ca // We've got the name of the person who owns the object hurling the url. // Display confirmation dialog. -void callback_load_url_name(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +void callback_load_url_name(const LLUUID& id, const std::string& full_name, bool is_group) { std::vector<LLSD>::iterator it; for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); ) @@ -6143,11 +6378,11 @@ void callback_load_url_name(const LLUUID& id, const std::string& first, const st std::string owner_name; if (is_group) { - owner_name = first + LLTrans::getString("Group"); + owner_name = full_name + LLTrans::getString("Group"); } else { - owner_name = first + " " + last; + owner_name = full_name; } // For legacy name-only mutes. @@ -6207,7 +6442,8 @@ void process_load_url(LLMessageSystem* msg, void**) // Add to list of pending name lookups gLoadUrlList.push_back(payload); - gCacheName->get(owner_id, owner_is_group, &callback_load_url_name); + gCacheName->get(owner_id, owner_is_group, + boost::bind(&callback_load_url_name, _1, _2, _3)); } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index ee89680fea..17aac1945b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -69,6 +69,7 @@ #include "llface.h" #include "llfloaterproperties.h" #include "llfollowcam.h" +#include "llhudtext.h" #include "llselectmgr.h" #include "llrendersphere.h" #include "lltooldraganddrop.h" @@ -1080,7 +1081,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // alpha was flipped so that it zero encoded better coloru.mV[3] = 255 - coloru.mV[3]; mText->setColor(LLColor4(coloru)); - mText->setStringUTF8(temp_string); + mText->setString(temp_string); if (mDrawable.notNull()) { @@ -1472,7 +1473,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, dp->unpackBinaryDataFixed(coloru.mV, 4, "Color"); coloru.mV[3] = 255 - coloru.mV[3]; mText->setColor(LLColor4(coloru)); - mText->setStringUTF8(temp_string); + mText->setString(temp_string); setChanged(TEXTURE); } @@ -4132,7 +4133,7 @@ void LLViewerObject::setDebugText(const std::string &utf8text) mText->setOnHUDAttachment(isHUDAttachment()); } mText->setColor(LLColor4::white); - mText->setStringUTF8(utf8text); + mText->setString(utf8text); mText->setZCompare(FALSE); mText->setDoFade(FALSE); updateText(); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index be83fb7ef8..346f33727f 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -37,7 +37,6 @@ #include "llassetstorage.h" #include "lldarrayptr.h" -#include "llhudtext.h" #include "llhudicon.h" #include "llinventory.h" #include "llrefcount.h" @@ -60,6 +59,7 @@ class LLColor4; class LLFrameTimer; class LLDrawable; class LLHost; +class LLHUDText; class LLWorld; class LLNameValue; class LLNetMap; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 752aeaaab0..f682a05532 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -53,7 +53,7 @@ #include "lltooltip.h" #include "llworld.h" #include "llstring.h" -#include "llhudtext.h" +#include "llhudnametag.h" #include "lldrawable.h" #include "xform.h" #include "llsky.h" @@ -1201,7 +1201,7 @@ void LLViewerObjectList::generatePickList(LLCamera &camera) } } - LLHUDText::addPickable(mSelectPickList); + LLHUDNameTag::addPickable(mSelectPickList); for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); iter != LLCharacter::sInstances.end(); ++iter) diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index a591cc1e14..8e1ce03964 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -2075,10 +2075,7 @@ void LLViewerParcelMgr::deedLandToGroup() args["GROUP_NAME"] = group_name; if(mCurrentParcel->getContributeWithDeed()) { - std::string first_name, last_name; - gCacheName->getName(mCurrentParcel->getOwnerID(), first_name, last_name); - args["FIRST_NAME"] = first_name; - args["LAST_NAME"] = last_name; + args["NAME"] = LLSLURL("agent", mCurrentParcel->getOwnerID(), "completename").getSLURLString(); LLNotificationsUtil::add("DeedLandToGroupWithContribution",args, LLSD(), deedAlertCB); } else diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index c48668df9a..0ef38b2d7c 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -34,7 +34,9 @@ #include "llviewerregion.h" +// linden libraries #include "indra_constants.h" +#include "llavatarnamecache.h" // name lookup cap url #include "llfloaterreg.h" #include "llmath.h" #include "llhttpclient.h" @@ -174,7 +176,9 @@ public: mRegion->showReleaseNotes(); } } - + + mRegion->setCapabilitiesReceived(true); + if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) { LLStartUp::setStartupState( STATE_SEED_CAP_GRANTED ); @@ -231,7 +235,8 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, // LLCapabilityListener binds all the globals it expects to need at // construction time. mCapabilityListener(host.getString(), gMessageSystem, *this, - gAgent.getID(), gAgent.getSessionID()) + gAgent.getID(), gAgent.getSessionID()), + mCapabilitiesReceived(false) { mWidth = region_width_meters; mOriginGlobal = from_region_handle(handle); @@ -1485,6 +1490,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) LLSD capabilityNames = LLSD::emptyArray(); capabilityNames.append("AttachmentResources"); + capabilityNames.append("AvatarPickerSearch"); capabilityNames.append("ChatSessionRequest"); capabilityNames.append("CopyInventoryFromNotecard"); capabilityNames.append("DispatchRegionInfo"); @@ -1495,6 +1501,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("ObjectMediaNavigate"); capabilityNames.append("FetchLib"); capabilityNames.append("FetchLibDescendents"); + capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetTexture"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); @@ -1516,6 +1523,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("SendUserReport"); capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); + capabilityNames.append("SetDisplayName"); capabilityNames.append("StartGroupProposal"); capabilityNames.append("TextureStats"); capabilityNames.append("UntrustedSimulatorMessage"); @@ -1573,6 +1581,16 @@ std::string LLViewerRegion::getCapability(const std::string& name) const return iter->second; } +bool LLViewerRegion::capabilitiesReceived() const +{ + return mCapabilitiesReceived; +} + +void LLViewerRegion::setCapabilitiesReceived(bool received) +{ + mCapabilitiesReceived = received; +} + void LLViewerRegion::logActiveCapabilities() const { int count = 0; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 5c4d5a61fd..400180a01c 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -235,6 +235,11 @@ public: void setCapability(const std::string& name, const std::string& url); // implements LLCapabilityProvider virtual std::string getCapability(const std::string& name) const; + + // has region received its final (not seed) capability list? + bool capabilitiesReceived() const; + void setCapabilitiesReceived(bool received); + static bool isSpecialCapabilityName(const std::string &name); void logActiveCapabilities() const; @@ -415,6 +420,7 @@ private: private: bool mAlive; // can become false if circuit disconnects + bool mCapabilitiesReceived; //spatial partitions for objects in this region std::vector<LLSpatialPartition*> mObjectPartition; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index d91f232f0e..96a6f56605 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -120,6 +120,7 @@ #include "llglheaders.h" #include "lltooltip.h" #include "llhudmanager.h" +#include "llhudobject.h" #include "llhudview.h" #include "llimagebmp.h" #include "llimagej2c.h" @@ -1203,12 +1204,8 @@ BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S //SetBKColor(hdc, RGB(255, 255, 255)); FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); - std::string name_str; - LLAgentUI::buildName(name_str); - std::string temp_str; - temp_str = llformat( "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ - name_str.c_str(), + temp_str = llformat( "FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ LLViewerStats::getInstance()->mFPSStat.getMeanPerSec(), LLViewerStats::getInstance()->mSimPhysicsFPS.getPrev(0), LLViewerStats::getInstance()->mSimTimeDilation.getPrev(0)); @@ -1888,7 +1885,7 @@ void LLViewerWindow::reshape(S32 width, S32 height) // clear font width caches if (display_scale_changed) { - LLHUDText::reshape(); + LLHUDObject::reshapeAll(); } sendShapeToSim(); @@ -4091,7 +4088,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei send_agent_pause(); //rescale fonts initFonts(scale_factor); - LLHUDText::reshape(); + LLHUDObject::reshapeAll(); } S32 output_buffer_offset_y = 0; @@ -4220,7 +4217,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei if (high_res) { initFonts(1.f); - LLHUDText::reshape(); + LLHUDObject::reshapeAll(); } // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 1fa953f157..dc458418d0 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,7 @@ #include <ctype.h> #include "llaudioengine.h" +#include "llcachename.h" #include "noise.h" #include "sound_ids.h" @@ -51,8 +52,10 @@ #include "llagentcamera.h" #include "llagentwearables.h" #include "llanimationstates.h" +#include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" #include "llviewercontrol.h" +#include "llcallingcard.h" // IDEVO for LLAvatarTracker #include "lldrawpoolavatar.h" #include "lldriverparam.h" #include "lleditingmotion.h" @@ -61,6 +64,8 @@ #include "llheadrotmotion.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" +#include "llhudnametag.h" +#include "llhudtext.h" // for mText/mDebugText #include "llkeyframefallmotion.h" #include "llkeyframestandmotion.h" #include "llkeyframewalkmotion.h" @@ -659,12 +664,14 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mAppearanceAnimating(FALSE), mNameString(), mTitle(), - mNameAway(FALSE), - mNameBusy(FALSE), - mNameMute(FALSE), + mNameAway(false), + mNameBusy(false), + mNameMute(false), + mNameAppearance(false), + mNameFriend(false), + mNameAlpha(0.f), mRenderGroupTitles(sRenderGroupTitles), - mNameAppearance(FALSE), - mNameCloud(FALSE), + mNameCloud(false), mFirstTEMessageReceived( FALSE ), mFirstAppearanceMessageReceived( FALSE ), mCulled( FALSE ), @@ -2766,8 +2773,18 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) && gSavedSettings.getS32("AvatarNameTagMode") )); } - if ( render_name ) + if ( !render_name ) { + if (mNameText) + { + // ...clean up old name tag + mNameText->markDead(); + mNameText = NULL; + sNumVisibleChatBubbles--; + } + return; + } + BOOL new_name = FALSE; if (visible_chat != mVisibleChat) { @@ -2783,7 +2800,6 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) // First Calculate Alpha // If alpha > 0, create mNameText if necessary, otherwise delete it - { F32 alpha = 0.f; if (mAppAngle > 5.f) { @@ -2804,66 +2820,62 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) alpha = (mAppAngle-2.f)/3.f; } - if (alpha > 0.f) + if (alpha <= 0.f) { + if (mNameText) + { + mNameText->markDead(); + mNameText = NULL; + sNumVisibleChatBubbles--; + } + return; + } + if (!mNameText) { - mNameText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT); - mNameText->setMass(10.f); + mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject( + LLHUDObject::LL_HUD_NAME_TAG) ); + //mNameText->setMass(10.f); mNameText->setSourceObject(this); - mNameText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP); + mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP); mNameText->setVisibleOffScreen(TRUE); mNameText->setMaxLines(11); mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); - mNameText->setUseBubble(TRUE); sNumVisibleChatBubbles++; new_name = TRUE; } - LLColor4 avatar_name_color = LLUIColorTable::instance().getColor( "AvatarNameColor" ); - avatar_name_color.setAlpha(alpha); - mNameText->setColor(avatar_name_color); + LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last); + mNameText->setPositionAgent(name_position); - LLQuaternion root_rot = mRoot.getWorldRotation(); - mNameText->setUsePixelSize(TRUE); - LLVector3 pixel_right_vec; - LLVector3 pixel_up_vec; - LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); - LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); - camera_to_av.normalize(); - LLVector3 local_camera_at = camera_to_av * ~root_rot; - LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); - local_camera_up.normalize(); - local_camera_up = local_camera_up * ~root_rot; + idleUpdateNameTagText(new_name); - local_camera_up.scaleVec(mBodySize * 0.5f); - local_camera_at.scaleVec(mBodySize * 0.5f); + idleUpdateNameTagAlpha(new_name, alpha); +} - LLVector3 name_position = mRoot.getWorldPosition() + - (local_camera_up * root_rot) - - (projected_vec(local_camera_at * root_rot, camera_to_av)); - name_position += pixel_up_vec * 15.f; - mNameText->setPositionAgent(name_position); - } - else if (mNameText) +void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) { - mNameText->markDead(); - mNameText = NULL; - sNumVisibleChatBubbles--; - } - } - LLNameValue *title = getNVPair("Title"); LLNameValue* firstname = getNVPair("FirstName"); LLNameValue* lastname = getNVPair("LastName"); - if (mNameText.notNull() && firstname && lastname) + // Avatars must have a first and last name + if (!firstname || !lastname) return; + + bool is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end(); + bool is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end(); + bool is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end(); + bool is_muted; + if (isSelf()) + { + is_muted = false; + } + else { - const BOOL is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end(); - const BOOL is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end(); - const BOOL is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end(); - const BOOL is_muted = isSelf() ? FALSE : LLMuteList::getInstance()->isMuted(getID()); - const BOOL is_cloud = getIsCloud(); + is_muted = LLMuteList::getInstance()->isMuted(getID()); + } + bool is_friend = LLAvatarTracker::instance().isBuddy(getID()); + bool is_cloud = getIsCloud(); if (gSavedSettings.getBOOL("DebugAvatarRezTime")) { @@ -2888,105 +2900,129 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) } } - if (mNameString.empty() || - new_name || - (!title && !mTitle.empty()) || - (title && mTitle != title->getString()) || - (is_away != mNameAway || is_busy != mNameBusy || is_muted != mNameMute) + // Rebuild name tag if state change detected + if (mNameString.empty() + || new_name + || (!title && !mTitle.empty()) + || (title && mTitle != title->getString()) + || is_away != mNameAway + || is_busy != mNameBusy + || is_muted != mNameMute || is_appearance != mNameAppearance - || is_cloud != mNameCloud - ) - { - std::string line; - if (!sRenderGroupTitles) - { - // If all group titles are turned off, stack first name - // on a line above last name - line += firstname->getString(); - line += "\n"; - } - else if (title && title->getString() && title->getString()[0] != '\0') + || is_friend != mNameFriend + || is_cloud != mNameCloud) { - line += title->getString(); - LLStringFn::replace_ascii_controlchars(line,LL_UNKNOWN_CHAR); - line += "\n"; - line += firstname->getString(); - } - else - { - line += firstname->getString(); - } + LLColor4 name_tag_color = getNameTagColor(is_friend); - line += " "; - line += lastname->getString(); - BOOL need_comma = FALSE; + clearNameTag(); - if (is_away || is_muted || is_busy) + if (is_away || is_muted || is_busy || is_appearance) { - line += " ("; + std::string line; if (is_away) { line += LLTrans::getString("AvatarAway"); - need_comma = TRUE; + line += ", "; } if (is_busy) { - if (need_comma) + line += LLTrans::getString("AvatarBusy"); + line += ", "; + } + if (is_muted) { + line += LLTrans::getString("AvatarMuted"); line += ", "; } - line += LLTrans::getString("AvatarBusy"); - need_comma = TRUE; + if (is_appearance) + { + line += LLTrans::getString("AvatarEditingAppearance"); + line += ", "; } - if (is_muted) + if (is_cloud) { - if (need_comma) + line += LLTrans::getString("LoadingData"); + line += ", "; + } + // trim last ", " + line.resize( line.length() - 2 ); + LLColor4 status_color = + LLUIColorTable::getInstance()->getColor("NameTagStatus"); + addNameTagLine(line, status_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); + } + + if (sRenderGroupTitles + && title && title->getString() && title->getString()[0] != '\0') { - line += ", "; + LLColor4 group_color = + LLUIColorTable::getInstance()->getColor("NameTagGroup"); + std::string title_str = title->getString(); + LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); + addNameTagLine(title_str, group_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); } - line += LLTrans::getString("AvatarMuted"); - need_comma = TRUE; + + static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames"); + static LLUICachedControl<bool> show_usernames("NameTagShowUsernames"); + + if (LLAvatarNameCache::useDisplayNames()) + { + LLAvatarName av_name; + if (!LLAvatarNameCache::get(getID(), &av_name)) + { + // ...call this function back when the name arrives + // and force a rebuild + LLAvatarNameCache::get(getID(), + boost::bind(&LLVOAvatar::clearNameTag, this)); } - line += ")"; + + // Might be blank if name not available yet, that's OK + if (show_display_names) + { + addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerif()); } - if (is_cloud) + // Suppress SLID display if display name matches exactly (ugh) + if (show_usernames && !av_name.mIsDisplayNameDefault) { - line += "\n"; - line += "(" + LLTrans::getString("LoadingData") + ")"; + // *HACK: Desaturate the color + LLColor4 username_color = name_tag_color * 0.83f; + addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL, + LLFontGL::getFontSansSerifSmall()); + } } - else if (is_appearance) + else { - line += "\n"; - line += LLTrans::getString("AvatarEditingAppearance"); + const LLFontGL* font = LLFontGL::getFontSansSerif(); + std::string full_name = + LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); + addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); } + mNameAway = is_away; mNameBusy = is_busy; mNameMute = is_muted; mNameAppearance = is_appearance; + mNameFriend = is_friend; mNameCloud = is_cloud; mTitle = title ? title->getString() : ""; LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR); - mNameString = utf8str_to_wstring(line); new_name = TRUE; } - if (visible_chat) + if (mVisibleChat) { - mNameText->setDropShadow(TRUE); mNameText->setFont(LLFontGL::getFontSansSerif()); - mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_LEFT); + mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT); mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f); - if (new_name) - { - mNameText->setLabel(mNameString); - } char line[MAX_STRING]; /* Flawfinder: ignore */ line[0] = '\0'; std::deque<LLChat>::iterator chat_iter = mChats.begin(); mNameText->clearString(); - LLColor4 new_chat = LLUIColorTable::instance().getColor( "AvatarNameColor" ); + LLColor4 new_chat = LLUIColorTable::instance().getColor( "NameTagChat" ); LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f); LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f); if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) @@ -3013,17 +3049,17 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) if (chat_fade_amt < 1.f) { F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f); - mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(new_chat, normal_chat, u), style); + mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style); } else if (chat_fade_amt < 2.f) { F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f); - mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(normal_chat, old_chat, u), style); + mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style); } else if (chat_fade_amt < 3.f) { // *NOTE: only remove lines down to minimum number - mNameText->addLine(utf8str_to_wstring(chat_iter->mText), old_chat, style); + mNameText->addLine(chat_iter->mText, old_chat, style); } } mNameText->setVisibleOffScreen(TRUE); @@ -3048,24 +3084,129 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) } else { - mNameText->setFont(LLFontGL::getFontSansSerif()); - mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_CENTER); + // ...not using chat bubbles, just names + mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_CENTER); mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); mNameText->setVisibleOffScreen(FALSE); - if (new_name) + } +} + +void LLVOAvatar::addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font) +{ + llassert(mNameText); + if (mVisibleChat) + { + mNameText->addLabel(line); + } + else + { + mNameText->addLine(line, color, (LLFontGL::StyleFlags)style, font); + } + mNameString += line; + mNameString += '\n'; +} + +void LLVOAvatar::clearNameTag() +{ + mNameString.clear(); + if (mNameText) { mNameText->setLabel(""); - mNameText->setString(mNameString); + mNameText->setString( "" ); + } +} + +//static +void LLVOAvatar::invalidateNameTag(const LLUUID& agent_id) +{ + LLViewerObject* obj = gObjectList.findObject(agent_id); + if (!obj) return; + + LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(obj); + if (!avatar) return; + + avatar->clearNameTag(); +} + +//static +void LLVOAvatar::invalidateNameTags() +{ + std::vector<LLCharacter*>::iterator it = LLCharacter::sInstances.begin(); + for ( ; it != LLCharacter::sInstances.end(); ++it) + { + LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(*it); + if (!avatar) continue; + if (avatar->isDead()) continue; + + avatar->clearNameTag(); } } + +// Compute name tag position during idle update +LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) +{ + LLQuaternion root_rot = mRoot.getWorldRotation(); + LLVector3 pixel_right_vec; + LLVector3 pixel_up_vec; + LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); + LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); + camera_to_av.normalize(); + LLVector3 local_camera_at = camera_to_av * ~root_rot; + LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); + local_camera_up.normalize(); + local_camera_up = local_camera_up * ~root_rot; + + local_camera_up.scaleVec(mBodySize * 0.5f); + local_camera_at.scaleVec(mBodySize * 0.5f); + + LLVector3 name_position = mRoot.getWorldPosition() + + (local_camera_up * root_rot) - + (projected_vec(local_camera_at * root_rot, camera_to_av)); + name_position += pixel_up_vec * 15.f; + return name_position; + } + +void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) +{ + llassert(mNameText); + + if (new_name + || alpha != mNameAlpha) + { + mNameText->setAlpha(alpha); + mNameAlpha = alpha; + } +} + +LLColor4 LLVOAvatar::getNameTagColor(bool is_friend) +{ + static LLUICachedControl<bool> show_friends("NameTagShowFriends"); + const char* color_name; + if (show_friends && is_friend) + { + color_name = "NameTagFriend"; + } + else if (LLAvatarNameCache::useDisplayNames()) + { + // ...color based on whether username "matches" a computed display + // name + LLAvatarName av_name; + if (LLAvatarNameCache::get(getID(), &av_name) + && av_name.mIsDisplayNameDefault) + { + color_name = "NameTagMatch"; } + else + { + color_name = "NameTagMismatch"; } - else if (mNameText) + } + else { - mNameText->markDead(); - mNameText = NULL; - sNumVisibleChatBubbles--; + // ...not using display names + color_name = "NameTagLegacy"; } + return LLUIColorTable::getInstance()->getColor( color_name ); } //-------------------------------------------------------------------- @@ -6786,7 +6927,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) { releaseComponentTextures(); } - + // parse visual params S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam); bool drop_visual_params_debug = gSavedSettings.getBOOL("BlockSomeAvatarAppearanceVisualParams") && (ll_rand(2) == 0); // pretend that ~12% of AvatarAppearance messages arrived without a VisualParam block, for testing @@ -6879,9 +7020,9 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // re-request appearance, hoping that it comes back with a shape next time llinfos << "Re-requesting AvatarAppearance for object: " << getID() << llendl; LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); - } - else - { + } + else + { llinfos << "That's okay, we already have a non-default shape for object: " << getID() << llendl; // we don't really care. } @@ -7773,9 +7914,7 @@ std::string LLVOAvatar::getFullname() const LLNameValue* last = getNVPair("LastName"); if (first && last) { - name += first->getString(); - name += " "; - name += last->getString(); + name = LLCacheName::buildFullName( first->getString(), last->getString() ); } return name; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index b30cce09f6..384ab88816 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -65,7 +65,7 @@ extern const LLUUID ANIM_AGENT_WALK_ADJUST; class LLTexLayerSet; class LLVoiceVisualizer; -class LLHUDText; +class LLHUDNameTag; class LLHUDEffectSpiral; class LLTexGlobalColor; class LLVOAvatarBoneInfo; @@ -213,6 +213,15 @@ public: void idleUpdateLoadingEffect(); void idleUpdateWindEffect(); void idleUpdateNameTag(const LLVector3& root_pos_last); + void idleUpdateNameTagText(BOOL new_name); + LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last); + void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha); + LLColor4 getNameTagColor(bool is_friend); + void clearNameTag(); + static void invalidateNameTag(const LLUUID& agent_id); + // force all name tags to rebuild, useful when display names turned on/off + static void invalidateNameTags(); + void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font); void idleUpdateRenderCost(); void idleUpdateTractorBeam(); void idleUpdateBelowWater(); @@ -829,13 +838,15 @@ protected: static void getAnimLabels(LLDynamicArray<std::string>* labels); static void getAnimNames(LLDynamicArray<std::string>* names); private: - LLWString mNameString; + std::string mNameString; // UTF-8 title + name + status std::string mTitle; - BOOL mNameAway; - BOOL mNameBusy; - BOOL mNameMute; - BOOL mNameAppearance; - BOOL mNameCloud; + bool mNameAway; + bool mNameBusy; + bool mNameMute; + bool mNameAppearance; + bool mNameFriend; + bool mNameCloud; + F32 mNameAlpha; BOOL mRenderGroupTitles; //-------------------------------------------------------------------- @@ -843,7 +854,7 @@ private: //-------------------------------------------------------------------- public: LLFrameTimer mChatTimer; - LLPointer<LLHUDText> mNameText; + LLPointer<LLHUDNameTag> mNameText; private: LLFrameTimer mTimeVisible; std::deque<LLChat> mChats; diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 573fab1f4f..1784ceaa12 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -82,6 +82,9 @@ public: virtual void getChannelInfo(); virtual BOOL isActive(); virtual BOOL callStarted(); + + // Session name is a UI label used for feedback about which person, + // group, or phone number you are talking to const std::string& getSessionName() const { return mSessionName; } boost::signals2::connection setStateChangedCallback(const state_changed_signal_t::slot_type& callback) diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index c6c155f0f0..e8ff3f6d49 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -37,6 +37,8 @@ #include "llsdutil.h" +// Linden library includes +#include "llavatarnamecache.h" #include "llvoavatarself.h" #include "llbufferstream.h" #include "llfile.h" @@ -52,6 +54,8 @@ #include "llviewercontrol.h" #include "llkeyboard.h" #include "llappviewer.h" // for gDisconnected, gDisableVoice + +// Viewer includes #include "llmutelist.h" // to check for muted avatars #include "llagent.h" #include "llcachename.h" @@ -2654,12 +2658,16 @@ void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) void LLVivoxVoiceClient::checkFriend(const LLUUID& id) { - std::string name; buddyListEntry *buddy = findBuddy(id); // Make sure we don't add a name before it's been looked up. - if(gCacheName->getFullName(id, name)) + LLAvatarName av_name; + if(LLAvatarNameCache::get(id, &av_name)) { + // *NOTE: For now, we feed legacy names to Vivox because I don't know + // if their service can support a mix of new and old clients with + // different sorts of names. + std::string name = av_name.getLegacyName(); const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); bool canSeeMeOnline = false; @@ -6185,16 +6193,18 @@ void LLVivoxVoiceClient::notifyFriendObservers() void LLVivoxVoiceClient::lookupName(const LLUUID &id) { - BOOL is_group = FALSE; - gCacheName->get(id, is_group, &LLVivoxVoiceClient::onAvatarNameLookup); + LLAvatarNameCache::get(id, + boost::bind(&LLVivoxVoiceClient::onAvatarNameCache, + this, _1, _2)); } -//static -void LLVivoxVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +void LLVivoxVoiceClient::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) { - std::string name = llformat("%s %s", first.c_str(), last.c_str()); - LLVivoxVoiceClient::getInstance()->avatarNameResolved(id, name); - + // For Vivox, we use the legacy name because I'm uncertain whether or + // not their service can tolerate switching to Username or Display Name + std::string legacy_name = av_name.getLegacyName(); + avatarNameResolved(agent_id, legacy_name); } void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 59fec8b954..341f22bd73 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -51,7 +51,7 @@ class LLVivoxProtocolParser; #endif #include "llvoiceclient.h" - +class LLAvatarName; class LLVivoxVoiceAccountProvisionResponder; class LLVivoxVoiceClientMuteListObserver; class LLVivoxVoiceClientFriendsObserver; @@ -619,7 +619,7 @@ protected: void leaveAudioSession(); void lookupName(const LLUUID &id); - static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); void avatarNameResolved(const LLUUID &id, const std::string &name); private: diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index cc65b34a61..ccd0f61fc1 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -70,6 +70,8 @@ #include "llfloaterreg.h" #include "llgldbg.h" #include "llhudmanager.h" +#include "llhudnametag.h" +#include "llhudtext.h" #include "lllightconstants.h" #include "llresmgr.h" #include "llselectmgr.h" @@ -2086,6 +2088,7 @@ void LLPipeline::shiftObjects(const LLVector3 &offset) } LLHUDText::shiftAll(offset); + LLHUDNameTag::shiftAll(offset); display_update_camera(); } @@ -5279,7 +5282,8 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start, ++iter) { LLVOAvatar* av = (LLVOAvatar*) *iter; - if (av->mNameText.notNull() && av->mNameText->lineSegmentIntersect(start, local_end, position)) + if (av->mNameText.notNull() + && av->mNameText->lineSegmentIntersect(start, local_end, position)) { drawable = av->mDrawable; local_end = position; diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index e8a893e31b..592bda63d2 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -115,9 +115,6 @@ name="AlertTextColor" value="0.58 0.66 0.84 1" /> <color - name="AvatarNameColor" - reference="White" /> - <color name="AvatarListItemIconDefaultColor" reference="White" /> <color @@ -528,6 +525,38 @@ <color name="MultiSliderTriangleColor" reference="Unused?" /> + <!-- + <color + name="NameTagBackground" + value="0.85 0.85 0.85 0.80" /> + --> + <color + name="NameTagBackground" + value="0 0 0 1" /> + <color + name="NameTagChat" + reference="White" /> + <color + name="NameTagFriend" + value="0.447 0.784 0.663 1" /> + <color + name="NameTagGroup" + value="1 1 1 1" /> + <color + name="NameTagLegacy" + reference="White" /> + <color + name="NameTagMatch" + reference="White" /> + <color + name="NameTagMismatch" + reference="White" /> + <color + name="NameTagSLID" + value="1 1 1 1" /> + <color + name="NameTagStatus" + value="1 1 1 1" /> <color name="NetMapBackgroundColor" value="0 0 0 1" /> @@ -562,6 +591,9 @@ name="NotifyTextColor" reference="White" /> <color + name="ObjectBubbleColor" + reference="DkGray_66" /> + <color name="ObjectChatColor" reference="EmphasisColor" /> <color diff --git a/indra/newview/skins/default/textures/Rounded_Rect.png b/indra/newview/skins/default/textures/Rounded_Rect.png Binary files differnew file mode 100644 index 0000000000..c270c28039 --- /dev/null +++ b/indra/newview/skins/default/textures/Rounded_Rect.png diff --git a/indra/newview/skins/default/textures/icons/Person_Check.png b/indra/newview/skins/default/textures/icons/Person_Check.png Binary files differnew file mode 100644 index 0000000000..f8638540d4 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Person_Check.png diff --git a/indra/newview/skins/default/textures/icons/Person_Star.png b/indra/newview/skins/default/textures/icons/Person_Star.png Binary files differnew file mode 100644 index 0000000000..ad10580ac4 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Person_Star.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 93c805f724..b3314aa32f 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -390,6 +390,8 @@ with the same filename but different name <texture name="Pause_Off" file_name="icons/Pause_Off.png" preload="false" /> <texture name="Pause_Over" file_name="icons/Pause_Over.png" preload="false" /> <texture name="Pause_Press" file_name="icons/Pause_Press.png" preload="false" /> + <texture name="Person_Check" file_name="icons/Person_Check.png" preload="false" /> + <texture name="Person_Star" file_name="icons/Person_Star.png" preload="false" /> <texture name="Play_Off" file_name="icons/Play_Off.png" preload="false" /> <texture name="Play_Over" file_name="icons/Play_Over.png" preload="false" /> <texture name="Play_Press" file_name="icons/Play_Press.png" preload="false" /> @@ -419,6 +421,7 @@ with the same filename but different name <texture name="Resize_Corner" file_name="windows/Resize_Corner.png" preload="true" /> + <texture name="Rounded_Rect" file_name="Rounded_Rect.png" preload="true" scale.left="6" scale.top="24" scale.right="58" scale.bottom="6" /> <texture name="Rounded_Square" file_name="rounded_square.j2c" preload="true" scale.left="16" scale.top="16" scale.right="112" scale.bottom="16" /> <texture name="Row_Selection" file_name="navbar/Row_Selection.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/da/floater_windlight_options.xml b/indra/newview/skins/default/xui/da/floater_windlight_options.xml index 65f3f67a70..56f94b24e9 100644 --- a/indra/newview/skins/default/xui/da/floater_windlight_options.xml +++ b/indra/newview/skins/default/xui/da/floater_windlight_options.xml @@ -1,18 +1,18 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater name="WindLight floater" title="AVANCERET OPSÆTNING FOR HIMMEL"> <text name="KeyFramePresetsText"> Faste indstillinger: </text> - <button label="Ny" label_selected="Ny" name="WLNewPreset"/> - <button label="Gem" label_selected="Gem" name="WLSavePreset"/> - <button label="Slet" label_selected="Slet" name="WLDeletePreset"/> - <button label="Dags cyklus" label_selected="Dags cyklus" name="WLDayCycleMenuButton"/> + <button label="Ny" label_selected="Ny" name="WLNewPreset" /> + <button label="Gem" label_selected="Gem" name="WLSavePreset" /> + <button label="Slet" label_selected="Slet" name="WLDeletePreset" /> + <button label="Dags cyklus" label_selected="Dags cyklus" name="WLDayCycleMenuButton" /> <tab_container name="WindLight Tabs"> <panel label="ATMOSFÆRE" name="Atmosphere"> <text name="BHText"> Blå - horisont </text> - <button label="?" name="WLBlueHorizonHelp"/> + <button label="?" name="WLBlueHorizonHelp" /> <text name="BHText2"> R </text> @@ -25,19 +25,19 @@ <text name="BHText5"> I </text> - <slider label="" name="WLBlueHorizonR"/> - <slider label="" name="WLBlueHorizonG"/> - <slider label="" name="WLBlueHorizonB"/> - <slider label="" name="WLBlueHorizonI"/> + <slider label="" name="WLBlueHorizonR" /> + <slider label="" name="WLBlueHorizonG" /> + <slider label="" name="WLBlueHorizonB" /> + <slider label="" name="WLBlueHorizonI" /> <text name="BDensText"> Dis - horisont </text> - <button label="?" name="WLHazeHorizonHelp"/> - <slider label="" name="WLHazeHorizon"/> + <button label="?" name="WLHazeHorizonHelp" /> + <slider label="" name="WLHazeHorizon" /> <text name="BDensText2"> Blå - tæthed </text> - <button label="?" name="WLBlueDensityHelp"/> + <button label="?" name="WLBlueDensityHelp" /> <text name="BHText6"> R </text> @@ -50,36 +50,36 @@ <text name="BHText9"> I </text> - <slider label="" name="WLBlueDensityR"/> - <slider label="" name="WLBlueDensityG"/> - <slider label="" name="WLBlueDensityB"/> - <slider label="" name="WLBlueDensityI"/> + <slider label="" name="WLBlueDensityR" /> + <slider label="" name="WLBlueDensityG" /> + <slider label="" name="WLBlueDensityB" /> + <slider label="" name="WLBlueDensityI" /> <text name="HDText"> Dis - intensitet </text> - <button label="?" name="WLHazeDensityHelp"/> - <slider label="" name="WLHazeDensity"/> + <button label="?" name="WLHazeDensityHelp" /> + <slider label="" name="WLHazeDensity" /> <text name="DensMultText"> Densitet faktor </text> - <button label="?" name="WLDensityMultHelp"/> - <slider label="" name="WLDensityMult"/> + <button label="?" name="WLDensityMultHelp" /> + <slider label="" name="WLDensityMult" /> <text name="WLDistanceMultText"> Distance faktor </text> - <button label="?" name="WLDistanceMultHelp"/> - <slider label="" name="WLDistanceMult"/> + <button label="?" name="WLDistanceMultHelp" /> + <slider label="" name="WLDistanceMult" /> <text name="MaxAltText"> Maximum højde </text> - <button label="?" name="WLMaxAltitudeHelp"/> - <slider label="" name="WLMaxAltitude"/> + <button label="?" name="WLMaxAltitudeHelp" /> + <slider label="" name="WLMaxAltitude" /> </panel> <panel label="LYS" name="Lighting"> <text name="SLCText"> Sol/Måne farve </text> - <button label="?" name="WLSunlightColorHelp"/> + <button label="?" name="WLSunlightColorHelp" /> <text name="BHText"> R </text> @@ -92,19 +92,19 @@ <text name="BHText4"> I </text> - <slider label="" name="WLSunlightR"/> - <slider label="" name="WLSunlightG"/> - <slider label="" name="WLSunlightB"/> - <slider label="" name="WLSunlightI"/> + <slider label="" name="WLSunlightR" /> + <slider label="" name="WLSunlightG" /> + <slider label="" name="WLSunlightB" /> + <slider label="" name="WLSunlightI" /> <text name="TODText"> Sol/Måne position </text> - <button label="?" name="WLTimeOfDayHelp"/> - <slider label="" name="WLSunAngle"/> + <button label="?" name="WLTimeOfDayHelp" /> + <slider label="" name="WLSunAngle" /> <text name="WLAmbientText"> Omgivende </text> - <button label="?" name="WLAmbientHelp"/> + <button label="?" name="WLAmbientHelp" /> <text name="BHText5"> R </text> @@ -117,37 +117,37 @@ <text name="BHText8"> I </text> - <slider label="" name="WLAmbientR"/> - <slider label="" name="WLAmbientG"/> - <slider label="" name="WLAmbientB"/> - <slider label="" name="WLAmbientI"/> + <slider label="" name="WLAmbientR" /> + <slider label="" name="WLAmbientG" /> + <slider label="" name="WLAmbientB" /> + <slider label="" name="WLAmbientI" /> <text name="WLEastAngleText"> Øst vinkel </text> - <button label="?" name="WLEastAngleHelp"/> - <slider label="" name="WLEastAngle"/> + <button label="?" name="WLEastAngleHelp" /> + <slider label="" name="WLEastAngle" /> <text name="SunGlowText"> Sol glød </text> - <button label="?" name="WLSunGlowHelp"/> - <slider label="Fokus " name="WLGlowB"/> - <slider label="Størr. " name="WLGlowR"/> + <button label="?" name="WLSunGlowHelp" /> + <slider label="Fokus " name="WLGlowB" /> + <slider label="Størr. " name="WLGlowR" /> <text name="SceneGammaText"> Lysintensitet (gamma) </text> - <button label="?" name="WLSceneGammaHelp"/> - <slider label="" name="WLGamma"/> + <button label="?" name="WLSceneGammaHelp" /> + <slider label="" name="WLGamma" /> <text name="WLStarText"> Stjerne intensitet </text> - <button label="?" name="WLStarBrightnessHelp"/> - <slider label="" name="WLStarAlpha"/> + <button label="?" name="WLStarBrightnessHelp" /> + <slider label="" name="WLStarAlpha" /> </panel> <panel label="SKYER" name="Clouds"> <text name="WLCloudColorText"> Farve på skyer </text> - <button label="?" name="WLCloudColorHelp"/> + <button label="?" name="WLCloudColorHelp" /> <text name="BHText"> R </text> @@ -160,14 +160,14 @@ <text name="BHText4"> I </text> - <slider label="" name="WLCloudColorR"/> - <slider label="" name="WLCloudColorG"/> - <slider label="" name="WLCloudColorB"/> - <slider label="" name="WLCloudColorI"/> + <slider label="" name="WLCloudColorR" /> + <slider label="" name="WLCloudColorG" /> + <slider label="" name="WLCloudColorB" /> + <slider label="" name="WLCloudColorI" /> <text name="WLCloudColorText2"> Skyer XY/Tæthed </text> - <button label="?" name="WLCloudDensityHelp"/> + <button label="?" name="WLCloudDensityHelp" /> <text name="BHText5"> X </text> @@ -177,23 +177,23 @@ <text name="BHText7"> T </text> - <slider label="" name="WLCloudX"/> - <slider label="" name="WLCloudY"/> - <slider label="" name="WLCloudDensity"/> + <slider label="" name="WLCloudX" /> + <slider label="" name="WLCloudY" /> + <slider label="" name="WLCloudDensity" /> <text name="WLCloudCoverageText"> Skydække </text> - <button label="?" name="WLCloudCoverageHelp"/> - <slider label="" name="WLCloudCoverage"/> + <button label="?" name="WLCloudCoverageHelp" /> + <slider label="" name="WLCloudCoverage" /> <text name="WLCloudScaleText"> Skystørrelse </text> - <button label="?" name="WLCloudScaleHelp"/> - <slider label="" name="WLCloudScale"/> + <button label="?" name="WLCloudScaleHelp" /> + <slider label="" name="WLCloudScale" /> <text name="WLCloudDetailText"> Sky detaljer(XY/tæthed) </text> - <button label="?" name="WLCloudDetailHelp"/> + <button label="?" name="WLCloudDetailHelp" /> <text name="BHText8"> X </text> @@ -203,23 +203,23 @@ <text name="BHText10"> T </text> - <slider label="" name="WLCloudDetailX"/> - <slider label="" name="WLCloudDetailY"/> - <slider label="" name="WLCloudDetailDensity"/> + <slider label="" name="WLCloudDetailX" /> + <slider label="" name="WLCloudDetailY" /> + <slider label="" name="WLCloudDetailDensity" /> <text name="WLCloudScrollXText"> Sky drift X </text> - <button label="?" name="WLCloudScrollXHelp"/> - <check_box label="Lås" name="WLCloudLockX"/> - <slider label="" name="WLCloudScrollX"/> + <button label="?" name="WLCloudScrollXHelp" /> + <check_box label="Lås" name="WLCloudLockX" /> + <slider label="" name="WLCloudScrollX" /> <text name="WLCloudScrollYText"> Sky drift Y </text> - <button label="?" name="WLCloudScrollYHelp"/> - <check_box label="Lås" name="WLCloudLockY"/> - <slider label="" name="WLCloudScrollY"/> - <check_box label="Benyt simple skyer" name="DrawClassicClouds"/> - <button label="?" name="WLClassicCloudsHelp"/> + <button label="?" name="WLCloudScrollYHelp" /> + <check_box label="Lås" name="WLCloudLockY" /> + <slider label="" name="WLCloudScrollY" /> + <check_box label="Benyt simple skyer" name="DrawClassicClouds" /> + <button label="?" name="WLClassicCloudsHelp" /> </panel> </tab_container> <string name="WLDefaultSkyNames"> diff --git a/indra/newview/skins/default/xui/da/panel_classified_info.xml b/indra/newview/skins/default/xui/da/panel_classified_info.xml index 28f8936457..0bb52b8fa6 100644 --- a/indra/newview/skins/default/xui/da/panel_classified_info.xml +++ b/indra/newview/skins/default/xui/da/panel_classified_info.xml @@ -40,7 +40,7 @@ </layout_panel> <layout_panel name="descr_layout_panel"> <text name="classified_desc_label" value="Beskrivelse:"/> - <text_editor name="classified_desc" value="[description]"/> + <text_editor name="classified_desc" value="[description]"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 54ef6d65a7..914268a9d2 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -373,7 +373,8 @@ Leyla Linden </text> left_delta="-199" name="For sale to" top_delta="20" - width="186"> + use_ellipses="true" + width="340"> For sale to: [BUYER] </text> <text @@ -409,7 +410,7 @@ Leyla Linden </text> right="-10" name="Cancel Land Sale" left_pad="5" - top_pad="-25" + top_pad="-15" width="180" /> <text type="string" @@ -540,7 +541,7 @@ Leyla Linden </text> layout="topleft" left_delta="0" name="Reclaim Land..." - top_delta="-61" + top_delta="-50" width="180" /> <button enabled="false" diff --git a/indra/newview/skins/default/xui/en/floater_avatar_picker.xml b/indra/newview/skins/default/xui/en/floater_avatar_picker.xml index f59badfcb4..a11946d352 100644 --- a/indra/newview/skins/default/xui/en/floater_avatar_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_avatar_picker.xml @@ -5,11 +5,11 @@ height="350" layout="topleft" min_height="200" - min_width="265" + min_width="400" name="avatarpicker" help_topic="avatarpicker" title="CHOOSE RESIDENT" - width="265"> + width="500"> <floater.string name="not_found"> '[TEXT]' not found @@ -40,7 +40,7 @@ name="ResidentChooserTabs" tab_position="top" top="20" - width="265"> + width="500"> <panel border="none" height="150" @@ -83,14 +83,24 @@ left_pad="5" name="Find" width="45" /> - <scroll_list - follows="all" - height="98" - layout="topleft" - left="0" - name="SearchResults" - top="52" - width="132" /> + <scroll_list + draw_heading="true" + follows="all" + height="98" + layout="topleft" + left="0" + name="SearchResults" + top="52" + width="132"> + <columns + label="Name" + name="name" + width="150" /> + <columns + label="Username" + name="username" + width="150" /> + </scroll_list> </panel> <panel border="none" @@ -128,7 +138,7 @@ </button> <scroll_list follows="all" - height="100" + height="120" border="false" layout="topleft" left="0" @@ -194,7 +204,8 @@ width="28" name="Refresh" image_overlay="Refresh_Off" /> - <scroll_list + <scroll_list + draw_heading="true" follows="all" height="100" border="false" @@ -202,21 +213,30 @@ left="0" name="NearMe" sort_column="0" - top="50" - width="132" /> + top="52" + width="132"> + <columns + label="Name" + name="name" + width="150" /> + <columns + label="Username" + name="username" + width="150" /> + </scroll_list> </panel> </tab_container> <button - follows="right|bottom" + follows="left|bottom" height="23" label="OK" label_selected="OK" name="ok_btn" top_pad="3" - left="46" + left="10" width="100" /> <button - follows="right|bottom" + follows="left|bottom" height="23" label="Cancel" label_selected="Cancel" diff --git a/indra/newview/skins/default/xui/en/floater_bumps.xml b/indra/newview/skins/default/xui/en/floater_bumps.xml index 303c28d7c8..1f2fe62b3c 100644 --- a/indra/newview/skins/default/xui/en/floater_bumps.xml +++ b/indra/newview/skins/default/xui/en/floater_bumps.xml @@ -14,23 +14,23 @@ </floater.string> <floater.string name="bump"> - [TIME] [FIRST] [LAST] bumped you + [TIME] [NAME] bumped you </floater.string> <floater.string name="llpushobject"> - [TIME] [FIRST] [LAST] pushed you with a script + [TIME] [NAME] pushed you with a script </floater.string> <floater.string name="selected_object_collide"> - [TIME] [FIRST] [LAST] hit you with an object + [TIME] [NAME] hit you with an object </floater.string> <floater.string name="scripted_object_collide"> - [TIME] [FIRST] [LAST] hit you with a scripted object + [TIME] [NAME] hit you with a scripted object </floater.string> <floater.string name="physical_object_collide"> - [TIME] [FIRST] [LAST] hit you with a physical object + [TIME] [NAME] hit you with a physical object </floater.string> <floater.string name="timeStr"> diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index df44b61632..d8a929eee2 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -564,7 +564,7 @@ sold with objects name="US$6.00/month,billedannually" /> </combo_box> <locate - height="10" + height="5" layout="topleft" /> <icon follows="top|left" @@ -584,7 +584,7 @@ sold with objects left="72" name="land_use_action" right="438" - top="284" + top="279" width="218"> Increase your monthly land use fees to US$ 40/month. </text> @@ -603,7 +603,7 @@ sold with objects This parcel is 512 m² of land. </text> <locate - height="10" + height="5" layout="topleft" /> <icon follows="top|left" @@ -616,14 +616,15 @@ This parcel is 512 m² of land. <text type="string" length="1" - bottom_delta="-38" + top_delta="10" follows="top|left" font="SansSerifBig" - height="16" + height="32" layout="topleft" left="72" name="purchase_action" - right="438"> + right="438" + wrap="true"> Pay Joe Resident L$ 4000 for the land </text> <text @@ -646,7 +647,7 @@ This parcel is 512 m² of land. left_delta="0" name="currency_action" top_pad="9" - width="90"> + width="95"> Buy additional L$ </text> <locate @@ -661,7 +662,7 @@ This parcel is 512 m² of land. layout="topleft" left="170" name="currency_amt" - top="408" + top_pad="-25" width="80"> 1000 </line_editor> diff --git a/indra/newview/skins/default/xui/en/floater_buy_object.xml b/indra/newview/skins/default/xui/en/floater_buy_object.xml index 3d8f5d678b..db595458c0 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_object.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_object.xml @@ -2,7 +2,7 @@ <floater legacy_header_height="18" can_resize="true" - height="290" + height="310" layout="topleft" min_height="150" min_width="225" @@ -84,15 +84,18 @@ length="1" follows="left|right|bottom" font="SansSerif" - height="16" + height="35" layout="topleft" left_delta="0" + line_spacing.pixels="7" name="buy_text" text_color="white" top_pad="5" use_ellipses="true" - width="260"> - Buy for L$[AMOUNT] from [NAME]? + width="260" + word_wrap="true"> +Buy for L$[AMOUNT] from: +[NAME]? </text> <button follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml index 24fff6d4ae..81194f61cf 100644 --- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml +++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml @@ -8,7 +8,7 @@ layout="topleft" name="incoming call" help_topic="incoming_call" - title="UNKNOWN PERSON IS CALLING" + title="Incoming call" width="410"> <floater.string name="lifetime"> diff --git a/indra/newview/skins/default/xui/en/floater_pay.xml b/indra/newview/skins/default/xui/en/floater_pay.xml index 509cffe490..9cb251e167 100644 --- a/indra/newview/skins/default/xui/en/floater_pay.xml +++ b/indra/newview/skins/default/xui/en/floater_pay.xml @@ -7,7 +7,7 @@ name="Give Money" help_topic="give_money" save_rect="true" - width="225"> + width="250"> <string name="payee_group"> Pay Group @@ -16,28 +16,7 @@ name="payee_resident"> Pay Resident </string> - <text - type="string" - length="1" - follows="left|top" - height="18" - layout="topleft" - left="12" - name="payee_label" - top="22" - width="75"> - Pay: - </text> - <icon - height="16" - width="16" - image_name="Generic_Person" - mouse_opaque="true" - name="icon_person" - tool_tip="Person" - top_pad="0" - left="10" - /> + <text type="string" length="1" @@ -45,17 +24,18 @@ font="SansSerifSmall" height="16" layout="topleft" - left_pad="7" + left="10" name="payee_name" - width="210"> - [FIRST] [LAST] + top="25" + width="230"> + Test Name That Is Extremely Long To Check Clipping </text> <button height="23" label="L$1" label_selected="L$1" layout="topleft" - left="25" + left="35" name="fastpay 1" top_pad="8" width="80" /> @@ -72,7 +52,7 @@ label="L$10" label_selected="L$10" layout="topleft" - left="25" + left="35" name="fastpay 10" top_pad="8" width="80" /> @@ -90,7 +70,7 @@ follows="left|top" height="18" layout="topleft" - left="25" + left="35" name="amount text" top_pad="8" width="180"> @@ -102,7 +82,7 @@ height="19" top_pad="0" layout="topleft" - left="120" + left="130" max_length="9" name="amount" width="80" /> @@ -112,16 +92,16 @@ label="Pay" label_selected="Pay" layout="topleft" - left="10" + left="20" name="pay btn" - top_pad="5" + top_pad="15" width="100" /> <button height="23" label="Cancel" label_selected="Cancel" layout="topleft" - left_pad="5" + left_pad="10" name="cancel btn" width="100" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_pay_object.xml b/indra/newview/skins/default/xui/en/floater_pay_object.xml index d09a0a0535..c65dd6e49f 100644 --- a/indra/newview/skins/default/xui/en/floater_pay_object.xml +++ b/indra/newview/skins/default/xui/en/floater_pay_object.xml @@ -2,7 +2,7 @@ <floater legacy_header_height="18" can_minimize="false" - height="220" + height="225" layout="topleft" name="Give Money" help_topic="give_money" @@ -16,27 +16,15 @@ name="payee_resident"> Pay Resident </string> - <icon - height="16" - width="16" - image_name="Generic_Person" - mouse_opaque="true" - name="icon_person" - tool_tip="Person" - top_pad="24" - left="10" - /> <text - type="string" - length="1" follows="left|top" height="16" layout="topleft" - left_pad="7" - top_delta="3" + left="10" + top_pad="24" name="payee_name" - width="184"> - [FIRST] [LAST] + width="200"> + Ericacita Moostopolison </text> <text type="string" @@ -45,9 +33,9 @@ halign="left" height="14" layout="topleft" - left="34" + left="10" name="object_name_label" - top_pad="0" + top_pad="5" width="180"> Via object: </text> @@ -58,7 +46,7 @@ mouse_opaque="true" name="icon_object" tool_tip="Objects" - top_pad="0" + top_pad="5" left="10" /> <text diff --git a/indra/newview/skins/default/xui/en/floater_report_abuse.xml b/indra/newview/skins/default/xui/en/floater_report_abuse.xml index 21c0bfef48..47383c8010 100644 --- a/indra/newview/skins/default/xui/en/floater_report_abuse.xml +++ b/indra/newview/skins/default/xui/en/floater_report_abuse.xml @@ -373,7 +373,6 @@ height="23" layout="topleft" left_delta="0" - max_length="32" name="abuser_name_edit" top_pad="0" width="195" /> diff --git a/indra/newview/skins/default/xui/en/floater_sell_land.xml b/indra/newview/skins/default/xui/en/floater_sell_land.xml index 4cae42bcfe..619669d28a 100644 --- a/indra/newview/skins/default/xui/en/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/en/floater_sell_land.xml @@ -164,7 +164,7 @@ left_delta="0" name="sell_to_agent" top_pad="4" - width="130" /> + width="170" /> <button height="20" label="Select" diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index efc1a66d95..2296d7e681 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -253,7 +253,7 @@ height="28" control_name="EditLinkedParts" label="Edit linked" - layout="topleft" + layout="topleft" name="checkbox edit linked parts" top_pad="0"> <check_box.commit_callback @@ -771,7 +771,7 @@ name="General" top="16" width="295"> -<panel.string + <panel.string name="text deed continued"> Deed </panel.string> @@ -824,7 +824,7 @@ height="10" left="10" name="Name:" - top="0" + top="4" width="90"> Name: </text> @@ -863,19 +863,23 @@ height="19" layout="topleft" name="Creator:" + top_pad="7" width="90"> Creator: </text> + <!-- *NOTE: Intentionally wide for long names --> <text type="string" length="1" follows="left|top" left_pad="0" - height="19" + height="30" layout="topleft" name="Creator Name" - width="175"> - Esbee Linden + top_delta="0" + width="190" + word_wrap="true"> + Mrs. Esbee Linden (esbee.linden) </text> <text type="string" @@ -885,19 +889,23 @@ height="19" layout="topleft" name="Owner:" + top_pad="3" width="90"> Owner: </text> + <!-- *NOTE: Intentionally wide for long names --> <text type="string" length="1" follows="left|top" - height="19" + height="30" layout="topleft" name="Owner Name" left_pad="0" - width="175"> - Erica Linden + top_delta="0" + width="190" + word_wrap="true"> + Mrs. Erica "Moose" Linden (erica.linden) </text> <text type="string" @@ -907,7 +915,7 @@ left="10" height="18" name="Group:" - top_pad="4" + top_pad="7" width="75"> Group: </text> @@ -960,7 +968,7 @@ follows="left|top" height="16" layout="topleft" - top_pad="15" + top_pad="20" left="10" name="label click action" width="98"> @@ -1066,8 +1074,8 @@ even though the user gets a free copy. bg_alpha_color="DkGray" name="perms_build" left="0" - top="241" - height="130" + top_pad="4" + height="105" width="290"> <text type="string" @@ -1138,14 +1146,16 @@ even though the user gets a free copy. top_delta="0" tool_tip="Next owner can give away or resell this object" width="100" /> +<!-- *NOTE: These "B/O/G/E/N/F fields may overlap "perm_modify" above, + but that's OK, this is used only for debugging. --> <text type="string" text_color="EmphasisColor" length="1" - top_pad="5" + top="9" follows="left|top" layout="topleft" - left="10" + left="230" name="B:" height="10" width="80"> @@ -1157,7 +1167,8 @@ even though the user gets a free copy. length="1" follows="left|top" layout="topleft" - left_pad="0" + left_delta="0" + top_pad="2" name="O:" height="10" width="80"> @@ -1169,7 +1180,8 @@ even though the user gets a free copy. length="1" follows="left|top" layout="topleft" - left_pad="0" + left_delta="0" + top_pad="2" name="G:" height="10" width="80"> @@ -1180,7 +1192,7 @@ even though the user gets a free copy. text_color="White" length="1" follows="left|top" - left="10" + left_delta="0" top_pad="2" layout="topleft" name="E:" @@ -1194,7 +1206,8 @@ even though the user gets a free copy. length="1" follows="left|top" layout="topleft" - left_pad="0" + left_delta="0" + top_pad="2" name="N:" height="10" width="80"> @@ -1206,7 +1219,8 @@ even though the user gets a free copy. length="1" follows="left|top" layout="topleft" - left_pad="0" + left_delta="0" + top_pad="2" name="F:" height="10" width="80"> @@ -1214,6 +1228,7 @@ even though the user gets a free copy. </text> </panel> </panel> + <!-- Object tab --> <panel border="false" follows="all" @@ -2934,4 +2949,5 @@ even though the user gets a free copy. top_pad="4" width="125" /> </panel> +<!-- end of tabs --> </floater> diff --git a/indra/newview/skins/default/xui/en/inspect_avatar.xml b/indra/newview/skins/default/xui/en/inspect_avatar.xml index 194ae151d2..a7ab021225 100644 --- a/indra/newview/skins/default/xui/en/inspect_avatar.xml +++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml @@ -9,7 +9,7 @@ bg_opaque_image="Inspector_Background" can_close="false" can_minimize="false" - height="148" + height="164" layout="topleft" name="inspect_avatar" single_instance="true" @@ -47,9 +47,20 @@ follows="top|left" height="16" left="8" + name="user_slid" + font="SansSerifSmall" + text_color="White" + value="James.pinden" + width="175" + use_ellipses="true" /> + <text + follows="top|left" + height="16" + left="8" name="user_subtitle" font="SansSerifSmall" text_color="White" + top_pad="0" value="11 Months, 3 days old" width="175" use_ellipses="true" /> @@ -108,7 +119,7 @@ height="20" label="Add Friend" left="8" - top="119" + top="135" name="add_friend_btn" width="90" /> <button diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index aff8f7ddf4..56ecf061d5 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2633,6 +2633,12 @@ <menu_item_call.on_click function="Advanced.SendTestIMs" /> </menu_item_call> + <menu_item_call + label="Flush Names Caches" + name="Flush Names Caches"> + <menu_item_call.on_click + function="Advanced.FlushNameCaches" /> + </menu_item_call> </menu> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 5ead756d20..08302432ad 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -257,7 +257,7 @@ Save all changes to clothing/body parts? name="GrantModifyRights" type="alertmodal"> Granting modify rights to another Resident allows them to change, delete or take ANY objects you may have in-world. Be VERY careful when handing out this permission. -Do you want to grant modify rights for [FIRST_NAME] [LAST_NAME]? +Do you want to grant modify rights for [NAME]? <usetemplate name="okcancelbuttons" notext="No" @@ -280,7 +280,7 @@ Do you want to grant modify rights for the selected Residents? icon="alertmodal.tga" name="RevokeModifyRights" type="alertmodal"> -Do you want to revoke modify rights for [FIRST_NAME] [LAST_NAME]? +Do you want to revoke modify rights for [NAME]? <usetemplate name="okcancelbuttons" notext="No" @@ -2011,20 +2011,6 @@ You do not have permission to buy land for your active group. <notification icon="alertmodal.tga" label="Add Friend" - name="AddFriend" - type="alertmodal"> -Friends can give permissions to track each other on the map and receive online status updates. - -Offer friendship to [NAME]? - <usetemplate - name="okcancelbuttons" - notext="Cancel" - yestext="OK"/> - </notification> - - <notification - icon="alertmodal.tga" - label="Add Friend" name="AddFriendWithMessage" type="alertmodal"> Friends can give permissions to track each other on the map and receive online status updates. @@ -2094,7 +2080,7 @@ Would you be my friend? icon="alertmodal.tga" name="RemoveFromFriends" type="alertmodal"> -Do you want to remove [FIRST_NAME] [LAST_NAME] from your Friends List? +Do you want to remove [NAME] from your Friends List? <usetemplate name="okcancelbuttons" notext="Cancel" @@ -2347,7 +2333,7 @@ Deed this [AREA] m² of land to the group '[GROUP_NAME]'? name="DeedLandToGroupWithContribution" type="alertmodal"> By deeding this parcel, the group will be required to have and maintain sufficient land use credits. -The deed will include a simultaneous land contribution to the group from '[FIRST_NAME] [LAST_NAME]'. +The deed will include a simultaneous land contribution to the group from '[NAME]'. The purchase price of the land is not refunded to the owner. If a deeded parcel is sold, the sale price will be divided evenly among group members. Deed this [AREA] m² of land to the group '[GROUP_NAME]'? @@ -3167,6 +3153,96 @@ You are no longer frozen. <notification icon="alertmodal.tga" + name="SetDisplayName" + type="alert"> +The name above your avatar's head is called a [http://wiki.secondlife.com/wiki/Setting_your_display_name display name]. You can change it periodically. + +Press reset to make it the same as your username. + +Change your display name? + <form name="form"> + <input name="display_name" type="text"> +[DISPLAY_NAME] + </input> + <button + default="true" + index="0" + name="Change" + text="Change"/> + <button + index="1" + name="Reset" + text="Reset"/> + <button + index="2" + name="Cancel" + text="Cancel"/> + </form> + </notification> + + <notification + icon="alertmodal.tga" + name="SetDisplayNameSuccess" + type="alert"> +Hi [DISPLAY_NAME]! + +Just like in real life, it takes a while for everyone to learn about a new name. Please allow several days for [http://wiki.secondlife.com/wiki/Setting_your_display_name your name to update] in objects, scripts, search, etc. + </notification> + + <notification + icon="alertmodal.tga" + name="SetDisplayNameFailedLockout" + type="alert"> +Sorry, you cannot [http://wiki.secondlife.com/wiki/Setting_your_display_name change your display name] until: +[TIME] + </notification> + + <notification + icon="alertmodal.tga" + name="SetDisplayNameFailedLength" + type="alertmodal"> +Sorry, that name is too long. Display names can have a maximum of [LENGTH] characters. + +Please try a shorter name. + </notification> + + <notification + icon="alertmodal.tga" + name="SetDisplayNameFailedGeneric" + type="alertmodal"> + Sorry, we could not set your display name. Please try again later. + </notification> + +<!-- *NOTE: This should never happen, users should see SetDisplayNameFailedLockout above --> + <notification + icon="alertmodal.tga" + name="AgentDisplayNameUpdateThresholdExceeded" + type="alertmodal"> +Sorry, you have to wait longer before you can change your display name. + +See http://wiki.secondlife.com/wiki/Setting_your_display_name + +Please try again later. + </notification> + + <notification + icon="alertmodal.tga" + name="AgentDisplayNameSetBlocked" + type="alertmodal"> + Sorry, we could not set your requested name because it contains a banned word. + + Please try a different name. + </notification> + + <notification + icon="notifytip.tga" + name="DisplayNameUpdate" + type="notifytip"> + [OLD_NAME] ([SLID]) is now known as [NEW_NAME]. + </notification> + + <notification + icon="alertmodal.tga" name="OfferTeleport" type="alertmodal"> Offer a teleport to your location with the following message? @@ -4469,14 +4545,14 @@ Topic: [SUBJECT], Message: [MESSAGE] icon="notifytip.tga" name="FriendOnline" type="notifytip"> -[FIRST] [LAST] is Online +[NAME] is Online </notification> <notification icon="notifytip.tga" name="FriendOffline" type="notifytip"> -[FIRST] [LAST] is Offline +[NAME] is Offline </notification> <notification @@ -4644,13 +4720,6 @@ You cannot remove protected categories. <notification icon="notifytip.tga" - name="OfferedCard" - type="notifytip"> -You have offered a calling card to [FIRST] [LAST] - </notification> - - <notification - icon="notifytip.tga" name="UnableToBuyWhileDownloading" type="notifytip"> Unable to buy while downloading object data. @@ -4804,7 +4873,15 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" - name="PaymentRecived" + name="PaymentReceived" + persist="true" + type="notify"> +[MESSAGE] + </notification> + + <notification + icon="notify.tga" + name="PaymentSent" persist="true" type="notify"> [MESSAGE] @@ -4902,7 +4979,7 @@ The objects you own on the selected parcel of land have been returned back to yo name="OtherObjectsReturned" persist="true" type="notify"> -The objects on the selected parcel of land that is owned by [FIRST] [LAST] have been returned to his or her inventory. +The objects on the selected parcel of land that is owned by [NAME] have been returned to his or her inventory. </notification> <notification @@ -5577,7 +5654,7 @@ Grant this request? name="ScriptDialog" persist="true" type="notify"> -[FIRST] [LAST]'s '[TITLE]' +[NAME]'s '[TITLE]' [MESSAGE] <form name="form"> <button @@ -5808,7 +5885,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block name="AutoUnmuteByIM" persist="true" type="notify"> -[FIRST] [LAST] was sent an instant message and has been automatically unblocked. +[NAME] was sent an instant message and has been automatically unblocked. </notification> <notification @@ -5816,7 +5893,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block name="AutoUnmuteByMoney" persist="true" type="notify"> -[FIRST] [LAST] was given money and has been automatically unblocked. +[NAME] was given money and has been automatically unblocked. </notification> <notification @@ -5824,7 +5901,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block name="AutoUnmuteByInventory" persist="true" type="notify"> -[FIRST] [LAST] was offered inventory and has been automatically unblocked. +[NAME] was offered inventory and has been automatically unblocked. </notification> <notification diff --git a/indra/newview/skins/default/xui/en/panel_activeim_row.xml b/indra/newview/skins/default/xui/en/panel_activeim_row.xml index 3416b2369d..72f41c62f4 100644 --- a/indra/newview/skins/default/xui/en/panel_activeim_row.xml +++ b/indra/newview/skins/default/xui/en/panel_activeim_row.xml @@ -71,7 +71,7 @@ top="10" left_pad="10" height="14" - width="255" + width="250" length="1" follows="right|left" use_ellipses="true" diff --git a/indra/newview/skins/default/xui/en/panel_edit_profile.xml b/indra/newview/skins/default/xui/en/panel_edit_profile.xml index dff2b9a214..8afdec690b 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_profile.xml @@ -87,12 +87,41 @@ min_height="300" left="0" width="292"> + <text + follows="top|left" + font="SansSerifBigBold" + height="20" + layout="topleft" + left="10" + name="user_name" + text_color="white" + top="4" + value="Hamilton Hitchings" + width="280" /> + <text + follows="top|left" + height="13" + layout="topleft" + left="10" + name="user_slid" + text_color="LtGray" + top_pad="5" + value="(hamilton.linden)" + width="150" /> + <button + follows="top|left" + height="20" + label="Set Name..." + left="170" + name="set_name" + top_delta="-4" + width="110" /> <panel name="lifes_images_panel" follows="left|top|right" height="244" layout="topleft" - top="0" + top="37" left="0" width="292"> <panel @@ -145,8 +174,8 @@ height="102" layout="topleft" left="123" - top="25" - max_length="511" + top="62" + max_length="512" name="sl_description_edit" width="157" word_wrap="true"> @@ -200,8 +229,8 @@ height="102" layout="topleft" left="123" - max_length="254" - top="157" + max_length="512" + top="195" name="fl_description_edit" width="157" word_wrap="true"> @@ -299,7 +328,7 @@ left="10" name="partner_data_panel" width="200"> - <name_box + <text follows="left|top|right" height="30" initial_value="(retrieving)" diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml index e79ae34627..f90db86975 100644 --- a/indra/newview/skins/default/xui/en/panel_group_general.xml +++ b/indra/newview/skins/default/xui/en/panel_group_general.xml @@ -54,17 +54,15 @@ Hover your mouse over the options for more help. top_delta="0"> Founder: </text> - <name_box - follows="left|top" + <text + follows="left|top" height="16" - initial_value="(retrieving)" layout="topleft" left_delta="0" - link="true" name="founder_name" top_pad="2" use_ellipses="true" - width="190" /> + width="185" /> <text font="SansSerifMedium" text_color="EmphasisColor" @@ -113,6 +111,7 @@ Hover your mouse over the options for more help. layout="topleft" left="0" name="visible_members" + short_names="false" top_pad="2"> <name_list.columns label="Member" diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 0eb5c47f85..0255cf80a1 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -81,6 +81,7 @@ clicking on their names. right="-1" multi_select="true" name="member_list" + short_names="false" top_pad="5"> <name_list.columns label="Member" diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml index 33a5e01e4c..a36f078f4f 100644 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml @@ -24,25 +24,15 @@ top_pad="5" width="114"> <layout_panel - mouse_opaque="false" - auto_resize="true" - follows="top|left" - height="0" - layout="topleft" - left="2" - min_height="0" - width="109" - top="0" - name="spacer" - user_resize="false" /> - <layout_panel auto_resize="false" follows="top|left|right" height="20" layout="topleft" + left="2" min_height="20" width="109" name="view_profile_btn_panel" + top="0" user_resize="false"> <button follows="left|top|right" @@ -171,5 +161,15 @@ name="voice_ctrls_btn" width="109" /> </layout_panel> + <layout_panel + mouse_opaque="false" + auto_resize="true" + follows="top|left" + height="0" + layout="topleft" + min_height="0" + width="109" + name="spacer" + user_resize="false" /> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_instant_message.xml b/indra/newview/skins/default/xui/en/panel_instant_message.xml index 34fd3352a3..021cf00d03 100644 --- a/indra/newview/skins/default/xui/en/panel_instant_message.xml +++ b/indra/newview/skins/default/xui/en/panel_instant_message.xml @@ -67,7 +67,7 @@ top="8" use_ellipses="true" value="Erica Vader" - width="212" /> + width="205" /> <!-- TIME STAMP --> <text font="SansSerifSmall" diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index a7e87f2a1e..8d43cc816c 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -3,7 +3,7 @@ name="Landmarks" top="0" height="400" - layout="topleft" + layout="topleft" left="0" width="313" help_topic="panel_landmarks" @@ -88,7 +88,7 @@ </accordion_tab> </accordion> <panel - background_visible="true" + background_visible="true" bevel_style="none" bottom="0" follows="left|right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 185e458a85..48b021efcf 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -65,12 +65,13 @@ Username: <line_editor follows="left|bottom" height="22" -label="Username" +label="bobsmith12 or Steller Sunshine" left_delta="0" -max_length="31" +max_length="63" name="username_edit" +prevalidate_callback="ascii" select_on_focus="true" -tool_tip="[SECOND_LIFE] Username" +tool_tip="The username you chose when you registered, like bobsmith12 or Steller Sunshine" top_pad="0" width="150" /> <text @@ -158,8 +159,8 @@ width="135" <layout_panel follows="right|bottom" name="links" -width="200" -min_width="200" +width="205" +min_width="205" user_resize="false" height="80"> <text @@ -171,7 +172,7 @@ height="16" top="12" right="-10" name="create_new_account_text" - width="180"> + width="200"> Sign up </text> <text @@ -183,8 +184,8 @@ height="16" name="forgot_password_text" top_pad="12" right="-10" - width="180"> - Forgot your name or password? + width="200"> + Forgot your username or password? </text> <text follows="right|bottom" @@ -195,7 +196,7 @@ height="16" name="login_help" top_pad="2" right="-10" - width="180"> + width="200"> Need help logging in? </text> <!-- <text follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_my_profile.xml b/indra/newview/skins/default/xui/en/panel_my_profile.xml index 841a4f5713..86713de81b 100644 --- a/indra/newview/skins/default/xui/en/panel_my_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_my_profile.xml @@ -284,7 +284,7 @@ name="partner_data_panel" top_pad="0" width="300"> - <name_box + <text follows="left|top" height="10" initial_value="(retrieving)" diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index 2ff46637f1..76e5e20498 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -52,7 +52,7 @@ <string name="Filter.Clothes/Body" value="Clothes/Body"/> <string name="Filter.Objects" value="Objects"/> - + <button follows="top|left" height="24" @@ -189,19 +189,19 @@ It is calculated as border_size + 2*UIResizeBarOverlap user_resize="false" visible="true"> - <!-- List containing items from the COF and Base outfit --> - <panel - background_visible="false" - class="cof_wearables" - filename="panel_cof_wearables.xml" + <!-- List containing items from the COF and Base outfit --> + <panel + background_visible="false" + class="cof_wearables" + filename="panel_cof_wearables.xml" follows="all" height="129" - layout="topleft" - left="1" - name="cof_wearables_list" - top="0" - width="311" /> - + layout="topleft" + left="1" + name="cof_wearables_list" + top="0" + width="311" /> + <button follows="left|bottom" height="22" @@ -216,24 +216,24 @@ It is calculated as border_size + 2*UIResizeBarOverlap <combo_box follows="left|right|bottom" height="22" - layout="topleft" + layout="topleft" left_pad="5" name="filter_wearables_combobox" top_delta="0" visible="false" width="152"/> - <button + <button follows="bottom|right" height="22" - image_overlay="Search_Icon" - is_toggle="true" - layout="topleft" - name="filter_button" + image_overlay="Search_Icon" + is_toggle="true" + layout="topleft" + name="filter_button" right="-5" - top_delta="0" + top_delta="0" visible="false" - width="20" /> + width="20" /> </layout_panel> <layout_panel @@ -263,13 +263,13 @@ It is calculated as border_size + 2*UIResizeBarOverlap </layout_panel> </layout_stack> - </layout_panel> + </layout_panel> - <layout_panel + <layout_panel background_visible="true" bg_alpha_color="DkGray2" - auto_resize="true" + auto_resize="true" default_tab_group="3" height="211" min_height="210" @@ -289,46 +289,46 @@ It is calculated as border_size + 2*UIResizeBarOverlap top_pad="-9" width="313" /> - <inventory_panel - allow_multi_select="true" - background_visible="false" - border="false" - follows="left|top|right|bottom" + <inventory_panel + allow_multi_select="true" + background_visible="false" + border="false" + follows="left|top|right|bottom" height="210" - layout="topleft" - left="0" - mouse_opaque="false" - name="inventory_items" - top_pad="5" - width="310" - visible="false"/> - <panel - name="filtered_wearables_panel" - background_opaque="true" - background_visible="false" - layout="topleft" - follows="left|top|right|bottom" - border="false" + layout="topleft" + left="0" + mouse_opaque="false" + name="inventory_items" + top_pad="5" + width="310" + visible="false"/> + <panel + name="filtered_wearables_panel" + background_opaque="true" + background_visible="false" + layout="topleft" + follows="left|top|right|bottom" + border="false" height="210" - left="0" - mouse_opaque="false" - width="311" - top_delta="0" - visible="true"> - <wearable_items_list - color="0.107 0.107 0.107 1" - name="filtered_wearables_list" - allow_select="true" - layout="topleft" - follows="all" - multi_select="true" - width="310" + left="0" + mouse_opaque="false" + width="311" + top_delta="0" + visible="true"> + <wearable_items_list + color="0.107 0.107 0.107 1" + name="filtered_wearables_list" + allow_select="true" + layout="topleft" + follows="all" + multi_select="true" + width="310" height="210" - left="0" - top="0"/> - </panel> + left="0" + top="0"/> + </panel> - </layout_panel> + </layout_panel> </layout_stack> @@ -431,17 +431,17 @@ It is calculated as border_size + 2*UIResizeBarOverlap name="add_to_outfit_btn" top="1" width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" + <icon + follows="bottom|left|right" + height="25" + image_name="Toolbar_Right_Off" + layout="topleft" + left_pad="1" name="dummy_right_icon" - width="184" > - </icon> + width="184" > + </icon> </panel> - + <!-- SAVE AND REVERT BUTTONS --> <panel follows="left|right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index b79ef1e287..f5228f5d35 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -81,7 +81,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M width="317"> <panel background_opaque="true" - background_visible="true" + background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" follows="all" @@ -157,7 +157,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M </panel> <panel background_opaque="true" - background_visible="true" + background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" follows="all" @@ -282,7 +282,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M </panel> <panel background_opaque="true" - background_visible="true" + background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" follows="all" @@ -372,7 +372,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M </panel> <panel background_opaque="true" - background_visible="true" + background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" follows="all" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index 31d8ea27d9..7d9bd1bf2a 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -125,6 +125,7 @@ Automatic position for: left_pad="30" name="first_person_avatar_visible" width="256" /> + <check_box control_name="ArrowKeysAlwaysMove" follows="left|top" @@ -206,7 +207,7 @@ Automatic position for: left="80" name="UI Size:" top_pad="25" - width="160"> + width="300"> UI size </text> <slider @@ -302,6 +303,7 @@ Automatic position for: halign="center" height="23" image_overlay="Refresh_Off" + layout="topleft" tool_tip="Reset to Middle Mouse Button" mouse_opaque="true" name="set_voice_middlemouse_button" @@ -315,7 +317,7 @@ Automatic position for: label="Other Devices" left="30" name="joystick_setup_button" - top="27" + top_pad="27" width="155"> <button.commit_callback function="Floater.Show" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 1c68d59993..ead5ff8ae4 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -222,28 +222,27 @@ </text> <radio_group control_name="AvatarNameTagMode" - height="20" + height="45" layout="topleft" left="50" name="Name_Tag_Preference"> <radio_item label="Off" - layout="topleft" name="radio" value="0" width="75" /> <radio_item label="On" - layout="topleft" - left_pad="12" + left_delta="0" name="radio2" + top_pad="5" value="1" width="75" /> <radio_item label="Show briefly" - layout="topleft" - left_pad="12" + left_delta="0" name="radio3" + top_pad="5" value="2" width="160" /> </radio_group> @@ -253,18 +252,58 @@ height="16" label="Show my name" layout="topleft" - left="50" + left="70" name="show_my_name_checkbox1" + top_pad="4" width="300" /> <check_box + control_name="NameTagShowFriends" enabled_control="AvatarNameTagMode" - control_name="RenderShowGroupTitleAll" height="16" - label="Show group titles" + label="Highlight friends" + left_delta="0" + name="show_friends" + tool_tip="Highlight the name tags of your friends" + top_pad="2" /> + <text + follows="left|top" + height="15" layout="topleft" - left_delta="175" + left="250" + name="name_tags_textbox" + top="175" + width="200"> + Tags show: + </text> + <check_box + control_name="NameTagShowGroupTitles" + enabled_control="AvatarNameTagMode" + height="16" + label="Group titles" + left="265" name="show_all_title_checkbox1" - width="200" /> + tool_tip="Show group titles, like Officer or Member" + top_pad="5" /> + <!-- + <check_box + control_name="NameTagShowDisplayNames" + enabled_control="AvatarNameTagMode" + height="16" + label="Display names" + left_delta="0" + name="show_display_names" + tool_tip="Show display names, like José Sanchez" + top_pad="5" /> + --> + <check_box + control_name="NameTagShowUsernames" + enabled_control="AvatarNameTagMode" + height="16" + label="Usernames" + left_delta="0" + name="show_slids" + tool_tip="Show username, like bobsmith123" + top_pad="2" /> <text type="string" length="1" @@ -273,7 +312,7 @@ layout="topleft" left="30" name="effects_color_textbox" - top_pad="15" + top="290" width="200"> My effects: </text> @@ -285,6 +324,7 @@ layout="topleft" left_pad="5" name="title_afk_text" + top_delta="0" width="190"> Away timeout: </text> @@ -353,8 +393,8 @@ bg_writeable_color="LtGray" use_ellipses="false" commit_on_focus_lost = "true" - follows="left|top|right" - height="60" + follows="left|top" + height="42" layout="topleft" left="50" name="busy_response" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 2c6ceeef2e..7148aba00a 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -115,7 +115,7 @@ layout="topleft" left="77" name="connection_port_enabled" - top_pad="20" + top_pad="15" width="256"> <check_box.commit_callback function="Notification.Show" @@ -147,7 +147,7 @@ left="80" mouse_opaque="false" name="cache_size_label_l" - top_pad="20" + top_pad="10" width="200"> Cache size </text> @@ -375,4 +375,27 @@ name="web_proxy_port" top_delta="0" width="145" /> + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="30" + name="Communications:" + top_pad="5" + width="300"> + Communications: + </text> + <check_box +control_name="UseDisplayNames" +follows="top|left" +height="15" +label="Use Display Names" +layout="topleft" +left_delta="50" +name="display_names_check" +width="237" +tool_tip="Check to use display names in chat, IM, name tags, etc." +top_pad="10"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index d7a601d7a3..6a6b74638d 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -263,7 +263,7 @@ name="partner_data_panel" top_pad="0" width="300"> - <name_box + <text follows="left|top" height="10" initial_value="(retrieving)" diff --git a/indra/newview/skins/default/xui/en/panel_profile_view.xml b/indra/newview/skins/default/xui/en/panel_profile_view.xml index cc5ba334d6..d6d6fa4975 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_view.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_view.xml @@ -50,8 +50,19 @@ height="13" layout="topleft" left="45" + name="user_slid" + text_color="LtGray" + value="" + width="150" /> + <text + follows="top|left" + halign="right" + height="13" + layout="topleft" + left="150" name="status" text_color="LtGray_50" + top_delta="0" value="Online" width="150" /> <tab_container diff --git a/indra/newview/skins/default/xui/en/panel_region_debug.xml b/indra/newview/skins/default/xui/en/panel_region_debug.xml index a6b4ddd01e..7d828b90b2 100644 --- a/indra/newview/skins/default/xui/en/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/en/panel_region_debug.xml @@ -103,7 +103,7 @@ mouse_opaque="false" name="target_avatar_name" top_delta="-2" - width="180"> + width="24 0"> (none) </line_editor> <button diff --git a/indra/newview/skins/default/xui/en/panel_region_estate.xml b/indra/newview/skins/default/xui/en/panel_region_estate.xml index 08e36d5e57..1307d807e2 100644 --- a/indra/newview/skins/default/xui/en/panel_region_estate.xml +++ b/indra/newview/skins/default/xui/en/panel_region_estate.xml @@ -72,7 +72,8 @@ left_delta="0" name="estate_owner" top_delta="16" - width="150"> + use_ellipses="true" + width="290"> (unknown) </text> <view_border diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index c816fd1479..8d19ab15b1 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -158,7 +158,7 @@ left_pad="5" name="LabelCreatorName" top_delta="6" - width="140"> + width="180"> Nicole Linden </text> <button @@ -201,7 +201,7 @@ left_pad="5" name="LabelOwnerName" top_delta="6" - width="140"> + width="180"> Thrax Linden </text> <button diff --git a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml index faa1ae4e8b..e038e104d3 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml @@ -169,7 +169,7 @@ left_pad="0" name="Creator Name" top_delta="0" - width="140"> + width="225"> Erica Linden </text> <text @@ -193,7 +193,7 @@ left_pad="0" name="Owner Name" top_delta="0" - width="140"> + width="225"> Erica Linden </text> <text diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index ed6484302f..5f1d4e6ca6 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -88,6 +88,7 @@ <string name="TooltipHttpUrl">Click to view this web page</string> <string name="TooltipSLURL">Click to view this location's information</string> <string name="TooltipAgentUrl">Click to view this Resident's profile</string> + <string name="TooltipAgentInspect">Learn more about this Resident</string> <string name="TooltipAgentMute">Click to mute this Resident</string> <string name="TooltipAgentUnmute">Click to unmute this Resident</string> <string name="TooltipAgentIM">Click to IM this Resident</string> @@ -3010,7 +3011,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. You are the only user in this session. </string> <string name="offline_message"> - [FIRST] [LAST] is offline. + [NAME] is offline. </string> <string name="invite_message"> Click the [BUTTON NAME] button to accept/connect to this voice chat. @@ -3094,17 +3095,20 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. </string> <!-- Financial operations strings --> - <string name="paid_you_ldollars">[NAME] paid you L$[AMOUNT]</string> + <string name="paid_you_ldollars">[NAME] paid you L$[AMOUNT] [REASON].</string> + <string name="paid_you_ldollars_no_reason">[NAME] paid you L$[AMOUNT].</string> <string name="you_paid_ldollars">You paid [NAME] L$[AMOUNT] [REASON].</string> <string name="you_paid_ldollars_no_info">You paid L$[AMOUNT].</string> <string name="you_paid_ldollars_no_reason">You paid [NAME] L$[AMOUNT].</string> <string name="you_paid_ldollars_no_name">You paid L$[AMOUNT] [REASON].</string> + <string name="for item">for [ITEM]</string> <string name="for a parcel of land">for a parcel of land</string> <string name="for a land access pass">for a land access pass</string> <string name="for deeding land">for deeding land</string> <string name="to create a group">to create a group</string> <string name="to join a group">to join a group</string> <string name="to upload">to upload</string> + <string name="to publish a classified ad">to publish a classified ad</string> <string name="giving">Giving L$ [AMOUNT]</string> <string name="uploading_costs">Uploading costs L$ [AMOUNT]</string> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 47dc31f5f4..58a74721e1 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -1397,7 +1397,7 @@ Esta actualización no es obligatoria, pero te sugerimos instalarla para mejorar </notification> <notification name="BusyModeSet"> Pasar al modo ocupado. -Se ocultará el chat y los mensajes instantáneos (éstos recibirán tu Respuesta en el modo ocupado). Se rehusarán todos los ofrecimientos de teleporte. Todas las ofertas de inventario irán a tu Papelera. +Se ocultará el chat y los mensajes instantáneos (éstos recibirán tu Respuesta en el modo ocupado). Se rehusarán todos los ofrecimientos de teleporte. Todas las ofertas de inventario irán a tu Papelera. <usetemplate ignoretext="Cambio mi estado al modo ocupado" name="okignore" yestext="OK"/> </notification> <notification name="JoinedTooManyGroupsMember"> @@ -1864,7 +1864,7 @@ Linden Lab </form> </notification> <notification name="ConfirmDeleteProtectedCategory"> - La carpeta '[FOLDERNAME]' pertenece al sistema, y borrar carpetas del sistema puede provocar inestabilidad. ¿Estás seguro de que quieres borrarla? + La carpeta '[FOLDERNAME]' pertenece al sistema, y borrar carpetas del sistema puede provocar inestabilidad. ¿Estás seguro de que quieres borrarla? <usetemplate ignoretext="Confirmar antes de borrar una carpeta del sistema" name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> <notification name="ConfirmEmptyTrash"> @@ -2428,7 +2428,7 @@ Esto añadirá un marcador en tu inventario para que puedas enviarle rápidament Si permaneces en esta región serás desconectado. </notification> <notification name="RegionRestartSeconds"> - Esta región se reiniciará en [SECONDS] segundos. + Esta región se reiniciará en [SECONDS] segundos. Si permaneces en esta región serás desconectado. </notification> <notification name="LoadWebPage"> @@ -2495,7 +2495,7 @@ Si no confias en este objeto y en su creador, deberías rehusar esta petición. <notification name="BuyLindenDollarSuccess"> ¡Gracias por tu pago! -Tu saldo de L$ se actualizará cuando se complete el proceso. Si el proceso tarda más de 20 minutos, se cancelará tu transacción, y la cantidad se cargará en tu saldo de US$. +Tu saldo de L$ se actualizará cuando se complete el proceso. Si el proceso tarda más de 20 minutos, se cancelará tu transacción, y la cantidad se cargará en tu saldo de US$. Puedes revisar el estado de tu pago en el Historial de transacciones de tu [http://secondlife.com/account/ Panel de Control] </notification> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index bb1c4242ee..c910b4a058 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -178,7 +178,7 @@ Voulez-vous continuer ? <notification name="JoinGroupNoCost"> Vous vous apprêtez à rejoindre le groupe [NAME]. Voulez-vous continuer ? - <usetemplate name="okcancelbuttons" notext="Annuler" yestext="Rejoindre"/> + <usetemplate name="okcancelbuttons" notext="Annuler" yestext="Fusionner"/> </notification> <notification name="JoinGroupCannotAfford"> Rejoindre ce groupe coûte [COST] L$. diff --git a/indra/newview/skins/default/xui/ja/panel_group_land_money.xml b/indra/newview/skins/default/xui/ja/panel_group_land_money.xml index 4b3a7f880b..016dc97ab6 100644 --- a/indra/newview/skins/default/xui/ja/panel_group_land_money.xml +++ b/indra/newview/skins/default/xui/ja/panel_group_land_money.xml @@ -48,7 +48,7 @@ あなたの貢献: </text> <text name="your_contribution_units"> - m² + 平方メートル </text> <text name="your_contribution_max_value"> (最大 [AMOUNT]) diff --git a/indra/newview/skins/default/xui/ja/panel_group_roles.xml b/indra/newview/skins/default/xui/ja/panel_group_roles.xml index 8a629be910..db6fe268c7 100644 --- a/indra/newview/skins/default/xui/ja/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/ja/panel_group_roles.xml @@ -17,7 +17,7 @@ Ctrl キーを押しながらメンバー名をクリックすると <name_list name="member_list"> <name_list.columns label="メンバー" name="name"/> <name_list.columns label="寄付" name="donated"/> - <name_list.columns label="ログイン" name="online"/> + <name_list.columns label="ステータス" name="online"/> </name_list> <button label="招待" name="member_invite"/> <button label="追放" name="member_eject"/> @@ -44,7 +44,7 @@ Ctrl キーを押しながらメンバー名をクリックすると <filter_editor label="役割を選別" name="filter_input"/> <scroll_list name="role_list"> <scroll_list.columns label="役割" name="name"/> - <scroll_list.columns label="タイトル" name="title"/> + <scroll_list.columns label="肩書き" name="title"/> <scroll_list.columns label="#" name="members"/> </scroll_list> <button label="新しい役割" name="role_create"/> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 51b6581d42..66fbbfc916 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1260,10 +1260,10 @@ Script não encontrado no servidor. </string> <string name="CompileQueueProblemDownloading"> - Problema no download + Problema no download </string> <string name="CompileQueueInsufficientPermDownload"> - Permissões insuficientes para fazer o download do script. + Permissões insuficientes para fazer o download do script. </string> <string name="CompileQueueInsufficientPermFor"> Permissões insuficientes para @@ -1564,7 +1564,7 @@ (vai atualizar depois de publicado) </string> <string name="NoPicksClassifiedsText"> - Você não criou nenhum Destaque ou Anúncio. Clique no botão "+" para criar um Destaque ou Anúncio. + Você não criou nenhum Destaque ou Anúncio. Clique no botão "+" para criar um Destaque ou Anúncio. </string> <string name="NoAvatarPicksClassifiedsText"> O usuário não tem nenhum destaque ou anúncio diff --git a/indra/newview/tests/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp index 7ba82fbd2c..9ec24eb515 100644 --- a/indra/newview/tests/lldateutil_test.cpp +++ b/indra/newview/tests/lldateutil_test.cpp @@ -189,4 +189,14 @@ namespace tut LLDateUtil::ageFromDate("12/13/2009", now), "3 weeks old" ); } + + //template<> template<> + //void dateutil_object_t::test<6>() + //{ + // set_test_name("ISO dates"); + // LLDate now(std::string("2010-01-04T12:00:00Z")); + // ensure_equals("days", + // LLDateUtil::ageFromDateISO("2009-12-13", now), + // "3 weeks old" ); + //} } |