summaryrefslogtreecommitdiff
path: root/indra/llui/llurlentry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llurlentry.cpp')
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlentry.cpp835
1 files changed, 749 insertions, 86 deletions
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index e8e3459673..5f60d80858 100644..100755
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -3,47 +3,49 @@
* @author Martin Reddy
* @brief Describes the Url types that can be registered in LLUrlRegistry
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llurlentry.h"
+#include "lluictrl.h"
#include "lluri.h"
#include "llurlmatch.h"
#include "llurlregistry.h"
+#include "lluriparser.h"
+#include "llavatarnamecache.h"
#include "llcachename.h"
#include "lltrans.h"
#include "lluicolortable.h"
+#include "message.h"
+
+#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
-LLUrlEntryBase::LLUrlEntryBase() :
- mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")),
- mDisabledLink(false)
+// Utility functions
+std::string localize_slapp_label(const std::string& url, const std::string& full_name);
+
+
+LLUrlEntryBase::LLUrlEntryBase()
{
}
@@ -56,6 +58,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
@@ -133,22 +151,65 @@ 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++);
}
}
+/// is this a match for a URL that should not be hyperlinked?
+bool LLUrlEntryBase::isLinkDisabled() const
+{
+ // this allows us to have a global setting to turn off text hyperlink highlighting/action
+ bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions");
+
+ return globally_disabled;
+}
+
+bool LLUrlEntryBase::isWikiLinkCorrect(std::string url)
+{
+ std::string label = getLabelFromWikiLink(url);
+ return (LLUrlRegistry::instance().hasUrl(label)) ? false : true;
+}
+
+std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const
+{
+ LLUriParser up(unescapeUrl(url));
+ up.normalize();
+
+ std::string label;
+ up.extractParts();
+ up.glueFirst(label);
+
+ return label;
+}
+
+std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const
+{
+ LLUriParser up(unescapeUrl(url));
+
+ std::string query;
+ up.extractParts();
+ up.glueSecond(query);
+
+ return query;
+}
+
+
static std::string getStringAfterToken(const std::string str, const std::string token)
{
size_t pos = str.find(token);
@@ -165,6 +226,7 @@ static std::string getStringAfterToken(const std::string str, const std::string
// LLUrlEntryHTTP Describes generic http: and https: Urls
//
LLUrlEntryHTTP::LLUrlEntryHTTP()
+ : LLUrlEntryBase()
{
mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",
boost::regex::perl|boost::regex::icase);
@@ -174,6 +236,25 @@ LLUrlEntryHTTP::LLUrlEntryHTTP()
std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTP::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTP::getUrl(const std::string &string) const
+{
+ if (string.find("://") == std::string::npos)
+ {
+ return "http://" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const
+{
return unescapeUrl(url);
}
@@ -191,7 +272,13 @@ LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return getLabelFromWikiLink(url);
+ std::string label = getLabelFromWikiLink(url);
+ return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
+}
+
+std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const
+{
+ return getUrl(string);
}
std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
@@ -203,6 +290,7 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
//
LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
+ : LLUrlEntryBase()
{
mPattern = boost::regex("("
"\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR
@@ -216,7 +304,12 @@ LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return unescapeUrl(url);
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
}
std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
@@ -228,6 +321,95 @@ std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
return escapeUrl(string);
}
+std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const
+{
+ return unescapeUrl(url);
+}
+
+LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
+ : LLUrlEntryBase()
+{
+ mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+
+ return escapeUrl(url);
+}
+
+std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const
+{
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const
+{
+ return unescapeUrl(url);
+}
+
+bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
+{
+ S32 actual_parts;
+
+ if(url.find(".com/secondlife/") != std::string::npos)
+ {
+ actual_parts = 5;
+ }
+ else if(url.find("/app/") != std::string::npos)
+ {
+ actual_parts = 6;
+ }
+ else
+ {
+ actual_parts = 3;
+ }
+
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ S32 x,y,z;
+
+ if (path_parts == actual_parts)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ LLStringUtil::convertToS32(path_array[path_parts-3],x);
+ LLStringUtil::convertToS32(path_array[path_parts-2],y);
+ LLStringUtil::convertToS32(path_array[path_parts-1],z);
+
+ if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0))
+ {
+ return TRUE;
+ }
+ }
+ else if (path_parts == (actual_parts-1))
+ {
+ // handle slurl with (X,Y) coordinates
+
+ LLStringUtil::convertToS32(path_array[path_parts-2],x);
+ LLStringUtil::convertToS32(path_array[path_parts-1],y);
+ ;
+ if((x>= 0 && x<= 256) && (y>= 0 && y<= 256))
+ {
+ return TRUE;
+ }
+ }
+ else if (path_parts == (actual_parts-2))
+ {
+ // handle slurl with (X) coordinate
+ LLStringUtil::convertToS32(path_array[path_parts-1],x);
+ if(x>= 0 && x<= 256)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
//
// LLUrlEntrySLURL Describes generic http: and https: Urls
//
@@ -249,6 +431,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa
// - http://slurl.com/secondlife/Place/X
// - http://slurl.com/secondlife/Place
//
+
LLURI uri(url);
LLSD path_array = uri.pathArray();
S32 path_parts = path_array.size();
@@ -301,25 +484,103 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
}
//
+// LLUrlEntrySeconlifeURL Describes *secondlife.com/ and *lindenlab.com/ urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL()
+{
+ mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*",
+ boost::regex::perl|boost::regex::icase);
+
+ mIcon = "Hand";
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+/// Return the url from a string that matched the regex
+std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const
+{
+ if (string.find("://") == std::string::npos)
+ {
+ return "https://" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
+}
+
+std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const
+{
+ return url;
+}
+
+//
+// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com and *lindenlab.com urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL()
+ {
+ mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)",
+ boost::regex::perl|boost::regex::icase);
+
+ mIcon = "Hand";
+ mMenuName = "menu_url_http.xml";
+}
+
+//
// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
//
-LLUrlEntryAgent::LLUrlEntryAgent()
+LLUrlEntryAgent::LLUrlEntryAgent() :
+ mAvatarNameCacheConnection()
{
- mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+",
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
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)
{
+ mAvatarNameCacheConnection.disconnect();
+
+ 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);
+}
+
+LLUUID LLUrlEntryAgent::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
}
std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
@@ -327,6 +588,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");
@@ -354,6 +619,12 @@ std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
return LLTrans::getString("TooltipAgentUrl");
}
+bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect");
+}
+
std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
if (!gCacheName)
@@ -370,74 +641,224 @@ 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))
+
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(agent_id, &av_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"))
+ std::string label = av_name.getCompleteName();
+
+ // handle suffixes like /mute or /offerteleport
+ label = localize_slapp_label(url, label);
+ return label;
+ }
+ else
+ {
+ if (mAvatarNameCacheConnection.connected())
{
- return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
+ mAvatarNameCacheConnection.disconnect();
}
- return full_name;
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
+ addObserver(agent_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+LLStyle::Params LLUrlEntryAgent::getStyle() const
+{
+ LLStyle::Params style_params = LLUrlEntryBase::getStyle();
+ style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ return style_params;
+}
+
+std::string localize_slapp_label(const std::string& url, const std::string& full_name)
+{
+ // customize label string based on agent SLapp suffix
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("SLappAgentMute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("SLappAgentUnmute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("SLappAgentIM") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("SLappAgentPay") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/removefriend"))
+ {
+ return LLTrans::getString("SLappAgentRemoveFriend") + " " + 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() :
+ mAvatarNameCacheConnection()
+{}
+
+void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
+ const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+
+ 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
{
- gCacheName->get(agent_id, FALSE,
- boost::bind(&LLUrlEntryAgent::onAgentNameReceived,
- this, _1, _2, _3, _4));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::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.getDisplayName();
+}
+
+//
+// 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.getAccountName();
+}
+
//
// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
//
LLUrlEntryGroup::LLUrlEntryGroup()
{
- mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/\\w+",
+ mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_group.xml";
mIcon = "Generic_Group";
mTooltip = LLTrans::getString("TooltipGroupUrl");
- 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);
+}
+
+LLUUID LLUrlEntryGroup::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
}
+
std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
if (!gCacheName)
@@ -465,14 +886,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("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ return style_params;
+}
+
+
//
// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
@@ -482,7 +912,8 @@ LLUrlEntryInventory::LLUrlEntryInventory()
//*TODO: add supporting of inventory item names with whitespaces
//this pattern cann't parse for example
//secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
- mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+\\S*",
+ //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+ mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_inventory.xml";
}
@@ -493,29 +924,141 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab
return LLURI::unescape(label.empty() ? url : label);
}
+//
+// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+//
+LLUrlEntryObjectIM::LLUrlEntryObjectIM()
+{
+ mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_objectim.xml";
+}
+
+std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("name"))
+ return query_map["name"];
+ return unescapeUrl(url);
+}
+
+std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("slurl"))
+ return query_map["slurl"];
+ return LLUrlEntryBase::getLocation(url);
+}
+
+// LLUrlEntryParcel statics.
+LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null);
+LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
+LLHost LLUrlEntryParcel::sRegionHost(LLHost::invalid);
+bool LLUrlEntryParcel::sDisconnected(false);
+std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
+
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
///
LLUrlEntryParcel::LLUrlEntryParcel()
{
- mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
+ mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_parcel.xml";
mTooltip = LLTrans::getString("TooltipParcelUrl");
+
+ sParcelInfoObservers.insert(this);
+}
+
+LLUrlEntryParcel::~LLUrlEntryParcel()
+{
+ sParcelInfoObservers.erase(this);
}
std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
+ LLSD path_array = LLURI(url).pathArray();
+ S32 path_parts = path_array.size();
+
+ if (path_parts < 3) // no parcel id
+ {
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
+ return url;
+ }
+
+ std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
+
+ // Add an observer to call LLUrlLabelCallback when we have parcel name.
+ addObserver(parcel_id_string, url, cb);
+
+ LLUUID parcel_id(parcel_id_string);
+
+ sendParcelInfoRequest(parcel_id);
+
return unescapeUrl(url);
}
+void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id)
+{
+ if (sRegionHost == LLHost::invalid || sDisconnected) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("ParcelInfoRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, sAgentID );
+ msg->addUUID("SessionID", sSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("ParcelID", parcel_id);
+ msg->sendReliable(sRegionHost);
+}
+
+void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label)
+{
+ callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon);
+}
+
+// static
+void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
+{
+ std::string label(LLStringUtil::null);
+ if (!parcel_data.name.empty())
+ {
+ label = parcel_data.name;
+ }
+ // If parcel name is empty use Sim_name (x, y, z) for parcel label.
+ else if (!parcel_data.sim_name.empty())
+ {
+ S32 region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS;
+ S32 region_z = ll_round(parcel_data.global_z);
+
+ label = llformat("%s (%d, %d, %d)",
+ parcel_data.sim_name.c_str(), region_x, region_y, region_z);
+ }
+
+ for (std::set<LLUrlEntryParcel*>::iterator iter = sParcelInfoObservers.begin();
+ iter != sParcelInfoObservers.end();
+ ++iter)
+ {
+ LLUrlEntryParcel* url_entry = *iter;
+ if (url_entry)
+ {
+ url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
+ }
+ }
+}
+
//
// LLUrlEntryPlace Describes secondlife://<location> URLs
//
LLUrlEntryPlace::LLUrlEntryPlace()
{
- mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
+ mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_slurl.xml";
mTooltip = LLTrans::getString("TooltipSLURL");
@@ -558,12 +1101,76 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const
}
//
+// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
+// secondlife:///app/region/Ahern/128/128/0
+//
+LLUrlEntryRegion::LLUrlEntryRegion()
+{
+ mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife:///app/region/Place/X/Y/Z
+ // - secondlife:///app/region/Place/X/Y
+ // - secondlife:///app/region/Place/X
+ // - secondlife:///app/region/Place
+ //
+
+ LLSD path_array = LLURI(url).pathArray();
+ S32 path_parts = path_array.size();
+
+ if (path_parts < 3) // no region name
+ {
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
+ return url;
+ }
+
+ std::string label = unescapeUrl(path_array[2]); // region name
+
+ if (path_parts > 3) // secondlife:///app/region/Place/X
+ {
+ std::string x = path_array[3];
+ label += " (" + x;
+
+ if (path_parts > 4) // secondlife:///app/region/Place/X/Y
+ {
+ std::string y = path_array[4];
+ label += "," + y;
+
+ if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
+ {
+ std::string z = path_array[5];
+ label = label + "," + z;
+ }
+ }
+
+ label += ")";
+ }
+
+ return label;
+}
+
+std::string LLUrlEntryRegion::getLocation(const std::string &url) const
+{
+ LLSD path_array = LLURI(url).pathArray();
+ std::string region_name = unescapeUrl(path_array[2]);
+ return region_name;
+}
+
+//
// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
// secondlife:///app/teleport/Ahern/50/50/50/
+// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
//
LLUrlEntryTeleport::LLUrlEntryTeleport()
{
- mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
+ mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_teleport.xml";
mTooltip = LLTrans::getString("TooltipTeleportUrl");
@@ -581,7 +1188,12 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe
LLURI uri(url);
LLSD path_array = uri.pathArray();
S32 path_parts = path_array.size();
- const std::string label = LLTrans::getString("SLurlLabelTeleport");
+ std::string host = uri.hostName();
+ std::string label = LLTrans::getString("SLurlLabelTeleport");
+ if (!host.empty())
+ {
+ label += " " + host;
+ }
if (path_parts == 6)
{
// handle teleport url with (X,Y,Z) coordinates
@@ -653,7 +1265,8 @@ LLUrlEntrySLLabel::LLUrlEntrySLLabel()
std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return getLabelFromWikiLink(url);
+ std::string label = getLabelFromWikiLink(url);
+ return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
}
std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
@@ -675,12 +1288,25 @@ std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
return LLUrlEntryBase::getTooltip(string);
}
+bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.underlineOnHoverOnly();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::underlineOnHoverOnly(string);
+}
+
//
// LLUrlEntryWorldMap Describes secondlife:///<location> URLs
//
LLUrlEntryWorldMap::LLUrlEntryWorldMap()
{
- mPattern = boost::regex("secondlife:///app/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+ mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_map.xml";
mTooltip = LLTrans::getString("TooltipMapUrl");
@@ -703,7 +1329,7 @@ std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabe
}
const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
- std::string location = path_array[2];
+ std::string location = unescapeUrl(path_array[2]);
std::string x = (path_parts > 3) ? path_array[3] : "128";
std::string y = (path_parts > 4) ? path_array[4] : "128";
std::string z = (path_parts > 5) ? path_array[5] : "0";
@@ -721,9 +1347,8 @@ std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
//
LLUrlEntryNoLink::LLUrlEntryNoLink()
{
- mPattern = boost::regex("<nolink>[^<]*</nolink>",
+ mPattern = boost::regex("<nolink>.*?</nolink>",
boost::regex::perl|boost::regex::icase);
- mDisabledLink = true;
}
std::string LLUrlEntryNoLink::getUrl(const std::string &url) const
@@ -736,3 +1361,41 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC
{
return getUrl(url);
}
+
+LLStyle::Params LLUrlEntryNoLink::getStyle() const
+{
+ // Don't render as URL (i.e. no context menu or hand cursor).
+ return LLStyle::Params().is_link(false);
+}
+
+
+//
+// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
+//
+LLUrlEntryIcon::LLUrlEntryIcon()
+{
+ mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryIcon::getUrl(const std::string &url) const
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getIcon(const std::string &url)
+{
+ // Grep icon info between <icon>...</icon> tags
+ // matches[1] contains the icon name/path
+ boost::match_results<std::string::const_iterator> matches;
+ mIcon = (boost::regex_match(url, matches, mPattern) && matches[1].matched)
+ ? matches[1]
+ : LLStringUtil::null;
+ LLStringUtil::trim(mIcon);
+ return mIcon;
+}