Merge in Storm-1838

This commit is contained in:
Inusaito Sayori
2013-07-31 15:49:34 -04:00
parent 9676eac3db
commit 04c78a1d8a
15 changed files with 314 additions and 50 deletions

View File

@@ -126,7 +126,7 @@ enum EInstantMessage
IM_LURE_ACCEPTED = 23,
IM_LURE_DECLINED = 24,
IM_GODLIKE_LURE_USER = 25,
IM_YET_TO_BE_USED = 26,
IM_TELEPORT_REQUEST = 26,
// IM that notifie of a new group election.
// Name is name of person who called vote.

View File

@@ -1330,7 +1330,7 @@ This should be as low as possible, but too low may break functionality</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>AntiSpamTeleports</key>
<key>AntiSpamTeleportRequests</key>
<map>
<key>Comment</key>
<string>When true, dialogs from teleport requests from other residents will be blocked.</string>
@@ -1341,6 +1341,17 @@ This should be as low as possible, but too low may break functionality</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>AntiSpamTeleports</key>
<map>
<key>Comment</key>
<string>When true, dialogs from teleport offers from other residents will be blocked.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>_NACL_Antispam</key>
<map>
<key>Comment</key>

View File

@@ -331,6 +331,7 @@ void LLPrefsAscentChat::refreshValues()
mBlockItemOfferSpam = gSavedSettings.getBOOL("AntiSpamItemOffers");
mBlockScriptSpam = gSavedSettings.getBOOL("AntiSpamScripts");
mBlockTeleportSpam = gSavedSettings.getBOOL("AntiSpamTeleports");
mBlockTeleportRequestSpam = gSavedSettings.getBOOL("AntiSpamTeleportRequests");
mNotifyOnSpam = gSavedSettings.getBOOL("AntiSpamNotify");
mSoundMulti = gSavedSettings.getU32("_NACL_AntiSpamSoundMulti");
mNewLines = gSavedSettings.getU32("_NACL_AntiSpamNewlines");
@@ -548,6 +549,7 @@ void LLPrefsAscentChat::cancel()
gSavedSettings.setBOOL("AntiSpamItemOffers", mBlockItemOfferSpam);
gSavedSettings.setBOOL("AntiSpamScripts", mBlockScriptSpam);
gSavedSettings.setBOOL("AntiSpamTeleports", mBlockTeleportSpam);
gSavedSettings.setBOOL("AntiSpamTeleportRequests", mBlockTeleportRequestSpam);
gSavedSettings.setBOOL("AntiSpamNotify", mNotifyOnSpam);
gSavedSettings.setU32("_NACL_AntiSpamSoundMulti", mSoundMulti);
gSavedSettings.setU32("_NACL_AntiSpamNewlines", mNewLines);

View File

@@ -107,6 +107,7 @@ protected:
BOOL mBlockItemOfferSpam;
BOOL mBlockScriptSpam;
BOOL mBlockTeleportSpam;
bool mBlockTeleportRequestSpam;
BOOL mNotifyOnSpam;
BOOL mSoundMulti;
U32 mNewLines;

View File

