phoenix-firestorm/indra/newview/llinspectobject.cpp

705 lines
20 KiB
C++

/**
* @file llinspectobject.cpp
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* 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.
*
* 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.
*
* 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 "llviewerprecompiledheaders.h"
#include "llinspectobject.h"
// Viewer
#include "llinspect.h"
#include "llmediaentry.h"
#include "llnotificationsutil.h" // *TODO: Eliminate, add LLNotificationsUtil wrapper
#include "llselectmgr.h"
#include "llslurl.h"
#include "llviewermenu.h" // handle_object_touch(), handle_buy()
#include "llviewermedia.h"
#include "llviewermediafocus.h"
#include "llviewerobjectlist.h" // to select the requested object
// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0c)
#include "rlvhandler.h"
#include "lltoolpie.h"
// [/RLVa:KB]
// Linden libraries
#include "llbutton.h" // setLabel(), not virtual!
#include "llclickaction.h"
#include "llfloaterreg.h"
#include "llmenubutton.h"
#include "llresmgr.h" // getMonetaryString
#include "llsafehandle.h"
#include "llsidetray.h"
#include "lltextbox.h" // for description truncation
#include "lltoggleablemenu.h"
#include "lltrans.h"
#include "llui.h" // positionViewNearMouse()
#include "lluictrl.h"
class LLViewerObject;
//////////////////////////////////////////////////////////////////////////////
// LLInspectObject
//////////////////////////////////////////////////////////////////////////////
// Object Inspector, a small information window used when clicking
// in the ambient inspector widget for objects in the 3D world.
class LLInspectObject : public LLInspect
{
friend class LLFloaterReg;
public:
// object_id - Root object ID for which to show information
// Inspector will be positioned relative to current mouse position
LLInspectObject(const LLSD& object_id);
virtual ~LLInspectObject();
/*virtual*/ BOOL postBuild(void);
// Because floater is single instance, need to re-parse data on each spawn
// (for example, inspector about same avatar but in different position)
/*virtual*/ void onOpen(const LLSD& avatar_id);
// Release the selection and do other cleanup
/*virtual*/ void onClose(bool app_quitting);
// override the inspector mouse leave so timer is only paused if
// gear menu is not open
/* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask);
private:
// Refresh displayed data with information from selection manager
void update();
void hideButtons();
void updateButtons(LLSelectNode* nodep);
void updateSitLabel(LLSelectNode* nodep);
void updateTouchLabel(LLSelectNode* nodep);
void updateName(LLSelectNode* nodep);
void updateDescription(LLSelectNode* nodep);
void updatePrice(LLSelectNode* nodep);
void updateCreator(LLSelectNode* nodep);
void updateMediaCurrentURL();
void updateSecureBrowsing();
void onClickBuy();
void onClickPay();
void onClickTakeFreeCopy();
void onClickTouch();
void onClickSit();
void onClickOpen();
void onClickMoreInfo();
void onClickZoomIn();
private:
LLUUID mObjectID;
S32 mObjectFace;
viewer_media_t mMediaImpl;
LLMediaEntry* mMediaEntry;
LLSafeHandle<LLObjectSelection> mObjectSelection;
};
LLInspectObject::LLInspectObject(const LLSD& sd)
: LLInspect( LLSD() ), // single_instance, doesn't really need key
mObjectID(NULL), // set in onOpen()
mObjectFace(0),
mObjectSelection(NULL),
mMediaImpl(NULL),
mMediaEntry(NULL)
{
// can't make the properties request until the widgets are constructed
// as it might return immediately, so do it in postBuild.
mCommitCallbackRegistrar.add("InspectObject.Buy", boost::bind(&LLInspectObject::onClickBuy, this));
mCommitCallbackRegistrar.add("InspectObject.Pay", boost::bind(&LLInspectObject::onClickPay, this));
mCommitCallbackRegistrar.add("InspectObject.TakeFreeCopy", boost::bind(&LLInspectObject::onClickTakeFreeCopy, this));
mCommitCallbackRegistrar.add("InspectObject.Touch", boost::bind(&LLInspectObject::onClickTouch, this));
mCommitCallbackRegistrar.add("InspectObject.Sit", boost::bind(&LLInspectObject::onClickSit, this));
mCommitCallbackRegistrar.add("InspectObject.Open", boost::bind(&LLInspectObject::onClickOpen, this));
mCommitCallbackRegistrar.add("InspectObject.MoreInfo", boost::bind(&LLInspectObject::onClickMoreInfo, this));
mCommitCallbackRegistrar.add("InspectObject.ZoomIn", boost::bind(&LLInspectObject::onClickZoomIn, this));
}
LLInspectObject::~LLInspectObject()
{
}
/*virtual*/
BOOL LLInspectObject::postBuild(void)
{
// The XML file has sample data in it. Clear that out so we don't
// flicker when data arrives off network.
getChild<LLUICtrl>("object_name")->setValue("");
getChild<LLUICtrl>("object_creator")->setValue("");
getChild<LLUICtrl>("object_description")->setValue("");
getChild<LLUICtrl>("object_media_url")->setValue("");
// Set buttons invisible until we know what this object can do
hideButtons();
// Hide floater when name links clicked
LLTextBox* textbox = getChild<LLTextBox>("object_creator");
textbox->setURLClickedCallback(boost::bind(&LLInspectObject::closeFloater, this, false) );
// Hook up functionality
getChild<LLUICtrl>("buy_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickBuy, this));
getChild<LLUICtrl>("pay_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickPay, this));
getChild<LLUICtrl>("take_free_copy_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickTakeFreeCopy, this));
getChild<LLUICtrl>("touch_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickTouch, this));
getChild<LLUICtrl>("sit_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickSit, this));
getChild<LLUICtrl>("open_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickOpen, this));
getChild<LLUICtrl>("more_info_btn")->setCommitCallback(
boost::bind(&LLInspectObject::onClickMoreInfo, this));
// Watch for updates to selection properties off the network
LLSelectMgr::getInstance()->mUpdateSignal.connect(
boost::bind(&LLInspectObject::update, this) );
return TRUE;
}
// Multiple calls to showInstance("inspect_avatar", foo) will provide different
// LLSD for foo, which we will catch here.
//virtual
void LLInspectObject::onOpen(const LLSD& data)
{
// Start animation
LLInspect::onOpen(data);
// Extract appropriate avatar id
mObjectID = data["object_id"];
if(data.has("object_face"))
{
mObjectFace = data["object_face"];
}
// Position the inspector relative to the mouse cursor
// Similar to how tooltips are positioned
// See LLToolTipMgr::createToolTip
if (data.has("pos"))
{
LLUI::positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger());
}
else
{
LLUI::positionViewNearMouse(this);
}
// Promote hovered object to a complete selection, which will also force
// a request for selected object data off the network
LLViewerObject* obj = gObjectList.findObject( mObjectID );
if (obj)
{
// Media focus and this code fight over the select manager.
// Make sure any media is unfocused before changing the selection here.
LLViewerMediaFocus::getInstance()->clearFocus();
LLSelectMgr::instance().deselectAll();
mObjectSelection = LLSelectMgr::instance().selectObjectAndFamily(obj);
// Mark this as a transient selection
struct SetTransient : public LLSelectedNodeFunctor
{
bool apply(LLSelectNode* node)
{
node->setTransient(TRUE);
return true;
}
} functor;
mObjectSelection->applyToNodes(&functor);
// Does this face have media?
const LLTextureEntry* tep = obj->getTE(mObjectFace);
if (!tep)
return;
mMediaEntry = tep->hasMedia() ? tep->getMediaData() : NULL;
if(!mMediaEntry)
return;
mMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mMediaEntry->getMediaID());
}
}
// virtual
void LLInspectObject::onClose(bool app_quitting)
{
// Release selection to deselect
mObjectSelection = NULL;
getChild<LLMenuButton>("gear_btn")->hideMenu();
}
void LLInspectObject::update()
{
// Performance optimization, because we listen to updates from select mgr
// but we're never destroyed.
if (!getVisible()) return;
LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection();
if (!selection) return;
LLSelectNode* nodep = selection->getFirstRootNode();
if (!nodep) return;
updateButtons(nodep);
updateName(nodep);
updateDescription(nodep);
updateCreator(nodep);
updatePrice(nodep);
LLViewerObject* obj = nodep->getObject();
if(!obj)
return;
if ( mObjectFace < 0
|| mObjectFace >= obj->getNumTEs() )
{
return;
}
// Does this face have media?
const LLTextureEntry* tep = obj->getTE(mObjectFace);
if (!tep)
return;
mMediaEntry = tep->hasMedia() ? tep->getMediaData() : NULL;
if(!mMediaEntry)
return;
mMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mMediaEntry->getMediaID());
updateMediaCurrentURL();
updateSecureBrowsing();
}
void LLInspectObject::hideButtons()
{
getChild<LLUICtrl>("buy_btn")->setVisible(false);
getChild<LLUICtrl>("pay_btn")->setVisible(false);
getChild<LLUICtrl>("take_free_copy_btn")->setVisible(false);
getChild<LLUICtrl>("touch_btn")->setVisible(false);
getChild<LLUICtrl>("sit_btn")->setVisible(false);
getChild<LLUICtrl>("open_btn")->setVisible(false);
}
// *TODO: Extract this method from lltoolpie.cpp and put somewhere shared
extern U8 final_click_action(LLViewerObject*);
// Choose the "most relevant" operation for this object, and show a button for
// that operation as the left-most button in the inspector.
void LLInspectObject::updateButtons(LLSelectNode* nodep)
{
// We'll start with everyone hidden and show the ones we need
hideButtons();
LLViewerObject* object = nodep->getObject();
LLViewerObject *parent = (LLViewerObject*)object->getParent();
bool for_copy = anyone_copy_selection(nodep);
bool for_sale = enable_buy_object();
S32 price = nodep->mSaleInfo.getSalePrice();
U8 click_action = final_click_action(object);
if (for_copy
|| (for_sale && price == 0))
{
// Free copies have priority over other operations
getChild<LLUICtrl>("take_free_copy_btn")->setVisible(true);
}
else if (for_sale)
{
getChild<LLUICtrl>("buy_btn")->setVisible(true);
}
else if ( enable_pay_object() )
{
getChild<LLUICtrl>("pay_btn")->setVisible(true);
}
else if (click_action == CLICK_ACTION_SIT)
{
// Click-action sit must come before "open" because many objects on
// which you can sit have scripts, and hence can be opened
getChild<LLUICtrl>("sit_btn")->setVisible(true);
updateSitLabel(nodep);
}
else if (object->flagHandleTouch()
|| (parent && parent->flagHandleTouch()))
{
getChild<LLUICtrl>("touch_btn")->setVisible(true);
updateTouchLabel(nodep);
}
else if ( enable_object_open() )
{
// Open is last because anything with a script in it can be opened
getChild<LLUICtrl>("open_btn")->setVisible(true);
}
else
{
// By default, we can sit on anything
getChild<LLUICtrl>("sit_btn")->setVisible(true);
updateSitLabel(nodep);
}
// No flash
focusFirstItem(FALSE, FALSE);
}
void LLInspectObject::updateSitLabel(LLSelectNode* nodep)
{
LLButton* sit_btn = getChild<LLButton>("sit_btn");
if (!nodep->mSitName.empty())
{
sit_btn->setLabel( nodep->mSitName );
}
else
{
sit_btn->setLabel( getString("Sit") );
}
// [RLVa:KB] - Checked: 2010-03-06 (RLVa-1.2.0c) | Added: RLVa-1.2.0a
// RELEASE-RLVa: [SL-2.0.0] Make sure we're examining the same object that handle_sit_or_stand() will request a sit for
if (rlv_handler_t::isEnabled())
{
const LLPickInfo& pick = LLToolPie::getInstance()->getPick();
sit_btn->setEnabled( (pick.mObjectID.notNull()) && (gRlvHandler.canSit(pick.getObject(), pick.mObjectOffset)) );
}
// [/RLVa:KB]
}
void LLInspectObject::updateTouchLabel(LLSelectNode* nodep)
{
LLButton* sit_btn = getChild<LLButton>("touch_btn");
if (!nodep->mTouchName.empty())
{
sit_btn->setLabel( nodep->mTouchName );
}
else
{
sit_btn->setLabel( getString("Touch") );
}
}
void LLInspectObject::updateName(LLSelectNode* nodep)
{
std::string name;
if (!nodep->mName.empty())
{
name = nodep->mName;
}
else
{
name = LLTrans::getString("TooltipNoName");
}
getChild<LLUICtrl>("object_name")->setValue(name);
}
void LLInspectObject::updateDescription(LLSelectNode* nodep)
{
const char* const DEFAULT_DESC = "(No Description)";
std::string desc;
if (!nodep->mDescription.empty()
&& nodep->mDescription != DEFAULT_DESC)
{
desc = nodep->mDescription;
}
LLTextBox* textbox = getChild<LLTextBox>("object_description");
textbox->setValue(desc);
}
void LLInspectObject::updateMediaCurrentURL()
{
if(!mMediaEntry)
return;
LLTextBox* textbox = getChild<LLTextBox>("object_media_url");
std::string media_url = "";
textbox->setValue(media_url);
textbox->setToolTip(media_url);
LLStringUtil::format_map_t args;
if(mMediaImpl.notNull() && mMediaImpl->hasMedia())
{
LLPluginClassMedia* media_plugin = NULL;
media_plugin = mMediaImpl->getMediaPlugin();
if(media_plugin)
{
if(media_plugin->pluginSupportsMediaTime())
{
args["[CurrentURL]"] = mMediaImpl->getMediaURL();
}
else
{
args["[CurrentURL]"] = media_plugin->getLocation();
}
media_url = LLTrans::getString("CurrentURL", args);
}
}
else if(mMediaEntry->getCurrentURL() != "")
{
args["[CurrentURL]"] = mMediaEntry->getCurrentURL();
media_url = LLTrans::getString("CurrentURL", args);
}
textbox->setText(media_url);
textbox->setToolTip(media_url);
}
void LLInspectObject::updateCreator(LLSelectNode* nodep)
{
// final information for display
LLStringUtil::format_map_t args;
std::string text;
// Leave text blank until data loaded
if (nodep->mValid)
{
// Utilize automatic translation of SLURL into name to display
// a clickable link
// Objects cannot be created by a group, so use agent URL format
LLUUID creator_id = nodep->mPermissions->getCreator();
// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1b) | Added: RLVa-1.2.1b
// Only anonimize the creator if they're also the owner or if they're a nearby avie
if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) &&
((nodep->mPermissions->getOwner() == creator_id) || (RlvUtil::isNearbyAgent(creator_id))) )
{
// TODO-RLVa: [RLVa-1.2.2] We need to put a callback here in case the name hasn't previously resolved
std::string strFullName;
args["[CREATOR]"] = (gCacheName->getFullName(creator_id, strFullName)) ? RlvStrings::getAnonym(strFullName)
: LLTrans::getString("Unknown");
}
else
{
// [/RLVa:KB]
std::string creator_url =
LLSLURL("agent", creator_id, "about").getSLURLString();
args["[CREATOR]"] = creator_url;
// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1b) | Added: RLVa-1.2.1b
}
// [/RLVa:KB]
// created by one user but owned by another
std::string owner_url;
LLUUID owner_id;
bool group_owned = nodep->mPermissions->isGroupOwned();
if (group_owned)
{
owner_id = nodep->mPermissions->getGroup();
owner_url = LLSLURL("group", owner_id, "about").getSLURLString();
}
else
{
owner_id = nodep->mPermissions->getOwner();
// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1b) | Added: RLVa-1.2.1b
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
{
// TODO-RLVa: [RLVa-1.2.2] We need to put a callback here in case the name hasn't previously resolved
std::string strFullName;
owner_url = (gCacheName->getFullName(owner_id, strFullName)) ? RlvStrings::getAnonym(strFullName)
: LLTrans::getString("Unknown");
}
else
{
// [/RLVa:KB]
owner_url = LLSLURL("agent", owner_id, "about").getSLURLString();
// [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.1b) | Added: RLVa-1.2.1b
}
// [/RLVa:KB]
}
args["[OWNER]"] = owner_url;
if (creator_id == owner_id)
{
// common case, created and owned by one user
text = getString("Creator", args);
}
else
{
text = getString("CreatorAndOwner", args);
}
}
getChild<LLUICtrl>("object_creator")->setValue(text);
}
void LLInspectObject::updatePrice(LLSelectNode* nodep)
{
// *TODO: Only look these up once and use for both updateButtons and here
bool for_copy = anyone_copy_selection(nodep);
bool for_sale = enable_buy_object();
S32 price = nodep->mSaleInfo.getSalePrice();
bool show_price_icon = false;
std::string line;
if (for_copy
|| (for_sale && price == 0))
{
line = getString("PriceFree");
show_price_icon = true;
}
else if (for_sale)
{
LLStringUtil::format_map_t args;
args["[AMOUNT]"] = LLResMgr::getInstance()->getMonetaryString(price);
line = getString("Price", args);
show_price_icon = true;
}
getChild<LLUICtrl>("price_text")->setValue(line);
getChild<LLUICtrl>("price_icon")->setVisible(show_price_icon);
}
void LLInspectObject::updateSecureBrowsing()
{
bool is_secure_browsing = false;
if(mMediaImpl.notNull()
&& mMediaImpl->hasMedia())
{
LLPluginClassMedia* media_plugin = NULL;
std::string current_url = "";
media_plugin = mMediaImpl->getMediaPlugin();
if(media_plugin)
{
if(media_plugin->pluginSupportsMediaTime())
{
current_url = mMediaImpl->getMediaURL();
}
else
{
current_url = media_plugin->getLocation();
}
}
std::string prefix = std::string("https://");
std::string test_prefix = current_url.substr(0, prefix.length());
LLStringUtil::toLower(test_prefix);
if(test_prefix == prefix)
{
is_secure_browsing = true;
}
}
getChild<LLUICtrl>("secure_browsing")->setVisible(is_secure_browsing);
}
// For the object inspector, only unpause the fade timer
// if the gear menu is not open
void LLInspectObject::onMouseLeave(S32 x, S32 y, MASK mask)
{
LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu();
if ( gear_menu && gear_menu->getVisible() )
{
return;
}
if(childHasVisiblePopupMenu())
{
return;
}
mOpenTimer.unpause();
}
void LLInspectObject::onClickBuy()
{
handle_buy();
closeFloater();
}
void LLInspectObject::onClickPay()
{
handle_give_money_dialog();
closeFloater();
}
void LLInspectObject::onClickTakeFreeCopy()
{
LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection();
if (!selection) return;
LLSelectNode* nodep = selection->getFirstRootNode();
if (!nodep) return;
// Figure out if this is a "free buy" or a "take copy"
bool for_copy = anyone_copy_selection(nodep);
// Prefer to just take a free copy
if (for_copy)
{
handle_take_copy();
}
else
{
// Buy for free (confusing, but that's how it is)
handle_buy();
}
closeFloater();
}
void LLInspectObject::onClickTouch()
{
handle_object_touch();
closeFloater();
}
void LLInspectObject::onClickSit()
{
handle_object_sit_or_stand();
closeFloater();
}
void LLInspectObject::onClickOpen()
{
LLFloaterReg::showInstance("openobject");
closeFloater();
}
void LLInspectObject::onClickMoreInfo()
{
LLSD key;
key["task"] = "task";
LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
closeFloater();
}
void LLInspectObject::onClickZoomIn()
{
handle_look_at_selection("zoom");
closeFloater();
}
//////////////////////////////////////////////////////////////////////////////
// LLInspectObjectUtil
//////////////////////////////////////////////////////////////////////////////
void LLInspectObjectUtil::registerFloater()
{
LLFloaterReg::add("inspect_object", "inspect_object.xml",
&LLFloaterReg::build<LLInspectObject>);
}