Per-avatar customizable icons next to name links in text

Changed LLUrlEntryAgent callbacks to handle both link label and icon
Eliminated legacy LLNameCache file loading
Reviewed with Kelly
master
James Cook 2010-02-12 16:12:12 -08:00
parent 3581d0001e
commit c16591c046
17 changed files with 231 additions and 206 deletions

View File

@ -35,6 +35,13 @@
#include "llavatarname.h"
LLAvatarName::LLAvatarName()
: mSLID(),
mDisplayName(),
mLastUpdate(0),
mBadge()
{ }
bool LLAvatarName::operator<(const LLAvatarName& rhs) const
{
if (mSLID == rhs.mSLID)

View File

@ -39,6 +39,8 @@
class LL_COMMON_API LLAvatarName
{
public:
LLAvatarName();
bool operator<(const LLAvatarName& rhs) const;
// "bobsmith123" or "james.linden", US-ASCII only
@ -48,6 +50,15 @@ public:
// Contains data whether or not user has explicitly set
// a display name; may duplicate their SLID.
std::string mDisplayName;
// Names can change, so need to keep track of when name was
// last checked.
// Unix time-from-epoch seconds
U32 mLastUpdate;
// Can be a viewer UI image name ("Person_Check") or a server-side
// image UUID, or empty string.
std::string mBadge;
};
#endif

View File

@ -38,6 +38,11 @@
#include <cctype> // tolower()
namespace LLAvatarNameCache
{
std::map<LLUUID, LLAvatarName> sCache;
}
static std::string slid_from_full_name(const std::string& full_name)
{
std::string id = full_name;
@ -52,64 +57,67 @@ static std::string slid_from_full_name(const std::string& full_name)
return id;
}
static std::map<LLUUID, std::string> sDisplayNames;
// JAMESDEBUG HACK temporary IDEVO code
static std::string get_display_name(const LLUUID& id)
{
if (sDisplayNames.empty())
{
LLUUID id;
const unsigned char miyazaki_hayao_san[]
= { 0xE5, 0xAE, 0xAE, 0xE5, 0xB4, 0x8E,
0xE9, 0xA7, 0xBF,
0xE3, 0x81, 0x95, 0xE3, 0x82, 0x93, '\0' };
id.set("27888d5f-4ddb-4df3-ad36-a1483ce0b3d9"); // miyazaki23
sDisplayNames[id] = (const char*)miyazaki_hayao_san;
id.set("3e5bf676-3577-c9ee-9fac-10df430015a1"); // Jim Linden
sDisplayNames[id] = "Jim Jenkins";
const unsigned char jose_sanchez[] =
{ 'J','o','s',0xC3,0xA9,' ','S','a','n','c','h','e','z', '\0' };
id.set("a2e76fcd-9360-4f6d-a924-938f923df11a"); // James Linden
sDisplayNames[id] = (const char*)jose_sanchez;
id.set("a23fff6c-80ae-4997-9253-48272fd01d3c"); // bobsmith123
sDisplayNames[id] = (const char*)jose_sanchez;
id.set("3f7ced39-5e38-4fdd-90f2-423560b1e6e2"); // Hamilton Linden
sDisplayNames[id] = "Hamilton Hitchings";
id.set("537da1e1-a89f-4f9b-9056-b1f0757ccdd0"); // Rome Linden
sDisplayNames[id] = "Rome Portlock";
id.set("244195d6-c9b7-4fd6-9229-c3a8b2e60e81"); // M Linden
sDisplayNames[id] = "Mark Kingdon";
id.set("49856302-98d4-4e32-b5e9-035e5b4e83a4"); // T Linden
sDisplayNames[id] = "Tom Hale";
id.set("e6ed7825-708f-4c6b-b6a7-f3fe921a9176"); // Callen Linden
sDisplayNames[id] = "Christina Allen";
id.set("a7f0ac18-205f-41d2-b5b4-f75f096ae511"); // Crimp Linden
sDisplayNames[id] = "Chris Rimple";
}
std::map<LLUUID,std::string>::iterator it = sDisplayNames.find(id);
if (it != sDisplayNames.end())
{
return it->second;
}
else
{
return std::string();
}
}
void LLAvatarNameCache::initClass()
{
// HACK - prepopulate the cache
LLAvatarName name;
LLUUID id;
name.mSLID = "miyazaki23";
const unsigned char miyazaki_hayao_san[]
= { 0xE5, 0xAE, 0xAE, 0xE5, 0xB4, 0x8E,
0xE9, 0xA7, 0xBF,
0xE3, 0x81, 0x95, 0xE3, 0x82, 0x93, '\0' };
name.mDisplayName = (const char*)miyazaki_hayao_san;
name.mBadge = "Person_Check";
sCache[LLUUID("27888d5f-4ddb-4df3-ad36-a1483ce0b3d9")] = name;
name.mSLID = "jim.linden";
name.mDisplayName = "Jim Jenkins";
name.mBadge = "Person_Star";
sCache[LLUUID("3e5bf676-3577-c9ee-9fac-10df430015a1")] = name;
name.mSLID = "james.linden";
const unsigned char jose_sanchez[] =
{ 'J','o','s',0xC3,0xA9,' ','S','a','n','c','h','e','z', '\0' };
name.mDisplayName = (const char*)jose_sanchez;
name.mBadge = "35f217a3-f618-49cf-bbca-c86d486551a9"; // IMG_SHOT
sCache[LLUUID("a2e76fcd-9360-4f6d-a924-938f923df11a")] = name;
name.mSLID = "bobsmith123";
name.mDisplayName = (const char*)jose_sanchez;
name.mBadge = "";
sCache[LLUUID("a23fff6c-80ae-4997-9253-48272fd01d3c")] = name;
name.mSLID = "hamilton.linden";
name.mDisplayName = "Hamilton Hitchings";
name.mBadge = "Person_Star";
sCache[LLUUID("3f7ced39-5e38-4fdd-90f2-423560b1e6e2")] = name;
name.mSLID = "rome.linden";
name.mDisplayName = "Rome Portlock";
name.mBadge = "Person_Star";
sCache[LLUUID("537da1e1-a89f-4f9b-9056-b1f0757ccdd0")] = name;
name.mSLID = "m.linden";
name.mDisplayName = "Mark Kingdon";
name.mBadge = "Person_Star";
sCache[LLUUID("244195d6-c9b7-4fd6-9229-c3a8b2e60e81")] = name;
name.mSLID = "t.linden";
name.mDisplayName = "Tom Hale";
name.mBadge = "Person_Star";
sCache[LLUUID("49856302-98d4-4e32-b5e9-035e5b4e83a4")] = name;
name.mSLID = "callen.linden";
name.mDisplayName = "Christina Allen";
name.mBadge = "Person_Star";
sCache[LLUUID("e6ed7825-708f-4c6b-b6a7-f3fe921a9176")] = name;
name.mSLID = "crimp.linden";
name.mDisplayName = "Chris Rimple";
name.mBadge = "Person_Star";
sCache[LLUUID("a7f0ac18-205f-41d2-b5b4-f75f096ae511")] = name;
}
void LLAvatarNameCache::cleanupClass()
@ -130,24 +138,23 @@ void LLAvatarNameCache::idle()
bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
{
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
*av_name = it->second;
return true;
}
std::string full_name;
bool found = gCacheName->getFullName(agent_id, full_name);
if (found)
if (gCacheName->getFullName(agent_id, full_name))
{
av_name->mSLID = slid_from_full_name(full_name);
std::string display_name = get_display_name(agent_id);
if (!display_name.empty())
{
av_name->mDisplayName = display_name;
}
else
{
// ...no explicit display name, use legacy name
av_name->mDisplayName = full_name;
}
av_name->mDisplayName = full_name;
av_name->mBadge = "Generic_Person";
return true;
}
return found;
return false;
}
void LLAvatarNameCache::get(const LLUUID& agent_id, name_cache_callback_t callback)

