Contact list categories and searching

This commit is contained in:
CharleyLevenque
2010-09-06 16:17:53 -04:00
parent 51665a0b31
commit 892573dfb4
10 changed files with 465 additions and 9 deletions

View File

@@ -35,8 +35,8 @@
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 4;
const S32 LL_VERSION_PATCH = 1;
const S32 LL_VERSION_BUILD = 0;
const S32 LL_VERSION_PATCH = 2;
const S32 LL_VERSION_BUILD = 1;
const char * const LL_CHANNEL = "Ascent Viewer Release";

View File

@@ -64,6 +64,7 @@ include_directories(
)
set(viewer_SOURCE_FILES
ascentfloatercontactgroups.cpp
ascentprefssys.cpp
ascentprefsvan.cpp
ascentuploadbrowser.cpp
@@ -514,6 +515,7 @@ endif (LINUX)
set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
ascentfloatercontactgroups.h
ascentprefssys.h
ascentprefsvan.h
ascentuploadbrowser.h

View File

@@ -0,0 +1,206 @@
/**
* @file ascentfloatercontactgroups.cpp
* @Author Charley Levenque
* Allows the user to assign friends to contact groups for advanced sorting.
*
* Created Sept 6th 2010
*
* ALL SOURCE CODE IS PROVIDED "AS IS." THE CREATOR MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
*
* HACKERS CAN TURN YOUR COMPUTER INTO A BOMB.
* & BLOW YOUR FAMILY TO SMITHEREENS!
*
* \ . ./
* \ .:";'.:.." /
* (M^^.^~~:.'").
* - (/ . . . \ \) -
* ((| :. ~ ^ :. .|))
* - (\- | \ / | /) -
* T -\ \ / /-
* [_]..........................\ \ / /
*/
#include "llviewerprecompiledheaders.h"
#include "ascentfloatercontactgroups.h"
//UI Elements
#include "llbutton.h" //Buttons
#include "llcombobox.h" //Combo dropdowns
#include "llscrolllistctrl.h" //List box for filenames
#include "lluictrlfactory.h" //Loads the XUI
// project includes
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llsdserialize.h"
#include "llchat.h"
#include "llfloaterchat.h"
ASFloaterContactGroups* ASFloaterContactGroups::sInstance = NULL;
ASFloaterContactGroups::ASFloaterContactGroups()
: LLFloater(std::string("floater_contact_groups"), std::string("FloaterContactRect"), LLStringUtil::null)
{
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_contact_groups.xml");
}
// static
void ASFloaterContactGroups::show(void*)
{
if (!sInstance)
sInstance = new ASFloaterContactGroups();
sInstance->open();
sInstance->populateGroupList();
}
ASFloaterContactGroups::~ASFloaterContactGroups()
{
sInstance=NULL;
}
void ASFloaterContactGroups::populateGroupList()
{
LLScrollListCtrl* scroller = getChild<LLScrollListCtrl>("group_scroll_list");
if(scroller != NULL)
{
std::string name;
gDirUtilp->getNextFileInDir(gDirUtilp->getPerAccountChatLogsDir(),"*",name,false);//stupid hack to clear last file search
scroller->clear();
std::string path_name(gDirUtilp->getPerAccountChatLogsDir());
bool found = true;
LLChat msg("Searching: " + path_name);
LLFloaterChat::addChat(msg);
while(found = gDirUtilp->getNextFileInDir(path_name, "*", name, false))
{
if ((name == ".") || (name == "..")) continue;
//llinfos << "path name " << path_name << " and name " << name << " and found " << found << llendl;
if(found)
{
LLChat msg("Found: " + name);
LLFloaterChat::addChat(msg);
scroller->addSimpleElement(name, ADD_BOTTOM);
}
else
{
LLChat msg("Skipped: " + name);
LLFloaterChat::addChat(msg);
}
}
}
else
{
LLChat msg("Null Scroller");
LLFloaterChat::addChat(msg);
}
}
/*
* BATGIRL
*
*
* .//////..... I: .
* .////....///////........IA...... //
* XYt///////./.....////......$$$$$$$$$$$./I$
* .Y/XX//?//./....////////......$$$$$$$$$$$$$$/
* XYXXXXXXX///......//////.....$$$$$$$$$$$$$$$.
* XXXXXXXX////XX/....//.///./..$$$$$$$$$$$$$$$....
* ///////......////X...//.//./$$$$$$$$$$$$$$$...../..
* ////..////////..../X/X//////$$$-.$$$$$$$$$$./...///..
* ///////////////....//X/XX///$$ .'$$.$-..$$$.///..//?....
* ////////////X/////////XX/XX/$I .;;. .$$.//xX/XXXXX///////. .$$$$$$/
* /XX/XXX//////XXXXX////XXX/X/$$ .::- ..$//XXX/////////////$$$$$$$/
* ////XXX//XXXXXX//////XXXXX//$$ --- $/////X///XXXXXXXA$$$$$$../...
* /////XX/XX$$$$$X$$$$$$$////$$$$. .$$$$$X//$$$$$$X/X/$$$$(////..///.
* ///XXXXX$$$$$$$$$$$$$X$$///$$$$$$$$$$$$$$$XX$$$$$X//////$$$$$$////////
* ?$$$$$$$$$$$$$:''':.::'V$$$$$'$$$$$$XXX$$$$X''''$$XXXX/$A$$$I,X//XX/////
* $$$$$$$$$.... .. .:.::::V$$$$$$$$$$::.. .$$$:X//VA$H/X/XX////
* $$$$$$$$::. . . ..:I::. . .:..I:. ..$$$$XHAMMHIHA//XX/
* $$$$$$$:..:... ..I:. .::. . .$$$$VMMMMMWV'I:.XX
* $$$$$$$:. .:$$$$$$$$. .I:. .:.. .$$$VMMMMV/.//XXXX
* $$$$$$::. $$4$$$$. now .I. .:. with ..:$;.. .$$VMMMA/XXXXX
* $$$$$$A:. /$$$$$$:dynamic.I.. .:..:breast .:$$$$:. .AMMMM$XXXX//
* $$$$$IMMAAM$$$$$$:: .I.. ... .:$$$$$$$:. .AMMMMM'$$XX$
* $$$$$IMMHMV$$$$$$$. phys.:I...ics .$$$$$$$$$$:. ..VMMMMMX$$$:X
* $$$$$:MHHM$$$$$$$$$:.. ...:II... . ... :$$$$$$$$$$$$$:.:VMMMM'$$$$$
* $$$$$HMHMV$$$$$$$$$$;....:.:II:......- .$$$$$$$$$$$$$$$$:.:VM'$$$$$$
* $$$$4HMHMV$$$$$$$$$$;....:.:II:......- .$$$$$$$$$$$$$$$$:.:VM'A$$$$$
* $$$$$HMHM4$$$$$$$$$$$$;. .:::.::.. .:$$$$$$$$$$$$$$$$$$$$$$$V$$$$$$
* $$$$$MHHV$$$$$$$$$$$$:. .:. .:$MV'.VMM$$$$$$$$$$$$$$$$$$$$$
* $$$$$MMM$$MM$$$MM$$$$... .. :$$. .VM$$$$$$MMMM$M$$$$$$$$
* $$$$$MMHA$$MMMM$M4$V .. ... .:$: .VM$$$$MMMMV..VM$$M$M$
* $$$$$MMLMA$MMMMMM$$' ... ... :$$. .H$$$$MV' .VMM$M$$
* $$$$$MHAHH$V. .V$$. ... ... .:$$ V$$$MV VMM$MM
* $$$$$MMMMHA V$ :. . . ..'V. V$$V VMM$$
* $$$$$$VHMHA $ .:.. .: . $. .$$ VMM$
* $$$$$$VAA . .::.. .; :.. . $$ VMM$$
* $$$$$$$$$$ .::. . .. . . V. VM
* $$$$$M$$$$. ..:.:... . . .. I .V
* $$$$$$M$$$$ . .:. :... . .. .. . .
* $$$$$$$$$$$. . . .:.. . .::. .
* $$$V''$$M$$$ : .
* $$$ .VM$. .:. $$$$ .
* $$$ $$$ :. $$$$$$$$$
* $$$$ $$ .. $$$$$$$ .
* $$$$$ IV .. $$$$$
* $$$$$$. $ .. .$$$. .
* M$$$$$$$ . ... .: :...
* MM$$$$$$$ ... .. :... .
* .V$MM$$$4$ ... .: .::..
* .V$MM$$$$ .. .$.:... .
* .V$MM$$$$ ... :$$$::.. .
* V$:V$:$$ ... .$$$::.. .
* $ $$ ;$ .. .$$$$:.... .
* . V$ .$ ... .$$$$.:..
* V$ $ .. .$$$$$:.. . .
* V$ . .. . $$$::.. .
* V$ ... . $$$;:.
* $I .. . $$;:.. .
* $. ... .. $;;.. .
* $ ... ... $;:.. ..
* $ ... .. $$.::$
* I. ... .. $$$$
* . ... ...
* .:... .:.
* ::.. ...
* .:.. .:.
* :.. .::.
* :. .:..
* :. .$$$;.
* :. $$$$$$.
* :$$$$$$$$$
* $4$$$$$$$$
* $$$$$$$$$$
* ;$$$$$M$$;
* $$$$$IM$$$
* V$$$M$$$$$
* ;$$$$M$$$;
* $$$M$$$$;
* $$$$$$$$
* ;$$$$$$$
* $$$$$$$
* $$$$$$$
* $$$$$$$
* ;$$$$$$
* $$$$$$
* $$$$$$.
* $$$$$$$$
* $$$$$$$:
* $$$$$$$:
* :$$$$$$:
* $$$$$$$:
* $$$$$$$$
* $$$$$$$$
* $$$$$$
* $$$$
* $$
*
*
*/

View File

@@ -0,0 +1,79 @@
/**
* @file ascentfloatercontactgroups.h
* @Author Charley Levenque
* Allows the user to assign friends to contact groups for advanced sorting.
*
* Created Sept 6th 2010
*
* ALL SOURCE CODE IS PROVIDED "AS IS." THE CREATOR MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
*
* "use a free softwares" - Richard Stallman, LUNIX Operations System
*/
#ifndef ASCENT_CONTACT_GROUPS
#define ASCENT_CONTACT_GROUPS
#include "llfloater.h"
class LLScrollListCtrl;
class ASFloaterContactGroups : public LLFloater
{
public:
ASFloaterContactGroups();
virtual ~ASFloaterContactGroups();
// by convention, this shows the floater and does instance management
static void show(void*);
void populateGroupList();
private:
//assuming we just need one, which is typical
static ASFloaterContactGroups* sInstance;
};
#endif // ASCENT_UPLOAD_BROWSER
/*
Thank you for come my website!
This is web page of Steve Eletor.
I live in Slovakia, in place called Presov.
I like all kind of thing, including robot film,
snake, castle, computer, and free softwares.
I have make a webpage for pet and one for family
and one for my computer.
you may have herd of richard stallman who wrot a
GNU operate system for PC.
"Welcome to website of Eletor" - Richard Stallman
I currently have "2.8.6 DOS."
but soon i hope to get "3.8.6 DOS IBM"
when i use internet, i dial into friend who
has a LINUX and he also has ISDN. my father
has also a computer, it is WINDOWS 98. i use
that to do thing like make website, and for
work, but my father not let me use it for
free softwares or the I.R.C chats.
please understand, while i can type lowercase
on my website, i have to update from a friends
computer. for IRC, i type in all caps as my
slovak keyboard does not have a lowercase. for
EMAIL, i reply to a friend on IRC who check my
email for me right now, also in all cap.
my father not very understand of free softwares
and he make a fun of RMS.
*/

View File

@@ -68,6 +68,12 @@
#include "llviewernetwork.h"
#include "hippogridmanager.h"
#include "llchat.h"
#include "llfloaterchat.h"
// <dogmode> stuff for Contact groups
#include "ascentfloatercontactgroups.h"
//Maximum number of people you can select to do an operation on at once.
#define MAX_FRIEND_SELECT 20
#define DEFAULT_PERIOD 5.0
@@ -192,6 +198,54 @@ void LLPanelFriends::updateFriends(U32 changed_mask)
mShowMaxSelectWarning = true;
}
// <dogmode>
// Contact search and group system.
// 09/05/2010 - Charley Levenque
void LLPanelFriends::filterContacts()
{
//LLComboBox* combo = getChild<LLComboBox>("buddy_group_combobox");
//if (combo->getValue().asString() != "All")
std::string friend_name;
std::string search_name;
search_name = LLPanelFriends::getChild<LLLineEditor>("buddy_search_lineedit")->getValue().asString();
if (search_name != "" && search_name != mLastContactSearch)
{
mLastContactSearch = search_name;
//LLStringUtil::toLower(search_name);
refreshNames(LLFriendObserver::ADD);
std::vector<LLScrollListItem*> vFriends = mFriendsList->getAllData(); // all of it.
for (std::vector<LLScrollListItem*>::iterator itr = vFriends.begin(); itr != vFriends.end(); ++itr)
{
friend_name = (*itr)->getColumn(LIST_FRIEND_NAME)->getValue().asString();
//LLStringUtil::toLower(friend_name);
BOOL show_entry = (friend_name.find(search_name) != std::string::npos);
if (!show_entry)
{
mFriendsList->deleteItems((*itr)->getValue());
}
}
refreshUI();
}
else if (search_name == "" && search_name != mLastContactSearch) refreshNames(LLFriendObserver::ADD);
}
void LLPanelFriends::onContactSearchKeystroke(LLLineEditor* caller, void* user_data)
{
if (caller)
{
LLPanelFriends* panelp = (LLPanelFriends*)caller->getParent();
if (panelp)
{
panelp->filterContacts();
}
}
}
// --
// virtual
BOOL LLPanelFriends::postBuild()
{
@@ -202,6 +256,16 @@ BOOL LLPanelFriends::postBuild()
childSetCommitCallback("friend_list", onSelectName, this);
childSetDoubleClickCallback("friend_list", onClickIM);
// <dogmode>
// Contact search and group system.
// 09/05/2010 - Charley Levenque
LLLineEditor* contact = getChild<LLLineEditor>("buddy_search_lineedit");
if (contact)
{
contact->setKeystrokeCallback(&onContactSearchKeystroke);
}
// --
getChild<LLTextBox>("s_num")->setValue("0");
getChild<LLTextBox>("f_num")->setValue(llformat("%d", mFriendsList->getItemCount()));
@@ -209,6 +273,7 @@ BOOL LLPanelFriends::postBuild()
refreshNames(changed_mask);
childSetAction("im_btn", onClickIM, this);
childSetAction("assign_btn", onClickAssign, this);
childSetAction("profile_btn", onClickProfile, this);
childSetAction("offer_teleport_btn", onClickOfferTeleport, this);
childSetAction("pay_btn", onClickPay, this);
@@ -417,6 +482,7 @@ void LLPanelFriends::refreshRightsChangeList()
if (num_selected == 0) // nothing selected
{
childSetEnabled("im_btn", FALSE);
childSetEnabled("assign_btn", FALSE);
childSetEnabled("offer_teleport_btn", FALSE);
}
else // we have at least one friend selected...
@@ -425,6 +491,7 @@ void LLPanelFriends::refreshRightsChangeList()
// to be consistent with context menus in inventory and because otherwise
// offline friends would be silently dropped from the session
childSetEnabled("im_btn", selected_friends_online || num_selected == 1);
childSetEnabled("assign_btn", TRUE);
childSetEnabled("offer_teleport_btn", can_offer_teleport);
}
}
@@ -538,7 +605,7 @@ BOOL LLPanelFriends::refreshNamesPresence(const LLAvatarTracker::buddy_map_t & a
return have_names;
}
// meal disk
void LLPanelFriends::refreshUI()
{
BOOL single_selected = FALSE;
@@ -570,6 +637,7 @@ void LLPanelFriends::refreshUI()
//(single_selected will always be true in this situations)
childSetEnabled("remove_btn", single_selected);
childSetEnabled("im_btn", single_selected);
childSetEnabled("assign_btn", single_selected);
childSetEnabled("friend_rights", single_selected);
refreshRightsChangeList();
@@ -625,6 +693,11 @@ void LLPanelFriends::onClickProfile(void* user_data)
}
// static
void LLPanelFriends::onClickAssign(void* user_data)
{
ASFloaterContactGroups::show(user_data);
}
void LLPanelFriends::onClickIM(void* user_data)
{
LLPanelFriends* panelp = (LLPanelFriends*)user_data;

View File

@@ -101,6 +101,9 @@ private:
// protected members
typedef std::map<LLUUID, S32> rights_map_t;
void refreshNames(U32 changed_mask);
// <dogmode> Contacts search and group system
void filterContacts();
// --
BOOL refreshNamesSync(const LLAvatarTracker::buddy_map_t & all_buddies);
BOOL refreshNamesPresence(const LLAvatarTracker::buddy_map_t & all_buddies);
void refreshUI();
@@ -126,8 +129,9 @@ private:
static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response);
static void onPickAvatar(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* user_data);
static void onMaximumSelect(void* user_data);
static void onContactSearchKeystroke(LLLineEditor* caller, void* user_data);
static void onClickIM(void* user_data);
static void onClickAssign(void* user_data);
static void onClickProfile(void* user_data);
static void onClickAddFriend(void* user_data);
static void onClickRemove(void* user_data);
@@ -154,6 +158,7 @@ private:
BOOL mAllowRightsChange;
S32 mNumRightsChanged;
S32 mNumOnline;
std::string mLastContactSearch;
static bool merging;
};

