PageRenderTime 51ms CodeModel.GetById 8ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

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