PageRenderTime 141ms CodeModel.GetById 41ms app.highlight 77ms RepoModel.GetById 13ms app.codeStats 0ms

/indra/newview/llinspectobject.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 661 lines | 480 code | 88 blank | 93 comment | 66 complexity | b1eed96b16fa066f4eadadb2d2dfe0e8 MD5 | raw file
  1/** 
  2 * @file llinspectobject.cpp
  3 *
  4 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  5 * Second Life Viewer Source Code
  6 * Copyright (C) 2010, Linden Research, Inc.
  7 * 
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation;
 11 * version 2.1 of the License only.
 12 * 
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * Lesser General Public License for more details.
 17 * 
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 21 * 
 22 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 23 * $/LicenseInfo$
 24 */
 25
 26#include "llviewerprecompiledheaders.h"
 27
 28#include "llinspectobject.h"
 29
 30// Viewer
 31#include "llfloatersidepanelcontainer.h"
 32#include "llinspect.h"
 33#include "llmediaentry.h"
 34#include "llnotificationsutil.h"	// *TODO: Eliminate, add LLNotificationsUtil wrapper
 35#include "llselectmgr.h"
 36#include "llslurl.h"
 37#include "llviewermenu.h"		// handle_object_touch(), handle_buy()
 38#include "llviewermedia.h"
 39#include "llviewermediafocus.h"
 40#include "llviewerobjectlist.h"	// to select the requested object
 41
 42// Linden libraries
 43#include "llbutton.h"			// setLabel(), not virtual!
 44#include "llclickaction.h"
 45#include "llfloaterreg.h"
 46#include "llmenubutton.h"
 47#include "llresmgr.h"			// getMonetaryString
 48#include "llsafehandle.h"
 49#include "lltextbox.h"			// for description truncation
 50#include "lltoggleablemenu.h"
 51#include "lltrans.h"
 52#include "llui.h"				// positionViewNearMouse()
 53#include "lluictrl.h"
 54
 55class LLViewerObject;
 56
 57//////////////////////////////////////////////////////////////////////////////
 58// LLInspectObject
 59//////////////////////////////////////////////////////////////////////////////
 60
 61// Object Inspector, a small information window used when clicking
 62// in the ambient inspector widget for objects in the 3D world.
 63class LLInspectObject : public LLInspect
 64{
 65	friend class LLFloaterReg;
 66	
 67public:
 68	// object_id - Root object ID for which to show information
 69	// Inspector will be positioned relative to current mouse position
 70	LLInspectObject(const LLSD& object_id);
 71	virtual ~LLInspectObject();
 72	
 73	/*virtual*/ BOOL postBuild(void);
 74	
 75	// Because floater is single instance, need to re-parse data on each spawn
 76	// (for example, inspector about same avatar but in different position)
 77	/*virtual*/ void onOpen(const LLSD& avatar_id);
 78	
 79	// Release the selection and do other cleanup
 80	/*virtual*/ void onClose(bool app_quitting);
 81	
 82	// override the inspector mouse leave so timer is only paused if 
 83	// gear menu is not open
 84	/* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask);
 85	
 86private:
 87	// Refresh displayed data with information from selection manager
 88	void update();
 89
 90	void hideButtons();
 91	void updateButtons(LLSelectNode* nodep);
 92	void updateSitLabel(LLSelectNode* nodep);
 93	void updateTouchLabel(LLSelectNode* nodep);
 94
 95	void updateName(LLSelectNode* nodep);
 96	void updateDescription(LLSelectNode* nodep);
 97	void updatePrice(LLSelectNode* nodep);
 98	void updateCreator(LLSelectNode* nodep);
 99	
100	void updateMediaCurrentURL();	
101	void updateSecureBrowsing();
102		
103	void onClickBuy();
104	void onClickPay();
105	void onClickTakeFreeCopy();
106	void onClickTouch();
107	void onClickSit();
108	void onClickOpen();
109	void onClickMoreInfo();
110	void onClickZoomIn();  
111	
112private:
113	LLUUID				mObjectID;
114	S32					mObjectFace;
115	viewer_media_t		mMediaImpl;
116	LLMediaEntry*       mMediaEntry;
117	LLSafeHandle<LLObjectSelection> mObjectSelection;
118};
119
120LLInspectObject::LLInspectObject(const LLSD& sd)
121:	LLInspect( LLSD() ),	// single_instance, doesn't really need key
122	mObjectID(NULL),			// set in onOpen()
123	mObjectFace(0),
124	mObjectSelection(NULL),
125	mMediaImpl(NULL),
126	mMediaEntry(NULL)
127{
128	// can't make the properties request until the widgets are constructed
129	// as it might return immediately, so do it in postBuild.
130	mCommitCallbackRegistrar.add("InspectObject.Buy",	boost::bind(&LLInspectObject::onClickBuy, this));	
131	mCommitCallbackRegistrar.add("InspectObject.Pay",	boost::bind(&LLInspectObject::onClickPay, this));	
132	mCommitCallbackRegistrar.add("InspectObject.TakeFreeCopy",	boost::bind(&LLInspectObject::onClickTakeFreeCopy, this));	
133	mCommitCallbackRegistrar.add("InspectObject.Touch",	boost::bind(&LLInspectObject::onClickTouch, this));	
134	mCommitCallbackRegistrar.add("InspectObject.Sit",	boost::bind(&LLInspectObject::onClickSit, this));	
135	mCommitCallbackRegistrar.add("InspectObject.Open",	boost::bind(&LLInspectObject::onClickOpen, this));	
136	mCommitCallbackRegistrar.add("InspectObject.MoreInfo",	boost::bind(&LLInspectObject::onClickMoreInfo, this));	
137	mCommitCallbackRegistrar.add("InspectObject.ZoomIn", boost::bind(&LLInspectObject::onClickZoomIn, this));
138}
139
140
141LLInspectObject::~LLInspectObject()
142{
143}
144
145/*virtual*/
146BOOL LLInspectObject::postBuild(void)
147{
148	// The XML file has sample data in it.  Clear that out so we don't
149	// flicker when data arrives off network.
150	getChild<LLUICtrl>("object_name")->setValue("");
151	getChild<LLUICtrl>("object_creator")->setValue("");
152	getChild<LLUICtrl>("object_description")->setValue("");
153	getChild<LLUICtrl>("object_media_url")->setValue("");
154	// Set buttons invisible until we know what this object can do
155	hideButtons();
156
157	// Hide floater when name links clicked
158	LLTextBox* textbox = getChild<LLTextBox>("object_creator");
159	textbox->setURLClickedCallback(boost::bind(&LLInspectObject::closeFloater, this, false) );
160
161	// Hook up functionality
162	getChild<LLUICtrl>("buy_btn")->setCommitCallback(
163		boost::bind(&LLInspectObject::onClickBuy, this));
164	getChild<LLUICtrl>("pay_btn")->setCommitCallback(
165		boost::bind(&LLInspectObject::onClickPay, this));
166	getChild<LLUICtrl>("take_free_copy_btn")->setCommitCallback(
167		boost::bind(&LLInspectObject::onClickTakeFreeCopy, this));
168	getChild<LLUICtrl>("touch_btn")->setCommitCallback(
169		boost::bind(&LLInspectObject::onClickTouch, this));
170	getChild<LLUICtrl>("sit_btn")->setCommitCallback(
171		boost::bind(&LLInspectObject::onClickSit, this));
172	getChild<LLUICtrl>("open_btn")->setCommitCallback(
173		boost::bind(&LLInspectObject::onClickOpen, this));
174	getChild<LLUICtrl>("more_info_btn")->setCommitCallback(
175		boost::bind(&LLInspectObject::onClickMoreInfo, this));
176
177	// Watch for updates to selection properties off the network
178	LLSelectMgr::getInstance()->mUpdateSignal.connect(
179		boost::bind(&LLInspectObject::update, this) );
180
181	return TRUE;
182}
183
184// Multiple calls to showInstance("inspect_avatar", foo) will provide different
185// LLSD for foo, which we will catch here.
186//virtual
187void LLInspectObject::onOpen(const LLSD& data)
188{
189	// Start animation
190	LLInspect::onOpen(data);
191
192	// Extract appropriate avatar id
193	mObjectID = data["object_id"];
194	
195	if(data.has("object_face"))
196	{
197		mObjectFace = data["object_face"];
198	}
199	// Position the inspector relative to the mouse cursor
200	// Similar to how tooltips are positioned
201	// See LLToolTipMgr::createToolTip
202	if (data.has("pos"))
203	{
204		LLUI::positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger());
205	}
206	else
207	{
208		LLUI::positionViewNearMouse(this);
209	}
210
211	// Promote hovered object to a complete selection, which will also force
212	// a request for selected object data off the network
213	LLViewerObject* obj = gObjectList.findObject( mObjectID );
214	if (obj)
215	{
216		// Media focus and this code fight over the select manager.  
217		// Make sure any media is unfocused before changing the selection here.
218		LLViewerMediaFocus::getInstance()->clearFocus();
219		
220		LLSelectMgr::instance().deselectAll();
221		mObjectSelection = LLSelectMgr::instance().selectObjectAndFamily(obj);
222
223		// Mark this as a transient selection
224		struct SetTransient : public LLSelectedNodeFunctor
225		{
226			bool apply(LLSelectNode* node)
227			{
228				node->setTransient(TRUE);
229				return true;
230			}
231		} functor;
232		mObjectSelection->applyToNodes(&functor);
233		
234		// Does this face have media?
235		const LLTextureEntry* tep = obj->getTE(mObjectFace);
236		if (!tep)
237			return;
238		
239		mMediaEntry = tep->hasMedia() ? tep->getMediaData() : NULL;
240		if(!mMediaEntry)
241			return;
242		
243		mMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mMediaEntry->getMediaID());
244	}
245}
246
247// virtual
248void LLInspectObject::onClose(bool app_quitting)
249{
250	// Release selection to deselect
251	mObjectSelection = NULL;
252
253	getChild<LLMenuButton>("gear_btn")->hideMenu();
254}
255
256
257void LLInspectObject::update()
258{
259	// Performance optimization, because we listen to updates from select mgr
260	// but we're never destroyed.
261	if (!getVisible()) return;
262
263	LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection();
264	if (!selection) return;
265
266	LLSelectNode* nodep = selection->getFirstRootNode();
267	if (!nodep) return;
268
269	updateButtons(nodep);
270	updateName(nodep);
271	updateDescription(nodep);
272	updateCreator(nodep);
273	updatePrice(nodep);
274	
275	LLViewerObject* obj = nodep->getObject();
276	if(!obj)
277		return;
278	
279	if ( mObjectFace < 0 
280		||  mObjectFace >= obj->getNumTEs() )
281	{
282		return;
283	}
284	
285	// Does this face have media?
286	const LLTextureEntry* tep = obj->getTE(mObjectFace);
287	if (!tep)
288		return;
289	
290	mMediaEntry = tep->hasMedia() ? tep->getMediaData() : NULL;
291	if(!mMediaEntry)
292		return;
293	
294	mMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mMediaEntry->getMediaID());
295	
296	updateMediaCurrentURL();
297	updateSecureBrowsing();
298}
299
300void LLInspectObject::hideButtons()
301{
302	getChild<LLUICtrl>("buy_btn")->setVisible(false);
303	getChild<LLUICtrl>("pay_btn")->setVisible(false);
304	getChild<LLUICtrl>("take_free_copy_btn")->setVisible(false);
305	getChild<LLUICtrl>("touch_btn")->setVisible(false);
306	getChild<LLUICtrl>("sit_btn")->setVisible(false);
307	getChild<LLUICtrl>("open_btn")->setVisible(false);
308}
309
310// *TODO: Extract this method from lltoolpie.cpp and put somewhere shared
311extern U8 final_click_action(LLViewerObject*);
312
313// Choose the "most relevant" operation for this object, and show a button for
314// that operation as the left-most button in the inspector.
315void LLInspectObject::updateButtons(LLSelectNode* nodep)
316{
317	// We'll start with everyone hidden and show the ones we need
318	hideButtons();
319	
320	LLViewerObject* object = nodep->getObject();
321	LLViewerObject *parent = (LLViewerObject*)object->getParent();
322	bool for_copy = anyone_copy_selection(nodep);
323	bool for_sale = enable_buy_object();
324	S32 price = nodep->mSaleInfo.getSalePrice();
325	U8 click_action = final_click_action(object);
326
327	if (for_copy
328		|| (for_sale && price == 0))
329	{
330		// Free copies have priority over other operations
331		getChild<LLUICtrl>("take_free_copy_btn")->setVisible(true);
332	}
333	else if (for_sale)
334	{
335		getChild<LLUICtrl>("buy_btn")->setVisible(true);
336	}
337	else if ( enable_pay_object() )
338	{
339		getChild<LLUICtrl>("pay_btn")->setVisible(true);
340	}
341	else if (click_action == CLICK_ACTION_SIT)
342	{
343		// Click-action sit must come before "open" because many objects on
344		// which you can sit have scripts, and hence can be opened
345		getChild<LLUICtrl>("sit_btn")->setVisible(true);
346		updateSitLabel(nodep);
347	}
348	else if (object->flagHandleTouch()
349		|| (parent && parent->flagHandleTouch()))
350	{
351		getChild<LLUICtrl>("touch_btn")->setVisible(true);
352		updateTouchLabel(nodep);
353	}
354	else if ( enable_object_open() )
355	{
356		// Open is last because anything with a script in it can be opened
357		getChild<LLUICtrl>("open_btn")->setVisible(true);
358	}
359	else
360	{
361		// By default, we can sit on anything
362		getChild<LLUICtrl>("sit_btn")->setVisible(true);
363		updateSitLabel(nodep);
364	}
365
366	// No flash
367	focusFirstItem(FALSE, FALSE);
368}
369
370void LLInspectObject::updateSitLabel(LLSelectNode* nodep)
371{
372	LLButton* sit_btn = getChild<LLButton>("sit_btn");
373	if (!nodep->mSitName.empty())
374	{
375		sit_btn->setLabel( nodep->mSitName );
376	}
377	else
378	{
379		sit_btn->setLabel( getString("Sit") );
380	}
381}
382
383void LLInspectObject::updateTouchLabel(LLSelectNode* nodep)
384{
385	LLButton* sit_btn = getChild<LLButton>("touch_btn");
386	if (!nodep->mTouchName.empty())
387	{
388		sit_btn->setLabel( nodep->mTouchName );
389	}
390	else
391	{
392		sit_btn->setLabel( getString("Touch") );
393	}
394}
395
396void LLInspectObject::updateName(LLSelectNode* nodep)
397{
398	std::string name;
399	if (!nodep->mName.empty())
400	{
401		name = nodep->mName;
402	}
403	else
404	{
405		name = LLTrans::getString("TooltipNoName");
406	}
407	getChild<LLUICtrl>("object_name")->setValue(name);
408}
409
410void LLInspectObject::updateDescription(LLSelectNode* nodep)
411{
412	const char* const DEFAULT_DESC = "(No Description)";
413	std::string desc;
414	if (!nodep->mDescription.empty()
415		&& nodep->mDescription != DEFAULT_DESC)
416	{
417		desc = nodep->mDescription;
418	}
419
420	LLTextBox* textbox = getChild<LLTextBox>("object_description");
421	textbox->setValue(desc);
422}
423
424void LLInspectObject::updateMediaCurrentURL()
425{	
426	if(!mMediaEntry)
427		return;
428	LLTextBox* textbox = getChild<LLTextBox>("object_media_url");
429	std::string media_url = "";
430	textbox->setValue(media_url);
431	textbox->setToolTip(media_url);
432	LLStringUtil::format_map_t args;
433	
434	if(mMediaImpl.notNull() && mMediaImpl->hasMedia())
435	{
436		
437		LLPluginClassMedia* media_plugin = NULL;
438		media_plugin = mMediaImpl->getMediaPlugin();
439		if(media_plugin)
440		{
441			if(media_plugin->pluginSupportsMediaTime())
442			{
443				args["[CurrentURL]"] =  mMediaImpl->getMediaURL();
444			}
445			else
446			{
447				args["[CurrentURL]"] =  media_plugin->getLocation();
448			}
449			media_url = LLTrans::getString("CurrentURL", args);
450
451		}
452	}
453	else if(mMediaEntry->getCurrentURL() != "")
454	{
455		args["[CurrentURL]"] = mMediaEntry->getCurrentURL();
456		media_url = LLTrans::getString("CurrentURL", args);
457	}
458
459	textbox->setText(media_url);
460	textbox->setToolTip(media_url);
461}
462
463void LLInspectObject::updateCreator(LLSelectNode* nodep)
464{
465	// final information for display
466	LLStringUtil::format_map_t args;
467	std::string text;
468	
469	// Leave text blank until data loaded
470	if (nodep->mValid)
471	{
472		// Utilize automatic translation of SLURL into name to display 
473		// a clickable link		
474		// Objects cannot be created by a group, so use agent URL format
475		LLUUID creator_id = nodep->mPermissions->getCreator();
476		std::string creator_url =
477			LLSLURL("agent", creator_id, "about").getSLURLString();
478		args["[CREATOR]"] = creator_url;
479				
480		// created by one user but owned by another
481		std::string owner_url;
482		LLUUID owner_id;
483		bool group_owned = nodep->mPermissions->isGroupOwned();
484		if (group_owned)
485		{
486			owner_id = nodep->mPermissions->getGroup();
487			owner_url =	LLSLURL("group", owner_id, "about").getSLURLString();
488		}
489		else
490		{
491			owner_id = nodep->mPermissions->getOwner();
492			owner_url =	LLSLURL("agent", owner_id, "about").getSLURLString();
493		}
494		args["[OWNER]"] = owner_url;
495		
496		if (creator_id == owner_id)
497		{
498			// common case, created and owned by one user
499			text = getString("Creator", args);
500		}
501		else
502		{
503			text = getString("CreatorAndOwner", args);
504		}
505	}
506	getChild<LLUICtrl>("object_creator")->setValue(text);
507}
508
509void LLInspectObject::updatePrice(LLSelectNode* nodep)
510{
511	// *TODO: Only look these up once and use for both updateButtons and here
512	bool for_copy = anyone_copy_selection(nodep);
513	bool for_sale = enable_buy_object();
514	S32 price = nodep->mSaleInfo.getSalePrice();
515	
516	bool show_price_icon = false;
517	std::string line;
518	if (for_copy
519		|| (for_sale && price == 0))
520	{
521		line = getString("PriceFree");
522		show_price_icon = true;
523	}
524	else if (for_sale)
525	{
526		LLStringUtil::format_map_t args;
527		args["[AMOUNT]"] = LLResMgr::getInstance()->getMonetaryString(price);
528		line = getString("Price", args);
529		show_price_icon = true;
530	}
531	getChild<LLUICtrl>("price_text")->setValue(line);
532	getChild<LLUICtrl>("price_icon")->setVisible(show_price_icon);
533}
534
535void LLInspectObject::updateSecureBrowsing()
536{
537	bool is_secure_browsing = false;
538	
539	if(mMediaImpl.notNull() 
540	   && mMediaImpl->hasMedia())
541	{
542		LLPluginClassMedia* media_plugin = NULL;
543		std::string current_url = "";
544		media_plugin = mMediaImpl->getMediaPlugin();
545		if(media_plugin)
546		{
547			if(media_plugin->pluginSupportsMediaTime())
548			{
549				current_url = mMediaImpl->getMediaURL();
550			}
551			else
552			{
553				current_url =  media_plugin->getLocation();
554			}
555		}
556		
557		std::string prefix =  std::string("https://");
558		std::string test_prefix = current_url.substr(0, prefix.length());
559		LLStringUtil::toLower(test_prefix);	
560		if(test_prefix == prefix)
561		{
562			is_secure_browsing = true;
563		}
564	}
565	getChild<LLUICtrl>("secure_browsing")->setVisible(is_secure_browsing);
566}
567
568// For the object inspector, only unpause the fade timer 
569// if the gear menu is not open
570void LLInspectObject::onMouseLeave(S32 x, S32 y, MASK mask)
571{
572	LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu();
573	if ( gear_menu && gear_menu->getVisible() )
574	{
575		return;
576	}
577
578	if(childHasVisiblePopupMenu())
579	{
580		return;
581	}
582
583	mOpenTimer.unpause();
584}
585
586void LLInspectObject::onClickBuy()
587{
588	handle_buy();
589	closeFloater();
590}
591
592void LLInspectObject::onClickPay()
593{
594	handle_give_money_dialog();
595	closeFloater();
596}
597
598void LLInspectObject::onClickTakeFreeCopy()
599{
600	LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection();
601	if (!selection) return;
602
603	LLSelectNode* nodep = selection->getFirstRootNode();
604	if (!nodep) return;
605
606	// Figure out if this is a "free buy" or a "take copy"
607	bool for_copy = anyone_copy_selection(nodep);
608	// Prefer to just take a free copy
609	if (for_copy)
610	{
611		handle_take_copy();
612	}
613	else
614	{
615		// Buy for free (confusing, but that's how it is)
616		handle_buy();
617	}
618	closeFloater();
619}
620
621void LLInspectObject::onClickTouch()
622{
623	handle_object_touch();
624	closeFloater();
625}
626
627void LLInspectObject::onClickSit()
628{
629	handle_object_sit_or_stand();
630	closeFloater();
631}
632
633void LLInspectObject::onClickOpen()
634{
635	LLFloaterReg::showInstance("openobject");
636	closeFloater();
637}
638
639void LLInspectObject::onClickMoreInfo()
640{
641	LLSD key;
642	key["task"] = "task";
643	LLFloaterSidePanelContainer::showPanel("inventory", key);
644	closeFloater();
645}
646
647void LLInspectObject::onClickZoomIn() 
648{
649	handle_look_at_selection("zoom");
650	closeFloater();
651}
652
653//////////////////////////////////////////////////////////////////////////////
654// LLInspectObjectUtil
655//////////////////////////////////////////////////////////////////////////////
656void LLInspectObjectUtil::registerFloater()
657{
658	LLFloaterReg::add("inspect_object", "inspect_object.xml",
659					  &LLFloaterReg::build<LLInspectObject>);
660}
661