View File

@@ -572,7 +572,7 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region)
// now set in processRegionInfo for teen grid detection
childSetEnabled("kick_btn", allow_modify);
childSetEnabled("kick_all_btn", allow_modify);
childSetEnabled("im_btn", allow_modify);
childSetEnabled("assign_btn", allow_modify);
childSetEnabled("manage_telehub_btn", allow_modify);
// Data gets filled in by processRegionInfo

View File

@@ -773,10 +773,12 @@ class LLFileLogOut : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
std::string command(gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() + gDirUtilp->getExecutableFilename() + " --channel \"Ascent\" --settings settings_ascent.xml");
std::string command(gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() + gDirUtilp->getExecutableFilename());
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
gViewerWindow->getWindow()->ShellEx(command);
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
LLAppViewer::instance()->userQuit();
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
return true;
}
};

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- All our XML is utf-8 encoded. -->
<!-- Floaters can optionally have their titlebar drag handle on the left.
If so, the title is not visible.
When a floater is resizable, a min_width and min_height must be specified. -->
<floater
name="floater_contact_groups"
title="Manage Contact Groups"
can_resize="true"
can_minimize="false"
can_close="true"
can_drag_on_left="false"
width="335"
height="275"
min_width="335"
min_height="275"
>
<tab_container bottom="30" follows="left|right|top|bottom" height="220" left="4"
name="ContactTabs" tab_position="top" tab_width="65" width="330">
<panel name="AddPanel" label="Add Contact"
border="true" bottom_delta="-10" height="150" left="6" width="135"
mouse_opaque="true">
<text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
bottom="-24" drop_shadow_visible="true" follows="left|top"
font="SansSerifSmall" h_pad="0" halign="left" height="16" left="10"
mouse_opaque="true" name="InstructManageContacts" v_pad="0"
width="240">
Create a new group or select an existing one:
</text>
<line_editor bevel_style="in" border_style="line"
border_thickness="1" bottom_delta="-20" enabled="true" follows="left|top|right"
font="SansSerifSmall" height="16" left="10" right="-120"
max_length="254" mouse_opaque="true" name="add_group_lineedit"
width="112" />
<button bottom_delta="-3" follows="top|right" font="SansSerif" halign="center"
height="20" label="Create" label_selected="Create" left_delta="20" right="-10"
mouse_opaque="true" name="create_grp_btn" scale_image="TRUE" width="90" />
<scroll_list background_visible="true" bottom_delta="-125" column_padding="5"
draw_border="true" draw_heading="false" draw_stripes="true"
follows="left|top|right" height="120" left="10" mouse_opaque="true"
multi_select="false" name="group_scroll_list" width="115" />
</panel>
<panel name="ManageGroupsPanel" label="Manage Groups"
border="true" bottom="0" height="150" left="0" width="135"
mouse_opaque="true">
<text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
bottom="-24" drop_shadow_visible="true" follows="left|top"
font="SansSerifSmall" h_pad="0" halign="left" height="16" left="10"
mouse_opaque="true" name="InstructSelectCallingCard" v_pad="0"
width="220">
Select a calling card:
</text>
<inventory_panel allow_multi_select="false" border="true" bottom_delta="-117"
follows="left|top|right|bottom" height="110" left="10" mouse_opaque="true"
name="InventoryPanel" sort_order="AvatarPickerSortOrder" width="115" />
</panel>
</tab_container>
<button bottom="6" left="10" follows="left|bottom" font="SansSerif" halign="center"
width="95" height="20" label="Save" label_selected="Save" mouse_opaque="true"
name="Save" scale_image="TRUE" />
<button bottom_delta="0" right="240" follows="left|bottom" font="SansSerif" halign="center"
width="95" height="20" label="Cancel" label_selected="Cancel" mouse_opaque="true"
name="Cancel" scale_image="TRUE" />
</floater>

