/indra/newview/llfloaterpay.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 559 lines · 394 code · 93 blank · 72 comment · 37 complexity · 9b55173baaa16c3f7e7af2954048cb7f MD5 · raw file

  1. /**
  2. * @file llfloaterpay.cpp
  3. * @author Aaron Brashears, Kelly Washington, James Cook
  4. * @brief Implementation of the LLFloaterPay class.
  5. *
  6. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "llviewerprecompiledheaders.h"
  28. #include "llfloaterpay.h"
  29. #include "message.h"
  30. #include "llfloater.h"
  31. #include "lllslconstants.h" // MAX_PAY_BUTTONS
  32. #include "lluuid.h"
  33. #include "llagent.h"
  34. #include "llfloaterreg.h"
  35. #include "llresmgr.h"
  36. #include "lltextbox.h"
  37. #include "lllineeditor.h"
  38. #include "llmutelist.h"
  39. #include "llfloaterreporter.h"
  40. #include "llslurl.h"
  41. #include "llviewerobject.h"
  42. #include "llviewerobjectlist.h"
  43. #include "llviewerregion.h"
  44. #include "llviewerwindow.h"
  45. #include "llbutton.h"
  46. #include "llselectmgr.h"
  47. #include "lltransactiontypes.h"
  48. #include "lluictrlfactory.h"
  49. ///----------------------------------------------------------------------------
  50. /// Local function declarations, constants, enums, and typedefs
  51. ///----------------------------------------------------------------------------
  52. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  53. // Class LLGiveMoneyInfo
  54. //
  55. // A small class used to track callback information
  56. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  57. class LLFloaterPay;
  58. struct LLGiveMoneyInfo
  59. {
  60. LLFloaterPay* mFloater;
  61. S32 mAmount;
  62. LLGiveMoneyInfo(LLFloaterPay* floater, S32 amount) :
  63. mFloater(floater), mAmount(amount){}
  64. };
  65. ///----------------------------------------------------------------------------
  66. /// Class LLFloaterPay
  67. ///----------------------------------------------------------------------------
  68. class LLFloaterPay : public LLFloater
  69. {
  70. public:
  71. LLFloaterPay(const LLSD& key);
  72. virtual ~LLFloaterPay();
  73. /*virtual*/ BOOL postBuild();
  74. /*virtual*/ void onClose(bool app_quitting);
  75. void setCallback(money_callback callback) { mCallback = callback; }
  76. static void payViaObject(money_callback callback, LLSafeHandle<LLObjectSelection> selection);
  77. static void payDirectly(money_callback callback,
  78. const LLUUID& target_id,
  79. bool is_group);
  80. private:
  81. static void onCancel(void* data);
  82. static void onKeystroke(LLLineEditor* editor, void* data);
  83. static void onGive(void* data);
  84. void give(S32 amount);
  85. static void processPayPriceReply(LLMessageSystem* msg, void **userdata);
  86. void finishPayUI(const LLUUID& target_id, BOOL is_group);
  87. protected:
  88. std::vector<LLGiveMoneyInfo*> mCallbackData;
  89. money_callback mCallback;
  90. LLTextBox* mObjectNameText;
  91. LLUUID mTargetUUID;
  92. BOOL mTargetIsGroup;
  93. BOOL mHaveName;
  94. LLButton* mQuickPayButton[MAX_PAY_BUTTONS];
  95. LLGiveMoneyInfo* mQuickPayInfo[MAX_PAY_BUTTONS];
  96. LLSafeHandle<LLObjectSelection> mObjectSelection;
  97. static S32 sLastAmount;
  98. };
  99. S32 LLFloaterPay::sLastAmount = 0;
  100. const S32 MAX_AMOUNT_LENGTH = 10;
  101. const S32 FASTPAY_BUTTON_WIDTH = 80;
  102. LLFloaterPay::LLFloaterPay(const LLSD& key)
  103. : LLFloater(key),
  104. mCallbackData(),
  105. mCallback(NULL),
  106. mObjectNameText(NULL),
  107. mTargetUUID(key.asUUID()),
  108. mTargetIsGroup(FALSE),
  109. mHaveName(FALSE)
  110. {
  111. }
  112. // Destroys the object
  113. LLFloaterPay::~LLFloaterPay()
  114. {
  115. std::for_each(mCallbackData.begin(), mCallbackData.end(), DeletePointer());
  116. // Name callbacks will be automatically disconnected since LLFloater is trackable
  117. // In case this floater is currently waiting for a reply.
  118. gMessageSystem->setHandlerFuncFast(_PREHASH_PayPriceReply, 0, 0);
  119. }
  120. BOOL LLFloaterPay::postBuild()
  121. {
  122. S32 i = 0;
  123. LLGiveMoneyInfo* info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_0);
  124. mCallbackData.push_back(info);
  125. childSetAction("fastpay 1",&LLFloaterPay::onGive,info);
  126. getChildView("fastpay 1")->setVisible(FALSE);
  127. mQuickPayButton[i] = getChild<LLButton>("fastpay 1");
  128. mQuickPayInfo[i] = info;
  129. ++i;
  130. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_1);
  131. mCallbackData.push_back(info);
  132. childSetAction("fastpay 5",&LLFloaterPay::onGive,info);
  133. getChildView("fastpay 5")->setVisible(FALSE);
  134. mQuickPayButton[i] = getChild<LLButton>("fastpay 5");
  135. mQuickPayInfo[i] = info;
  136. ++i;
  137. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_2);
  138. mCallbackData.push_back(info);
  139. childSetAction("fastpay 10",&LLFloaterPay::onGive,info);
  140. getChildView("fastpay 10")->setVisible(FALSE);
  141. mQuickPayButton[i] = getChild<LLButton>("fastpay 10");
  142. mQuickPayInfo[i] = info;
  143. ++i;
  144. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_3);
  145. mCallbackData.push_back(info);
  146. childSetAction("fastpay 20",&LLFloaterPay::onGive,info);
  147. getChildView("fastpay 20")->setVisible(FALSE);
  148. mQuickPayButton[i] = getChild<LLButton>("fastpay 20");
  149. mQuickPayInfo[i] = info;
  150. ++i;
  151. getChildView("amount text")->setVisible(FALSE);
  152. std::string last_amount;
  153. if(sLastAmount > 0)
  154. {
  155. last_amount = llformat("%d", sLastAmount);
  156. }
  157. getChildView("amount")->setVisible(FALSE);
  158. getChild<LLLineEditor>("amount")->setKeystrokeCallback(&LLFloaterPay::onKeystroke, this);
  159. getChild<LLUICtrl>("amount")->setValue(last_amount);
  160. getChild<LLLineEditor>("amount")->setPrevalidate(LLTextValidate::validateNonNegativeS32);
  161. info = new LLGiveMoneyInfo(this, 0);
  162. mCallbackData.push_back(info);
  163. childSetAction("pay btn",&LLFloaterPay::onGive,info);
  164. setDefaultBtn("pay btn");
  165. getChildView("pay btn")->setVisible(FALSE);
  166. getChildView("pay btn")->setEnabled((sLastAmount > 0));
  167. childSetAction("cancel btn",&LLFloaterPay::onCancel,this);
  168. return TRUE;
  169. }
  170. // virtual
  171. void LLFloaterPay::onClose(bool app_quitting)
  172. {
  173. // Deselect the objects
  174. mObjectSelection = NULL;
  175. }
  176. // static
  177. void LLFloaterPay::processPayPriceReply(LLMessageSystem* msg, void **userdata)
  178. {
  179. LLFloaterPay* self = (LLFloaterPay*)userdata;
  180. if (self)
  181. {
  182. S32 price;
  183. LLUUID target;
  184. msg->getUUIDFast(_PREHASH_ObjectData,_PREHASH_ObjectID,target);
  185. if (target != self->mTargetUUID)
  186. {
  187. // This is a message for a different object's pay info
  188. return;
  189. }
  190. msg->getS32Fast(_PREHASH_ObjectData,_PREHASH_DefaultPayPrice,price);
  191. if (PAY_PRICE_HIDE == price)
  192. {
  193. self->getChildView("amount")->setVisible(FALSE);
  194. self->getChildView("pay btn")->setVisible(FALSE);
  195. self->getChildView("amount text")->setVisible(FALSE);
  196. }
  197. else if (PAY_PRICE_DEFAULT == price)
  198. {
  199. self->getChildView("amount")->setVisible(TRUE);
  200. self->getChildView("pay btn")->setVisible(TRUE);
  201. self->getChildView("amount text")->setVisible(TRUE);
  202. }
  203. else
  204. {
  205. // PAY_PRICE_HIDE and PAY_PRICE_DEFAULT are negative values
  206. // So we take the absolute value here after we have checked for those cases
  207. self->getChildView("amount")->setVisible(TRUE);
  208. self->getChildView("pay btn")->setVisible(TRUE);
  209. self->getChildView("pay btn")->setEnabled(TRUE);
  210. self->getChildView("amount text")->setVisible(TRUE);
  211. self->getChild<LLUICtrl>("amount")->setValue(llformat("%d", llabs(price)));
  212. }
  213. S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_ButtonData);
  214. S32 i = 0;
  215. if (num_blocks > MAX_PAY_BUTTONS) num_blocks = MAX_PAY_BUTTONS;
  216. S32 max_pay_amount = 0;
  217. S32 padding_required = 0;
  218. for (i=0;i<num_blocks;++i)
  219. {
  220. S32 pay_button;
  221. msg->getS32Fast(_PREHASH_ButtonData,_PREHASH_PayButton,pay_button,i);
  222. if (pay_button > 0)
  223. {
  224. std::string button_str = "L$";
  225. button_str += LLResMgr::getInstance()->getMonetaryString( pay_button );
  226. self->mQuickPayButton[i]->setLabelSelected(button_str);
  227. self->mQuickPayButton[i]->setLabelUnselected(button_str);
  228. self->mQuickPayButton[i]->setVisible(TRUE);
  229. self->mQuickPayInfo[i]->mAmount = pay_button;
  230. self->getChildView("fastpay text")->setVisible(TRUE);
  231. if ( pay_button > max_pay_amount )
  232. {
  233. max_pay_amount = pay_button;
  234. }
  235. }
  236. else
  237. {
  238. self->mQuickPayButton[i]->setVisible(FALSE);
  239. }
  240. }
  241. // build a string containing the maximum value and calc nerw button width from it.
  242. std::string balance_str = "L$";
  243. balance_str += LLResMgr::getInstance()->getMonetaryString( max_pay_amount );
  244. const LLFontGL* font = LLFontGL::getFontSansSerif();
  245. S32 new_button_width = font->getWidth( std::string(balance_str));
  246. new_button_width += ( 12 + 12 ); // padding
  247. // dialong is sized for 2 digit pay amounts - larger pay values need to be scaled
  248. const S32 threshold = 100000;
  249. if ( max_pay_amount >= threshold )
  250. {
  251. S32 num_digits_threshold = (S32)log10((double)threshold) + 1;
  252. S32 num_digits_max = (S32)log10((double)max_pay_amount) + 1;
  253. // calculate the extra width required by 2 buttons with max amount and some commas
  254. padding_required = ( num_digits_max - num_digits_threshold + ( num_digits_max / 3 ) ) * font->getWidth( std::string("0") );
  255. };
  256. // change in button width
  257. S32 button_delta = new_button_width - FASTPAY_BUTTON_WIDTH;
  258. if ( button_delta < 0 )
  259. button_delta = 0;
  260. // now we know the maximum amount, we can resize all the buttons to be
  261. for (i=0;i<num_blocks;++i)
  262. {
  263. LLRect r;
  264. r = self->mQuickPayButton[i]->getRect();
  265. // RHS button colum needs to move further because LHS changed too
  266. if ( i % 2 )
  267. {
  268. r.setCenterAndSize( r.getCenterX() + ( button_delta * 3 ) / 2 ,
  269. r.getCenterY(),
  270. r.getWidth() + button_delta,
  271. r.getHeight() );
  272. }
  273. else
  274. {
  275. r.setCenterAndSize( r.getCenterX() + button_delta / 2,
  276. r.getCenterY(),
  277. r.getWidth() + button_delta,
  278. r.getHeight() );
  279. }
  280. self->mQuickPayButton[i]->setRect( r );
  281. }
  282. for (i=num_blocks;i<MAX_PAY_BUTTONS;++i)
  283. {
  284. self->mQuickPayButton[i]->setVisible(FALSE);
  285. }
  286. self->reshape( self->getRect().getWidth() + padding_required, self->getRect().getHeight(), FALSE );
  287. }
  288. msg->setHandlerFunc("PayPriceReply",NULL,NULL);
  289. }
  290. // static
  291. void LLFloaterPay::payViaObject(money_callback callback, LLSafeHandle<LLObjectSelection> selection)
  292. {
  293. // Object that lead to the selection, may be child
  294. LLViewerObject* object = selection->getPrimaryObject();
  295. if (!object)
  296. return;
  297. LLFloaterPay *floater = LLFloaterReg::showTypedInstance<LLFloaterPay>("pay_object", LLSD(object->getID()));
  298. if (!floater)
  299. return;
  300. floater->setCallback(callback);
  301. // Hold onto the selection until we close
  302. floater->mObjectSelection = selection;
  303. LLSelectNode* node = selection->getFirstRootNode();
  304. if (!node)
  305. {
  306. //FIXME: notify user object no longer exists
  307. floater->closeFloater();
  308. return;
  309. }
  310. LLHost target_region = object->getRegion()->getHost();
  311. LLMessageSystem* msg = gMessageSystem;
  312. msg->newMessageFast(_PREHASH_RequestPayPrice);
  313. msg->nextBlockFast(_PREHASH_ObjectData);
  314. msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
  315. msg->sendReliable(target_region);
  316. msg->setHandlerFuncFast(_PREHASH_PayPriceReply, processPayPriceReply,(void **)floater);
  317. LLUUID owner_id;
  318. BOOL is_group = FALSE;
  319. node->mPermissions->getOwnership(owner_id, is_group);
  320. floater->getChild<LLUICtrl>("object_name_text")->setValue(node->mName);
  321. floater->finishPayUI(owner_id, is_group);
  322. }
  323. void LLFloaterPay::payDirectly(money_callback callback,
  324. const LLUUID& target_id,
  325. bool is_group)
  326. {
  327. LLFloaterPay *floater = LLFloaterReg::showTypedInstance<LLFloaterPay>("pay_resident", LLSD(target_id));
  328. if (!floater)
  329. return;
  330. floater->setCallback(callback);
  331. floater->mObjectSelection = NULL;
  332. floater->getChildView("amount")->setVisible(TRUE);
  333. floater->getChildView("pay btn")->setVisible(TRUE);
  334. floater->getChildView("amount text")->setVisible(TRUE);
  335. floater->getChildView("fastpay text")->setVisible(TRUE);
  336. for(S32 i=0;i<MAX_PAY_BUTTONS;++i)
  337. {
  338. floater->mQuickPayButton[i]->setVisible(TRUE);
  339. }
  340. floater->finishPayUI(target_id, is_group);
  341. }
  342. void LLFloaterPay::finishPayUI(const LLUUID& target_id, BOOL is_group)
  343. {
  344. std::string slurl;
  345. if (is_group)
  346. {
  347. setTitle(getString("payee_group"));
  348. slurl = LLSLURL("group", target_id, "inspect").getSLURLString();
  349. }
  350. else
  351. {
  352. setTitle(getString("payee_resident"));
  353. slurl = LLSLURL("agent", target_id, "inspect").getSLURLString();
  354. }
  355. getChild<LLTextBox>("payee_name")->setText(slurl);
  356. // Make sure the amount field has focus
  357. LLLineEditor* amount = getChild<LLLineEditor>("amount");
  358. amount->setFocus(TRUE);
  359. amount->selectAll();
  360. mTargetIsGroup = is_group;
  361. }
  362. // static
  363. void LLFloaterPay::onCancel(void* data)
  364. {
  365. LLFloaterPay* self = reinterpret_cast<LLFloaterPay*>(data);
  366. if(self)
  367. {
  368. self->closeFloater();
  369. }
  370. }
  371. // static
  372. void LLFloaterPay::onKeystroke(LLLineEditor*, void* data)
  373. {
  374. LLFloaterPay* self = reinterpret_cast<LLFloaterPay*>(data);
  375. if(self)
  376. {
  377. // enable the Pay button when amount is non-empty and positive, disable otherwise
  378. std::string amtstr = self->getChild<LLUICtrl>("amount")->getValue().asString();
  379. self->getChildView("pay btn")->setEnabled(!amtstr.empty() && atoi(amtstr.c_str()) > 0);
  380. }
  381. }
  382. // static
  383. void LLFloaterPay::onGive(void* data)
  384. {
  385. LLGiveMoneyInfo* info = reinterpret_cast<LLGiveMoneyInfo*>(data);
  386. if(info && info->mFloater)
  387. {
  388. info->mFloater->give(info->mAmount);
  389. info->mFloater->closeFloater();
  390. }
  391. }
  392. void LLFloaterPay::give(S32 amount)
  393. {
  394. if(mCallback)
  395. {
  396. // if the amount is 0, that menas that we should use the
  397. // text field.
  398. if(amount == 0)
  399. {
  400. amount = atoi(getChild<LLUICtrl>("amount")->getValue().asString().c_str());
  401. }
  402. sLastAmount = amount;
  403. // Try to pay an object.
  404. if (mObjectSelection.notNull())
  405. {
  406. LLViewerObject* dest_object = gObjectList.findObject(mTargetUUID);
  407. if(dest_object)
  408. {
  409. LLViewerRegion* region = dest_object->getRegion();
  410. if (region)
  411. {
  412. // Find the name of the root object
  413. LLSelectNode* node = mObjectSelection->getFirstRootNode();
  414. std::string object_name;
  415. if (node)
  416. {
  417. object_name = node->mName;
  418. }
  419. S32 tx_type = TRANS_PAY_OBJECT;
  420. if(dest_object->isAvatar()) tx_type = TRANS_GIFT;
  421. mCallback(mTargetUUID, region, amount, FALSE, tx_type, object_name);
  422. mObjectSelection = NULL;
  423. // request the object owner in order to check if the owner needs to be unmuted
  424. LLMessageSystem* msg = gMessageSystem;
  425. msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
  426. msg->nextBlockFast(_PREHASH_AgentData);
  427. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  428. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  429. msg->nextBlockFast(_PREHASH_ObjectData);
  430. msg->addU32Fast(_PREHASH_RequestFlags, OBJECT_PAY_REQUEST );
  431. msg->addUUIDFast(_PREHASH_ObjectID, mTargetUUID);
  432. msg->sendReliable( region->getHost() );
  433. }
  434. }
  435. }
  436. else
  437. {
  438. // just transfer the L$
  439. mCallback(mTargetUUID, gAgent.getRegion(), amount, mTargetIsGroup, TRANS_GIFT, LLStringUtil::null);
  440. // check if the payee needs to be unmuted
  441. LLMuteList::getInstance()->autoRemove(mTargetUUID, LLMuteList::AR_MONEY);
  442. }
  443. }
  444. }
  445. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  446. // Namespace LLFloaterPayUtil
  447. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  448. void LLFloaterPayUtil::registerFloater()
  449. {
  450. // Sneaky, use same code but different XML for dialogs
  451. LLFloaterReg::add("pay_resident", "floater_pay.xml",
  452. &LLFloaterReg::build<LLFloaterPay>);
  453. LLFloaterReg::add("pay_object", "floater_pay_object.xml",
  454. &LLFloaterReg::build<LLFloaterPay>);
  455. }
  456. void LLFloaterPayUtil::payViaObject(money_callback callback,
  457. LLSafeHandle<LLObjectSelection> selection)
  458. {
  459. LLFloaterPay::payViaObject(callback, selection);
  460. }
  461. void LLFloaterPayUtil::payDirectly(money_callback callback,
  462. const LLUUID& target_id,
  463. bool is_group)
  464. {
  465. LLFloaterPay::payDirectly(callback, target_id, is_group);
  466. }