View File

@ -308,89 +308,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
@ -512,7 +433,7 @@ BOOL LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::strin
}
// 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;

View File

@ -70,9 +70,6 @@ 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);
@ -116,7 +113,7 @@ public:
void dumpStats(); // Dumps the sizes of the cache and associated queues.
static std::string getDefaultName();
static void LocalizeCacheName(std::string key, std::string value);
static void localizeCacheName(std::string key, std::string value);
static std::map<std::string, std::string> sCacheName;
private:

View File

@ -1527,6 +1527,20 @@ std::string LLTextBase::getText() const
return getViewModel()->getValue().asString();
}
// 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::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
@ -1539,7 +1553,7 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
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;
@ -1570,15 +1584,18 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
// 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).
appendAndHighlightText(" ", prepend_newline, part, icon);
appendAndHighlightText(" ", prepend_newline, part, icon_params);
prepend_newline = false;
}
}
@ -1728,8 +1745,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();
@ -1759,6 +1777,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();
}

View File

@ -309,7 +309,10 @@ protected:
// misc
void updateRects();
void needsScroll() { mScrollNeeded = TRUE; }
void replaceUrlLabel(const std::string &url, const std::string &label);
// 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);
protected:
// text segmentation and flow

View File

@ -55,6 +55,12 @@ std::string LLUrlEntryBase::getUrl(const std::string &string) const
return escapeUrl(string);
}
//virtual
std::string LLUrlEntryBase::getIcon(const std::string &url) const
{
return mIcon;
}
std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
{
// return the id from a SLURL in the format /app/{cmd}/{id}/about
@ -133,7 +139,9 @@ void LLUrlEntryBase::addObserver(const std::string &id,
}
}
void LLUrlEntryBase::callObservers(const std::string &id, const std::string &label)
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;
@ -141,7 +149,7 @@ void LLUrlEntryBase::callObservers(const std::string &id, const std::string &lab
{
// 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++);
@ -312,22 +320,18 @@ LLUrlEntryAgent::LLUrlEntryAgent()
mColor = LLUIColorTable::instance().getColor("AgentLinkColor");
}
// IDEVO demo code
std::string LLUrlEntryAgent::buildName(const LLUUID& id)
{
// JAMESDEBUG HACK: assume name is there
LLAvatarName av_name;
LLAvatarNameCache::get(id, &av_name);
return av_name.mDisplayName + " (" + av_name.mSLID + ")";
}
void LLUrlEntryAgent::onNameCache(const LLUUID& id,
const std::string& full_name,
bool is_group)
{
std::string final = buildName(id);
// IDEVO demo code
LLAvatarName av_name;
LLAvatarNameCache::get(id, &av_name);
std::string label = av_name.mDisplayName + " (" + av_name.mSLID + ")";
// use custom icon if available
std::string icon = (!av_name.mBadge.empty() ? av_name.mBadge : mIcon);
// received the agent name from the server - tell our observers
callObservers(id.asString(), final);
callObservers(id.asString(), label, icon);
}
std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
@ -353,7 +357,10 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
else if (gCacheName->getFullName(agent_id, full_name))
{
return buildName(agent_id);
LLAvatarName av_name;
LLAvatarNameCache::get(agent_id, &av_name);
std::string label = av_name.mDisplayName + " (" + av_name.mSLID + ")";
return label;
}
else
{
@ -366,6 +373,30 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
std::string LLUrlEntryAgent::getIcon(const std::string &url) const
{
std::string agent_id_string = getIDStringFromUrl(url);
if (agent_id_string.empty())
{
return mIcon;
}
LLUUID agent_id(agent_id_string);
if (agent_id.isNull())
{
return mIcon;
}
LLAvatarName av_name;
LLAvatarNameCache::get(agent_id, &av_name);
if (av_name.mBadge.empty())
{
return mIcon;
}
return av_name.mBadge;
}
//
// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
@ -386,7 +417,7 @@ void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
bool is_group)
{
// received the group name from the server - tell our observers
callObservers(id.asString(), name);
callObservers(id.asString(), name, mIcon);
}
std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)