View File

@@ -3,11 +3,30 @@
<string name="Multiple">
Multiple friends...
</string>
<text bottom_delta="-30" follows="left|top" font="SansSerif" height="16" left_delta="10" name="friends" width="110">
Contact Group:
</text>
<text bottom_delta="-20" follows="left|top" font="SansSerif" height="16" left_delta="0" name="friends" width="110">
Contact Search:
</text>
<combo_box allow_text_entry="false" enabled="true" follows="right|top"
bottom_delta="20" height="18" hidden="false" left="-235" max_chars="20" mouse_opaque="true"
tool_tip="Buddy group"
name="buddy_group_combobox" width="130">
<combo_item type="string" length="1" enabled="true" name="All"
value="All">
All
</combo_item>
</combo_box>
<line_editor bottom_delta="-14" enabled="true" follows="right|top" font="SansSerif"
height="18" left="-235" name="buddy_search_lineedit"
tool_tip="The friend name you want to search for"
width="130" />
<scroll_list bottom="10" can_resize="true" column_padding="0" draw_heading="true"
follows="left|top|bottom|right" left="10" multi_select="true"
name="friend_list" right="-100" search_column="1"
tool_tip="Hold shift or control while clicking to select multiple friends"
top="-10">
top="-60">
<column image="ff_online_status_button.tga" name="icon_online_status"
tool_tip="Online status" width="20" />
<column dynamicwidth="true" label="Name" name="friend_name" tool_tip="Name" />
@@ -26,6 +45,9 @@
<column name="friend_last_update_generation" width="0" />
</scroll_list>
<pad bottom="-7" height="0" left="-90" width="1" />
<button bottom_delta="-25" follows="top|right" height="22" label="Set Contact"
left_delta="0" name="assign_btn" tool_tip="Asign a friend to a Contact Group"
width="80" />
<button bottom_delta="-25" follows="top|right" height="22" label="IM/Call"
left_delta="0" name="im_btn" tool_tip="Open Instant Message session"
width="80" />
@@ -41,7 +63,7 @@
<button bottom_delta="-25" follows="top|right" height="22" label="Remove..."
left_delta="0" name="remove_btn"
tool_tip="Remove this person from your friends list" width="80" />
<button bottom_delta="-35" follows="top|right" height="22" label="Add..."
<button bottom_delta="-25" follows="top|right" height="22" label="Add..."
left_delta="0" name="add_btn" tool_tip="Offer friendship to a resident"
width="80" />
<text bottom_delta="-50" follows="right|top" font="SansSerif" height="32" left_delta="10" name="friends" width="95">