llfloaterpay.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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=viewergpl$
  7. *
  8. * Copyright (c) 2002-2009, Linden Research, Inc.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include "llfloaterpay.h"
  35. #include "llbutton.h"
  36. #include "llcachename.h"
  37. #include "lllineeditor.h"
  38. #include "lllocale.h"
  39. #include "llnotifications.h"
  40. #include "lltextbox.h"
  41. #include "lltransactiontypes.h"
  42. #include "lluictrlfactory.h"
  43. #include "llmessage.h"
  44. #include "llagent.h"
  45. #include "llfloaterreporter.h" // For OBJECT_PAY_REQUEST
  46. #include "llmutelist.h"
  47. #include "llselectmgr.h"
  48. #include "llviewerobjectlist.h"
  49. #include "llviewerregion.h"
  50. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  51. // Class LLGiveMoneyInfo
  52. //
  53. // A small class used to track callback information
  54. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  55. struct LLGiveMoneyInfo
  56. {
  57. LLFloaterPay* mFloater;
  58. S32 mAmount;
  59. LLGiveMoneyInfo(LLFloaterPay* floater, S32 amount)
  60. : mFloater(floater),
  61. mAmount(amount)
  62. {
  63. }
  64. };
  65. ///----------------------------------------------------------------------------
  66. /// Class LLFloaterPay
  67. ///----------------------------------------------------------------------------
  68. std::set<LLFloaterPay*> LLFloaterPay::sInstances;
  69. S32 LLFloaterPay::sLastAmount = 0;
  70. constexpr S32 FASTPAY_BUTTON_WIDTH = 80;
  71. //static
  72. void LLFloaterPay::payViaObject(money_callback callback,
  73. const LLUUID& object_id)
  74. {
  75. LLViewerObject* object = gObjectList.findObject(object_id);
  76. if (!object || !object->getRegion()) return;
  77. LLFloaterPay* floater = new LLFloaterPay("Give L$", callback, object_id,
  78. true);
  79. if (!floater) return;
  80. LLSelectNode* node = NULL;
  81. if (floater->mObjectSelection.notNull())
  82. {
  83. node = floater->mObjectSelection->getFirstRootNode();
  84. }
  85. if (!node)
  86. {
  87. gNotifications.add("PayObjectFailed");
  88. floater->close();
  89. return;
  90. }
  91. LLHost target_region = object->getRegion()->getHost();
  92. LLMessageSystem* msg = gMessageSystemp;
  93. msg->newMessageFast(_PREHASH_RequestPayPrice);
  94. msg->nextBlockFast(_PREHASH_ObjectData);
  95. msg->addUUIDFast(_PREHASH_ObjectID, object_id);
  96. msg->sendReliable(target_region);
  97. msg->setHandlerFuncFast(_PREHASH_PayPriceReply, processPayPriceReply,
  98. (void**)floater);
  99. LLUUID owner_id;
  100. bool is_group = false;
  101. node->mPermissions->getOwnership(owner_id, is_group);
  102. floater->childSetText("object_name_text", node->mName);
  103. floater->finishPayUI(owner_id, is_group);
  104. }
  105. //static
  106. void LLFloaterPay::payDirectly(money_callback callback,
  107. const LLUUID& target_id, bool is_group)
  108. {
  109. LLFloaterPay* floater = new LLFloaterPay("Give L$", callback, target_id,
  110. false);
  111. if (!floater) return;
  112. floater->childSetVisible("amount", true);
  113. floater->childSetVisible("pay btn", true);
  114. floater->childSetVisible("amount text", true);
  115. for (S32 i = 0; i < MAX_PAY_BUTTONS; ++i)
  116. {
  117. floater->mQuickPayButton[i]->setVisible(true);
  118. }
  119. floater->finishPayUI(target_id, is_group);
  120. }
  121. LLFloaterPay::LLFloaterPay(const std::string& name, money_callback callback,
  122. const LLUUID& uuid, bool target_is_object)
  123. : LLFloater(name, "FloaterPayRectB", LLStringUtil::null, RESIZE_NO,
  124. DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, DRAG_ON_TOP, MINIMIZE_NO,
  125. CLOSE_YES),
  126. mCallbackData(),
  127. mCallback(callback),
  128. mObjectNameText(NULL),
  129. mPayMessageText(NULL),
  130. mTargetUUID(uuid),
  131. mTargetIsObject(target_is_object),
  132. mTargetIsGroup(false)
  133. {
  134. if (target_is_object)
  135. {
  136. LLUICtrlFactory::getInstance()->buildFloater(this,
  137. "floater_pay_object.xml");
  138. mObjectSelection = gSelectMgr.getEditSelection();
  139. }
  140. else
  141. {
  142. LLUICtrlFactory::getInstance()->buildFloater(this, "floater_pay.xml");
  143. }
  144. sInstances.insert(this);
  145. S32 i = 0;
  146. LLGiveMoneyInfo* info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_0);
  147. mCallbackData.push_back(info);
  148. childSetAction("fastpay 1", onGive, info);
  149. childSetVisible("fastpay 1", false);
  150. mQuickPayButton[i] = getChild<LLButton>("fastpay 1");
  151. mQuickPayInfo[i++] = info;
  152. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_1);
  153. mCallbackData.push_back(info);
  154. childSetAction("fastpay 5", onGive, info);
  155. childSetVisible("fastpay 5", false);
  156. mQuickPayButton[i] = getChild<LLButton>("fastpay 5");
  157. mQuickPayInfo[i++] = info;
  158. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_2);
  159. mCallbackData.push_back(info);
  160. childSetAction("fastpay 10", onGive, info);
  161. childSetVisible("fastpay 10", false);
  162. mQuickPayButton[i] = getChild<LLButton>("fastpay 10");
  163. mQuickPayInfo[i++] = info;
  164. info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_3);
  165. mCallbackData.push_back(info);
  166. childSetAction("fastpay 20", onGive, info);
  167. childSetVisible("fastpay 20", false);
  168. mQuickPayButton[i] = getChild<LLButton>("fastpay 20");
  169. mQuickPayInfo[i++] = info;
  170. childSetVisible("amount text", false);
  171. std::string last_amount;
  172. if (sLastAmount > 0)
  173. {
  174. last_amount = llformat("%d", sLastAmount);
  175. }
  176. mPayMessageText = getChild<LLLineEditor>("payment_message", true, false);
  177. if (mPayMessageText)
  178. {
  179. mPayMessageText->setPrevalidate(LLLineEditor::prevalidateASCII);
  180. }
  181. childSetVisible("amount", false);
  182. childSetKeystrokeCallback("amount", onKeystroke, this);
  183. childSetText("amount", last_amount);
  184. childSetPrevalidate("amount", LLLineEditor::prevalidateNonNegativeS32);
  185. info = new LLGiveMoneyInfo(this, 0);
  186. mCallbackData.push_back(info);
  187. childSetAction("pay btn", onGive, info);
  188. setDefaultBtn("pay btn");
  189. childSetVisible("pay btn", false);
  190. childSetEnabled("pay btn", sLastAmount > 0);
  191. childSetAction("cancel btn", onCancel, this);
  192. center();
  193. open();
  194. }
  195. LLFloaterPay::~LLFloaterPay()
  196. {
  197. sInstances.erase(this);
  198. gMessageSystemp->setHandlerFunc("PayPriceReply", NULL, NULL);
  199. std::for_each(mCallbackData.begin(), mCallbackData.end(), DeletePointer());
  200. mCallbackData.clear();
  201. }
  202. void LLFloaterPay::finishPayUI(const LLUUID& target_id, bool is_group)
  203. {
  204. if (!gCacheNamep) return; // Paranoia
  205. gCacheNamep->get(target_id, is_group,
  206. boost::bind(&LLFloaterPay::onCacheOwnerName,
  207. _1, _2, _3, this));
  208. // Make sure the amount field has focus
  209. childSetFocus("amount", true);
  210. LLLineEditor* amount = getChild<LLLineEditor>("amount");
  211. amount->selectAll();
  212. mTargetIsGroup = is_group;
  213. }
  214. bool LLFloaterPay::give(S32 amount)
  215. {
  216. if (mCallback)
  217. {
  218. // if the amount is 0, that means that we should use the text field.
  219. if (amount == 0)
  220. {
  221. amount = atoi(childGetText("amount").c_str());
  222. }
  223. sLastAmount = amount;
  224. // Try to pay an object.
  225. if (mTargetIsObject)
  226. {
  227. LLViewerObject* dest_object = gObjectList.findObject(mTargetUUID);
  228. LLViewerRegion* region;
  229. if (dest_object && (region = dest_object->getRegion()))
  230. {
  231. std::string object_name;
  232. if (mObjectSelection.notNull())
  233. {
  234. // Find the name of the root object
  235. LLSelectNode* node = mObjectSelection->getFirstRootNode();
  236. if (node)
  237. {
  238. object_name = node->mName;
  239. }
  240. else
  241. {
  242. return false;
  243. }
  244. }
  245. S32 tx_type = TRANS_PAY_OBJECT;
  246. if (dest_object->isAvatar())
  247. {
  248. tx_type = TRANS_GIFT;
  249. }
  250. mCallback(mTargetUUID, region, amount, false, tx_type,
  251. object_name);
  252. mObjectSelection = NULL;
  253. // request the object owner in order to check if the owner
  254. // needs to be unmuted
  255. LLSelectMgr::registerObjectPropertiesFamilyRequest(mTargetUUID);
  256. LLMessageSystem* msg = gMessageSystemp;
  257. msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
  258. msg->nextBlockFast(_PREHASH_AgentData);
  259. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  260. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  261. msg->nextBlockFast(_PREHASH_ObjectData);
  262. msg->addU32Fast(_PREHASH_RequestFlags, OBJECT_PAY_REQUEST);
  263. msg->addUUIDFast(_PREHASH_ObjectID, mTargetUUID);
  264. msg->sendReliable(region->getHost());
  265. }
  266. else
  267. {
  268. return false;
  269. }
  270. }
  271. else
  272. {
  273. // just transfer the L$
  274. std::string message;
  275. if (mPayMessageText)
  276. {
  277. message = mPayMessageText->getValue().asString();
  278. }
  279. mCallback(mTargetUUID, gAgent.getRegion(), amount, mTargetIsGroup,
  280. TRANS_GIFT, message);
  281. // check if the payee needs to be unmuted
  282. LLMuteList::autoRemove(mTargetUUID, LLMuteList::AR_MONEY);
  283. }
  284. }
  285. return true;
  286. }
  287. //static
  288. void LLFloaterPay::processPayPriceReply(LLMessageSystem* msg, void** userdata)
  289. {
  290. LLFloaterPay* self = (LLFloaterPay*)userdata;
  291. if (self && sInstances.count(self))
  292. {
  293. S32 price;
  294. LLUUID target;
  295. msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, target);
  296. if (target != self->mTargetUUID)
  297. {
  298. // This is a message for a different object's pay info
  299. return;
  300. }
  301. msg->getS32Fast(_PREHASH_ObjectData, _PREHASH_DefaultPayPrice, price);
  302. if (price == PAY_PRICE_HIDE)
  303. {
  304. self->childSetVisible("amount", false);
  305. self->childSetVisible("pay btn", false);
  306. self->childSetVisible("amount text", false);
  307. }
  308. else if (price == PAY_PRICE_DEFAULT)
  309. {
  310. self->childSetVisible("amount", true);
  311. self->childSetVisible("pay btn", true);
  312. self->childSetVisible("amount text", true);
  313. }
  314. else
  315. {
  316. // PAY_PRICE_HIDE and PAY_PRICE_DEFAULT are negative values. So we
  317. // take the absolute value here after we have checked for those
  318. // cases.
  319. self->childSetVisible("amount", true);
  320. self->childSetVisible("pay btn", true);
  321. self->childSetEnabled("pay btn", true);
  322. self->childSetVisible("amount text", true);
  323. self->childSetText("amount", llformat("%d", abs(price)));
  324. }
  325. S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_ButtonData);
  326. S32 i = 0;
  327. if (num_blocks > MAX_PAY_BUTTONS) num_blocks = MAX_PAY_BUTTONS;
  328. S32 max_pay_amount = 0;
  329. S32 padding_required = 0;
  330. for (i = 0; i < num_blocks; ++i)
  331. {
  332. S32 pay_button;
  333. msg->getS32Fast(_PREHASH_ButtonData, _PREHASH_PayButton,
  334. pay_button, i);
  335. if (pay_button > 0)
  336. {
  337. std::string button_str = "L$";
  338. button_str += LLLocale::getMonetaryString(pay_button);
  339. self->mQuickPayButton[i]->setLabelSelected(button_str);
  340. self->mQuickPayButton[i]->setLabelUnselected(button_str);
  341. self->mQuickPayButton[i]->setVisible(true);
  342. self->mQuickPayInfo[i]->mAmount = pay_button;
  343. if (pay_button > max_pay_amount)
  344. {
  345. max_pay_amount = pay_button;
  346. }
  347. }
  348. else
  349. {
  350. self->mQuickPayButton[i]->setVisible(false);
  351. }
  352. }
  353. // build a string containing the maximum value and calc nerw button
  354. // width from it.
  355. std::string balance_str = "L$";
  356. balance_str += LLLocale::getMonetaryString(max_pay_amount);
  357. static const LLFontGL* font = LLFontGL::getFontSansSerif();
  358. S32 new_button_width = font->getWidth(std::string(balance_str));
  359. new_button_width += 24; // padding
  360. // dialog is sized for 2 digit pay amounts - larger pay values need to
  361. // be scaled
  362. constexpr S32 threshold = 100000;
  363. if (max_pay_amount >= threshold)
  364. {
  365. S32 num_digits_threshold = (S32)log10f((F32)threshold) + 1;
  366. S32 num_digits_max = (S32)log10f((F32)max_pay_amount) + 1;
  367. // Calculate the extra width required by 2 buttons with max amount
  368. // and some commas
  369. padding_required = font->getWidth(std::string("0")) *
  370. (num_digits_max - num_digits_threshold +
  371. num_digits_max / 3);
  372. }
  373. // Change in button width
  374. S32 button_delta = new_button_width - FASTPAY_BUTTON_WIDTH;
  375. if (button_delta < 0)
  376. {
  377. button_delta = 0;
  378. }
  379. // now we know the maximum amount, we can resize all the buttons to be
  380. for (i = 0; i < num_blocks; ++i)
  381. {
  382. LLRect r;
  383. r = self->mQuickPayButton[i]->getRect();
  384. // RHS button colum needs to move further because LHS changed too
  385. if (i % 2)
  386. {
  387. r.setCenterAndSize(r.getCenterX() + (button_delta * 3) / 2 ,
  388. r.getCenterY(), r.getWidth() + button_delta,
  389. r.getHeight());
  390. }
  391. else
  392. {
  393. r.setCenterAndSize(r.getCenterX() + button_delta / 2,
  394. r.getCenterY(), r.getWidth() + button_delta,
  395. r.getHeight());
  396. }
  397. self->mQuickPayButton[i]->setRect(r);
  398. }
  399. for (i = num_blocks; i < MAX_PAY_BUTTONS; ++i)
  400. {
  401. self->mQuickPayButton[i]->setVisible(false);
  402. }
  403. self->reshape(self->getRect().getWidth() + padding_required,
  404. self->getRect().getHeight(), false);
  405. }
  406. msg->setHandlerFunc("PayPriceReply", NULL, NULL);
  407. }
  408. //static
  409. void LLFloaterPay::onCacheOwnerName(const LLUUID& owner_id,
  410. const std::string& full_name,
  411. bool is_group,
  412. LLFloaterPay* self)
  413. {
  414. if (!self || !sInstances.count(self))
  415. {
  416. return;
  417. }
  418. if (self->mTargetIsObject)
  419. {
  420. if (is_group)
  421. {
  422. self->childSetVisible("payee_group", true);
  423. self->childSetVisible("payee_resident", false);
  424. }
  425. else
  426. {
  427. self->childSetVisible("payee_group", false);
  428. self->childSetVisible("payee_resident", true);
  429. }
  430. }
  431. self->childSetTextArg("payeename", "[NAME]", full_name);
  432. }
  433. //static
  434. void LLFloaterPay::onCancel(void* data)
  435. {
  436. LLFloaterPay* self = (LLFloaterPay*)data;
  437. if (self)
  438. {
  439. self->close();
  440. }
  441. }
  442. //static
  443. void LLFloaterPay::onKeystroke(LLLineEditor*, void* data)
  444. {
  445. LLFloaterPay* self = (LLFloaterPay*)data;
  446. if (self)
  447. {
  448. // enable the Pay button when amount is non-empty and positive, disable
  449. // otherwise
  450. std::string amtstr = self->childGetText("amount");
  451. self->childSetEnabled("pay btn",
  452. !amtstr.empty() && atoi(amtstr.c_str()) > 0);
  453. }
  454. }
  455. //static
  456. void LLFloaterPay::onGive(void* data)
  457. {
  458. LLGiveMoneyInfo* info = (LLGiveMoneyInfo*)data;
  459. if (info && info->mFloater)
  460. {
  461. if (!info->mFloater->give(info->mAmount))
  462. {
  463. gNotifications.add("PayObjectFailed");
  464. }
  465. info->mFloater->close();
  466. }
  467. }