@@ -35,7 +35,7 @@
#include "roles_constants.h" // for GP_MEMBER_INVITE
#include "llagent.h"
#include "llcallingcard.h" // LLAvatarTracker
#include "llcallingcard.h" // for LLAvatarTracker
#include "llfloateravatarinfo.h"
#include "llfloatergroupinvite.h"
#include "llfloatergroups.h"
@@ -50,6 +50,8 @@
#include "llvoiceclient.h"
#include "llweb.h"
#include "llslurl.h" // IDEVO
#include "llavatarname.h"
#include "llagentui.h"
// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -449,6 +451,77 @@ void LLAvatarActions::pay(const LLUUID& id)
}
}
void LLAvatarActions::teleport_request_callback(const LLSD& notification, const LLSD& response)
{
S32 option;
if (response.isInteger())
{
option = response.asInteger();
}
else
{
option = LLNotificationsUtil::getSelectedOption(notification, response);
}
if (0 == option)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_MessageBlock);
msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
msg->addUUIDFast(_PREHASH_ToAgentID, notification["substitutions"]["uuid"] );
msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
msg->addU8Fast(_PREHASH_Dialog, IM_TELEPORT_REQUEST);
msg->addUUIDFast(_PREHASH_ID, LLUUID::null);
msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
std::string name;
LLAgentUI::buildFullname(name);
msg->addStringFast(_PREHASH_FromAgentName, name);
msg->addStringFast(_PREHASH_Message, response["message"]);
msg->addU32Fast(_PREHASH_ParentEstateID, 0);
msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
gMessageSystem->addBinaryDataFast(
_PREHASH_BinaryBucket,
EMPTY_BINARY_BUCKET,
EMPTY_BINARY_BUCKET_SIZE);
gAgent.sendReliableMessage();
}
}
// static
void LLAvatarActions::teleportRequest(const LLUUID& id)
{
LLAvatarName av_name;
if (LLAvatarNameCache::get(id, &av_name)) // Bypass expiration, open NOW!
on_avatar_name_cache_teleport_request(id, av_name);
else
LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_cache_teleport_request, _1, _2));
}
// static
void LLAvatarActions::on_avatar_name_cache_teleport_request(const LLUUID& id, const LLAvatarName& av_name)
{
LLSD notification;
notification["uuid"] = id;
//notification["NAME_SLURL"] = LLSLURL("agent", id, "about").getSLURLString();
std::string name;
LLAvatarNameCache::getPNSName(av_name, name);
notification["NAME"] = name;
LLSD payload;
LLNotificationsUtil::add("TeleportRequestPrompt", notification, payload, teleport_request_callback);
}
// static
void LLAvatarActions::kick(const LLUUID& id)
{

View File

@@ -101,6 +101,13 @@ public:
* Give money to the avatar.
*/
static void pay(const LLUUID& id);
/**
* Request teleport from other avatar
*/
static void teleportRequest(const LLUUID& id);
static void teleport_request_callback(const LLSD& notification, const LLSD& response);
/**
* Block/unblock the avatar.
*/
@@ -211,6 +218,7 @@ private:
static bool handleFreeze(const LLSD& notification, const LLSD& response);
static bool handleUnfreeze(const LLSD& notification, const LLSD& response);
static void callback_invite_to_group(LLUUID group_id, LLUUID id);
static void on_avatar_name_cache_teleport_request(const LLUUID& id, const LLAvatarName& av_name);
public:
// Just request friendship, no dialog.

View File

@@ -4595,6 +4595,16 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act
LLAvatarActions::offerTeleport(item->getCreatorUUID());
}
}
else if ("request_lure" == action)
{
LLViewerInventoryItem *item = getItem();
if (item && (item->getCreatorUUID() != gAgent.getID()) &&
(!item->getCreatorUUID().isNull()))
{
LLAvatarActions::teleportRequest(item->getCreatorUUID());
}
}
else LLItemBridge::performAction(model, action);
}
@@ -4678,6 +4688,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
items.push_back(std::string("Send Instant Message Separator"));
items.push_back(std::string("Send Instant Message"));
items.push_back(std::string("Offer Teleport..."));
items.push_back(std::string("Request Teleport..."));
items.push_back(std::string("Conference Chat"));
if (!good_card)
@@ -4687,6 +4698,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!good_card || !user_online)
{
disabled_items.push_back(std::string("Offer Teleport..."));
disabled_items.push_back(std::string("Request Teleport..."));
disabled_items.push_back(std::string("Conference Chat"));
}
}

View File

