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