View File

@ -42,7 +42,8 @@
#include <map>
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,7 +78,7 @@ 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
std::string getIcon() const { return mIcon; }
virtual std::string getIcon(const std::string &url) const;
/// Return the color to render the displayed text
LLUIColor getColor() const { return mColor; }
@ -101,7 +102,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);
void callObservers(const std::string &id, const std::string &label, const std::string& icon);
typedef struct {
std::string url;
@ -168,10 +169,10 @@ 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) const;
private:
void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group);
std::string buildName(const LLUUID& id);
};
///

View File

@ -37,7 +37,7 @@
#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)
{
}
@ -180,7 +180,7 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
match_entry->getUrl(url),
match_entry->getLabel(url, cb),
match_entry->getTooltip(),
match_entry->getIcon(),
match_entry->getIcon(url),
match_entry->getColor(),
match_entry->getMenuName(),
match_entry->getLocation(url),

View File

@ -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

View File

@ -1298,8 +1298,7 @@ bool LLAppViewer::cleanup()
LLPolyMesh::freeAllMeshes();
delete gCacheName;
gCacheName = NULL;
LLStartUp::cleanupNameCache();
// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
@ -3337,15 +3336,6 @@ 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)
{
gCacheName->importFile(name_cache_fp);
fclose(name_cache_fp);
}
}
void LLAppViewer::saveNameCache()

View File

@ -52,6 +52,7 @@
#endif
#include "llares.h"
#include "llavatarnamecache.h"
#include "lllandmark.h"
#include "llcachename.h"
#include "lldir.h"
@ -1280,16 +1281,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
@ -2781,6 +2773,30 @@ 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();
LLAvatarNameCache::initClass();
}
void LLStartUp::cleanupNameCache()
{
LLAvatarNameCache::cleanupClass();
delete gCacheName;
gCacheName = NULL;
}
bool LLStartUp::dispatchURL()
{
// ok, if we've gotten this far and have a startup URL

View File

@ -95,6 +95,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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -410,6 +410,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" />