@@ -1394,6 +1394,7 @@ LLPanelAvatar::LLPanelAvatar(
factory_map["My Notes"] = LLCallbackMap(createPanelAvatarNotes, this);
mCommitCallbackRegistrar.add("Profile.Web", boost::bind(LLAvatarActions::showProfile, boost::bind(&LLPanelAvatar::getAvatarID, this), true));
mCommitCallbackRegistrar.add("Profile.TeleportRequest", boost::bind(LLAvatarActions::teleportRequest, boost::bind(&LLPanelAvatar::getAvatarID, this)));
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar.xml", &factory_map);
selectTab(0);

View File

@@ -9371,6 +9371,17 @@ class ListRequestFriendship : public view_listener_t
}
};
class ListRequestTeleport : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLScrollListCtrl* list = get_focused_list();
if (!list) return false;
LLAvatarActions::teleportRequest(list->getStringUUIDSelectedItem());
return true;
}
};
class ListShowProfile : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
@@ -9853,6 +9864,7 @@ void initialize_menus()
addMenu(new ListPay(), "List.Pay");
addMenu(new ListRemoveFriend(), "List.RemoveFriend");
addMenu(new ListRequestFriendship(), "List.RequestFriendship");
addMenu(new ListRequestTeleport(), "List.RequestTeleport");
addMenu(new ListShowProfile(), "List.ShowProfile");
addMenu(new ListStartAdhocCall(), "List.StartAdhocCall");
addMenu(new ListStartCall(), "List.StartCall");

View File

@@ -1960,7 +1960,7 @@ static std::string clean_name_from_im(const std::string& name, EInstantMessage t
case IM_LURE_ACCEPTED:
case IM_LURE_DECLINED:
case IM_GODLIKE_LURE_USER:
case IM_YET_TO_BE_USED:
case IM_TELEPORT_REQUEST:
case IM_GROUP_ELECTION_DEPRECATED:
//IM_GOTO_URL
//IM_FROM_TASK_AS_ALERT
@@ -3020,8 +3020,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
break;
case IM_LURE_USER:
case IM_TELEPORT_REQUEST:
{
if(antispam || gSavedSettings.getBOOL("AntiSpamTeleports")) return; //NaCl Antispam
if (antispam || gSavedSettings.getBOOL(dialog == IM_LURE_USER ? "AntiSpamTeleports" : "AntiSpamTeleportRequests")) return; //NaCl Antispam
// [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Added: RLVa-1.2.2c
// If the lure sender is a specific @accepttp exception they will override muted and busy status
bool fRlvSummon = (rlv_handler_t::isEnabled()) && (gRlvHandler.isException(RLV_BHVR_ACCEPTTP, from_id));
@@ -3054,7 +3055,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
bool canUserAccessDstRegion = true;
bool doesUserRequireMaturityIncrease = false;
if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access))
// Do not parse the (empty) lure bucket for TELEPORT_REQUEST
if (IM_TELEPORT_REQUEST != dialog && parse_lure_bucket(region_info, region_handle, pos, look_at, region_access))
{
region_access_str = LLViewerRegion::accessToString(region_access);
region_access_icn = LLViewerRegion::getAccessIcon(region_access);
@@ -3098,7 +3100,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
// [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Modified: RLVa-1.2.2c
if (rlv_handler_t::isEnabled())
{
if (!gRlvHandler.canTeleportViaLure(from_id))
if (IM_TELEPORT_REQUEST != dialog && !gRlvHandler.canTeleportViaLure(from_id))
{
RlvUtil::sendBusyMessage(from_id, RlvStrings::getString(RLV_STRING_BLOCKED_TPLURE_REMOTE));
if (is_busy)
@@ -3107,7 +3109,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
// Censor lure message if: 1) restricted from receiving IMs from the sender, or 2) @showloc=n restricted
if ( (!gRlvHandler.canReceiveIM(from_id)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) )
if ( (!gRlvHandler.canReceiveIM(from_id)) || (IM_TELEPORT_REQUEST != dialog && gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) )
{
message = RlvStrings::getString(RLV_STRING_HIDDEN);
}
@@ -3129,7 +3131,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
//LLNotificationsUtil::add("TeleportOffered", args, payload);
// [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Modified: RLVa-1.2.2c
if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_ACCEPTTP)) || (fRlvSummon)) )
if ( IM_TELEPORT_REQUEST != dialog && (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_ACCEPTTP)) || (fRlvSummon)) )
{
gRlvHandler.setCanCancelTp(false);
if (is_busy)
@@ -3138,9 +3140,27 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
else
{
LLNotificationsUtil::add("TeleportOffered", args, payload);
/* Singu Note: No default constructor for LLNotification::Params
LLNotification::Params params;
if (IM_LURE_USER == dialog)
{
params.name = "TeleportOffered";
params.functor_name = "TeleportOffered";
}
else if (IM_TELEPORT_REQUEST == dialog)
{
params.name = "TeleportRequest";
params.functor_name = "TeleportRequest";
}
*/
LLNotification::Params params(IM_LURE_USER == dialog ? "TeleportOffered" : "TeleportRequest");
params.substitutions = args;
params.payload = payload;
LLNotifications::instance().add(params);
// <edit>
gAgent.showLureDestination(name, region_handle, pos.mV[VX], pos.mV[VY], pos.mV[VZ]);
if (IM_LURE_USER == dialog)
gAgent.showLureDestination(name, region_handle, pos.mV[VX], pos.mV[VY], pos.mV[VZ]);
// </edit>
}
// [/RLVa:KB]
@@ -7440,6 +7460,49 @@ void send_group_notice(const LLUUID& group_id,
bin_bucket_size);
}
void send_lures(const LLSD& notification, const LLSD& response)
{
std::string text = response["message"].asString();
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
text.append("\r\n").append(slurl.getSLURLString());
// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c
if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIMTO)) )
{
// Filter the lure message if one of the recipients of the lure can't be sent an IM to
for (LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
it != notification["payload"]["ids"].endArray(); ++it)
{
if (!gRlvHandler.canSendIM(it->asUUID()))
{
text = RlvStrings::getString(RLV_STRING_HIDDEN);
break;
}
}
}
// [/RLVa:KB]
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_StartLure);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_Info);
msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
msg->addStringFast(_PREHASH_Message, text);
for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
it != notification["payload"]["ids"].endArray();
++it)
{
LLUUID target_id = it->asUUID();
msg->nextBlockFast(_PREHASH_TargetData);
msg->addUUIDFast(_PREHASH_TargetID, target_id);
}
gAgent.sendReliableMessage();
}
bool handle_lure_callback(const LLSD& notification, const LLSD& response)
{
static const unsigned OFFER_RECIPIENT_LIMIT = 250;
@@ -7453,49 +7516,12 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response)
LLNotificationsUtil::add("TooManyTeleportOffers", args);
return false;
}
std::string text = response["message"].asString();
LLSLURL slurl;
LLAgentUI::buildSLURL(slurl);
text.append("\r\n").append(slurl.getSLURLString());
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if(0 == option)
{
// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c
if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIMTO)) )
{
// Filter the lure message if one of the recipients of the lure can't be sent an IM to
for (LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
it != notification["payload"]["ids"].endArray(); ++it)
{
if (!gRlvHandler.canSendIM(it->asUUID()))
{
text = RlvStrings::getString(RLV_STRING_HIDDEN);
break;
}
}
}
// [/RLVa:KB]
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_StartLure);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_Info);
msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
msg->addStringFast(_PREHASH_Message, text);
for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
it != notification["payload"]["ids"].endArray();
++it)
{
LLUUID target_id = it->asUUID();
msg->nextBlockFast(_PREHASH_TargetData);
msg->addUUIDFast(_PREHASH_TargetID, target_id);
}
gAgent.sendReliableMessage();
send_lures(notification, response);
}
return false;
@@ -7551,6 +7577,66 @@ void handle_lure(const uuid_vec_t& ids)
}
}
bool teleport_request_callback(const LLSD& notification, const LLSD& response)
{
LLUUID from_id = notification["payload"]["from_id"].asUUID();
if(from_id.isNull())
{
llwarns << "from_id is NULL" << llendl;
return false;
}
std::string from_name;
gCacheName->getFullName(from_id, from_name);
if(LLMuteList::getInstance()->isMuted(from_id) && !LLMuteList::getInstance()->isLinden(from_name))
{
return false;
}
S32 option = 0;
if (response.isInteger())
{
option = response.asInteger();
}
else
{
option = LLNotificationsUtil::getSelectedOption(notification, response);
}
switch(option)
{
// Yes
case 0:
{
LLSD dummy_notification;
dummy_notification["payload"]["ids"][0] = from_id;
LLSD dummy_response;
dummy_response["message"] = response["message"];
send_lures(dummy_notification, dummy_response);
}
break;
// Profile
case 3:
{
LLAvatarActions::showProfile(from_id);
LLNotificationsUtil::add(notification["name"], notification["substitutions"], notification["payload"]); //Respawn!
}
break;
// No
case 1:
default:
break;
}
return false;
}
static LLNotificationFunctorRegistration teleport_request_callback_reg("TeleportRequest", teleport_request_callback);
void send_improved_im(const LLUUID& to_id,
const std::string& name,

View File

@@ -44,6 +44,10 @@
<on_click function="List.OfferTeleport"/>
<on_enable function="List.EnableAnySelected"/>
</menu_item_call>
<menu_item_call label="Request Teleport" name="Request Teleport">
<on_click function="List.RequestTeleport"/>
<on_visible function="List.EnableSingleSelected"/>
</menu_item_call>
<menu_item_separator/>
<menu_item_call label="Mute/Unmute" name="Mute/Unmute">
<on_click function="List.Mute"/>

View File

@@ -275,6 +275,9 @@
mouse_opaque="true" name="Offer Teleport..." width="128">
<on_click filter="" function="Inventory.DoToSelected" userdata="lure" />
</menu_item_call>
<menu_item_call label="Request Teleport..." name="Request Teleport...">
<on_click filter="" function="Inventory.DoToSelected" userdata="request_lure" />
</menu_item_call>
<menu_item_call bottom_delta="-18" height="18" label="Start Conference Chat" left="0"
mouse_opaque="true" name="Conference Chat" width="128">
<on_click filter="" function="Inventory.BeginIMSession" userdata="selected" />

View File

@@ -49,6 +49,10 @@
<on_click function="Radar.TeleportTo"/>
<on_enable function="List.EnableSingleSelected"/>
</menu_item_call>
<menu_item_call label="Request Teleport" name="Request Teleport">
<on_click function="List.RequestTeleport"/>
<on_visible function="List.EnableSingleSelected"/>
</menu_item_call>
<menu_item_call label="Track/Untrack" name="Track/Untrack">
<on_click function="Radar.Track"/>
<on_enable function="List.EnableSingleSelected"/>

View File

@@ -3615,6 +3615,27 @@ Join me in [REGION]
</form>
</notification>
<notification
icon="alertmodal.tga"
name="TeleportRequestPrompt"
type="alertmodal">
Request a teleport to [NAME] with the following message
<tag>confirm</tag>
<form name="form">
<input name="message" type="text">
</input>
<button
default="true"
index="0"
name="OK"
text="OK"/>
<button
index="1"
name="Cancel"
text="Cancel"/>
</form>
</notification>
<notification
icon="alertmodal.tga"
name="TooManyTeleportOffers"
@@ -6765,6 +6786,31 @@ An object named [OBJECTFROMNAME] owned by (an unknown user) has given you a [OBJ
</form>
</notification>
<notification
icon="notify.tga"
name="TeleportRequest"
type="notify">
[NAME] is requesting to be teleported to your location.
[MESSAGE]
Offer a teleport?
<tag>confirm</tag>
<form name="form">
<button
index="0"
name="Yes"
text="Yes"/>
<button
index="1"
name="No"
text="No"/>
<button
index="3"
name="Profile"
text="Profile"/>
</form>
</notification>
<notification
icon="notify.tga"
name="GotoURL"

View File

@@ -111,6 +111,7 @@ The following wildcards are available to enhance your autoresponses: #n for user
<check_box control_name="AntiSpamScripts" height="16" label="Scripts" name="Scripts" bottom_delta="0" left_delta="120"/>
<check_box control_name="AntiSpamTeleports" height="16" label="Teleport Offers" name="Teleport Offers" bottom_delta="0" left_delta="120"/>
<check_box control_name="AntiSpamGroupNotices" height="16" label="Group Notices" name="Group Notices" bottom_delta="0" left_delta="120"/>
<check_box control_name="AntiSpamTeleportRequests" height="16" label="Teleport Requests" name="Teleport Requests" left="14" bottom_delta="-20"/>
<check_box control_name="EnableGestureSounds" label="Enable Gesture Sounds" name="Enable Gesture Sounds" left="3" bottom_delta="-20"/>
</panel>