123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /**
- * @file llfloaterpay.cpp
- * @author Aaron Brashears, Kelly Washington, James Cook
- * @brief Implementation of the LLFloaterPay class.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llfloaterpay.h"
- #include "llbutton.h"
- #include "llcachename.h"
- #include "lllineeditor.h"
- #include "lllocale.h"
- #include "llnotifications.h"
- #include "lltextbox.h"
- #include "lltransactiontypes.h"
- #include "lluictrlfactory.h"
- #include "llmessage.h"
- #include "llagent.h"
- #include "llfloaterreporter.h" // For OBJECT_PAY_REQUEST
- #include "llmutelist.h"
- #include "llselectmgr.h"
- #include "llviewerobjectlist.h"
- #include "llviewerregion.h"
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Class LLGiveMoneyInfo
- //
- // A small class used to track callback information
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- struct LLGiveMoneyInfo
- {
- LLFloaterPay* mFloater;
- S32 mAmount;
- LLGiveMoneyInfo(LLFloaterPay* floater, S32 amount)
- : mFloater(floater),
- mAmount(amount)
- {
- }
- };
- ///----------------------------------------------------------------------------
- /// Class LLFloaterPay
- ///----------------------------------------------------------------------------
- std::set<LLFloaterPay*> LLFloaterPay::sInstances;
- S32 LLFloaterPay::sLastAmount = 0;
- constexpr S32 FASTPAY_BUTTON_WIDTH = 80;
- //static
- void LLFloaterPay::payViaObject(money_callback callback,
- const LLUUID& object_id)
- {
- LLViewerObject* object = gObjectList.findObject(object_id);
- if (!object || !object->getRegion()) return;
- LLFloaterPay* floater = new LLFloaterPay("Give L$", callback, object_id,
- true);
- if (!floater) return;
- LLSelectNode* node = NULL;
- if (floater->mObjectSelection.notNull())
- {
- node = floater->mObjectSelection->getFirstRootNode();
- }
- if (!node)
- {
- gNotifications.add("PayObjectFailed");
- floater->close();
- return;
- }
- LLHost target_region = object->getRegion()->getHost();
- LLMessageSystem* msg = gMessageSystemp;
- msg->newMessageFast(_PREHASH_RequestPayPrice);
- msg->nextBlockFast(_PREHASH_ObjectData);
- msg->addUUIDFast(_PREHASH_ObjectID, object_id);
- msg->sendReliable(target_region);
- msg->setHandlerFuncFast(_PREHASH_PayPriceReply, processPayPriceReply,
- (void**)floater);
- LLUUID owner_id;
- bool is_group = false;
- node->mPermissions->getOwnership(owner_id, is_group);
- floater->childSetText("object_name_text", node->mName);
- floater->finishPayUI(owner_id, is_group);
- }
- //static
- void LLFloaterPay::payDirectly(money_callback callback,
- const LLUUID& target_id, bool is_group)
- {
- LLFloaterPay* floater = new LLFloaterPay("Give L$", callback, target_id,
- false);
- if (!floater) return;
- floater->childSetVisible("amount", true);
- floater->childSetVisible("pay btn", true);
- floater->childSetVisible("amount text", true);
- for (S32 i = 0; i < MAX_PAY_BUTTONS; ++i)
- {
- floater->mQuickPayButton[i]->setVisible(true);
- }
- floater->finishPayUI(target_id, is_group);
- }
- LLFloaterPay::LLFloaterPay(const std::string& name, money_callback callback,
- const LLUUID& uuid, bool target_is_object)
- : LLFloater(name, "FloaterPayRectB", LLStringUtil::null, RESIZE_NO,
- DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, DRAG_ON_TOP, MINIMIZE_NO,
- CLOSE_YES),
- mCallbackData(),
- mCallback(callback),
- mObjectNameText(NULL),
- mPayMessageText(NULL),
- mTargetUUID(uuid),
- mTargetIsObject(target_is_object),
- mTargetIsGroup(false)
- {
- if (target_is_object)
- {
- LLUICtrlFactory::getInstance()->buildFloater(this,
- "floater_pay_object.xml");
- mObjectSelection = gSelectMgr.getEditSelection();
- }
- else
- {
- LLUICtrlFactory::getInstance()->buildFloater(this, "floater_pay.xml");
- }
- sInstances.insert(this);
- S32 i = 0;
- LLGiveMoneyInfo* info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_0);
- mCallbackData.push_back(info);
- childSetAction("fastpay 1", onGive, info);
- childSetVisible("fastpay 1", false);
- mQuickPayButton[i] = getChild<LLButton>("fastpay 1");
- mQuickPayInfo[i++] = info;
- info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_1);
- mCallbackData.push_back(info);
- childSetAction("fastpay 5", onGive, info);
- childSetVisible("fastpay 5", false);
- mQuickPayButton[i] = getChild<LLButton>("fastpay 5");
- mQuickPayInfo[i++] = info;
- info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_2);
- mCallbackData.push_back(info);
- childSetAction("fastpay 10", onGive, info);
- childSetVisible("fastpay 10", false);
- mQuickPayButton[i] = getChild<LLButton>("fastpay 10");
- mQuickPayInfo[i++] = info;
- info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_3);
- mCallbackData.push_back(info);
- childSetAction("fastpay 20", onGive, info);
- childSetVisible("fastpay 20", false);
- mQuickPayButton[i] = getChild<LLButton>("fastpay 20");
- mQuickPayInfo[i++] = info;
- childSetVisible("amount text", false);
- std::string last_amount;
- if (sLastAmount > 0)
- {
- last_amount = llformat("%d", sLastAmount);
- }
- mPayMessageText = getChild<LLLineEditor>("payment_message", true, false);
- if (mPayMessageText)
- {
- mPayMessageText->setPrevalidate(LLLineEditor::prevalidateASCII);
- }
- childSetVisible("amount", false);
- childSetKeystrokeCallback("amount", onKeystroke, this);
- childSetText("amount", last_amount);
- childSetPrevalidate("amount", LLLineEditor::prevalidateNonNegativeS32);
- info = new LLGiveMoneyInfo(this, 0);
- mCallbackData.push_back(info);
- childSetAction("pay btn", onGive, info);
- setDefaultBtn("pay btn");
- childSetVisible("pay btn", false);
- childSetEnabled("pay btn", sLastAmount > 0);
- childSetAction("cancel btn", onCancel, this);
- center();
- open();
- }
- LLFloaterPay::~LLFloaterPay()
- {
- sInstances.erase(this);
- gMessageSystemp->setHandlerFunc("PayPriceReply", NULL, NULL);
- std::for_each(mCallbackData.begin(), mCallbackData.end(), DeletePointer());
- mCallbackData.clear();
- }
- void LLFloaterPay::finishPayUI(const LLUUID& target_id, bool is_group)
- {
- if (!gCacheNamep) return; // Paranoia
- gCacheNamep->get(target_id, is_group,
- boost::bind(&LLFloaterPay::onCacheOwnerName,
- _1, _2, _3, this));
- // Make sure the amount field has focus
- childSetFocus("amount", true);
- LLLineEditor* amount = getChild<LLLineEditor>("amount");
- amount->selectAll();
- mTargetIsGroup = is_group;
- }
- bool LLFloaterPay::give(S32 amount)
- {
- if (mCallback)
- {
- // if the amount is 0, that means that we should use the text field.
- if (amount == 0)
- {
- amount = atoi(childGetText("amount").c_str());
- }
- sLastAmount = amount;
- // Try to pay an object.
- if (mTargetIsObject)
- {
- LLViewerObject* dest_object = gObjectList.findObject(mTargetUUID);
- LLViewerRegion* region;
- if (dest_object && (region = dest_object->getRegion()))
- {
- std::string object_name;
- if (mObjectSelection.notNull())
- {
- // Find the name of the root object
- LLSelectNode* node = mObjectSelection->getFirstRootNode();
- if (node)
- {
- object_name = node->mName;
- }
- else
- {
- return false;
- }
- }
- S32 tx_type = TRANS_PAY_OBJECT;
- if (dest_object->isAvatar())
- {
- tx_type = TRANS_GIFT;
- }
- mCallback(mTargetUUID, region, amount, false, tx_type,
- object_name);
- mObjectSelection = NULL;
- // request the object owner in order to check if the owner
- // needs to be unmuted
- LLSelectMgr::registerObjectPropertiesFamilyRequest(mTargetUUID);
- LLMessageSystem* msg = gMessageSystemp;
- msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
- msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
- msg->nextBlockFast(_PREHASH_ObjectData);
- msg->addU32Fast(_PREHASH_RequestFlags, OBJECT_PAY_REQUEST);
- msg->addUUIDFast(_PREHASH_ObjectID, mTargetUUID);
- msg->sendReliable(region->getHost());
- }
- else
- {
- return false;
- }
- }
- else
- {
- // just transfer the L$
- std::string message;
- if (mPayMessageText)
- {
- message = mPayMessageText->getValue().asString();
- }
- mCallback(mTargetUUID, gAgent.getRegion(), amount, mTargetIsGroup,
- TRANS_GIFT, message);
- // check if the payee needs to be unmuted
- LLMuteList::autoRemove(mTargetUUID, LLMuteList::AR_MONEY);
- }
- }
- return true;
- }
- //static
- void LLFloaterPay::processPayPriceReply(LLMessageSystem* msg, void** userdata)
- {
- LLFloaterPay* self = (LLFloaterPay*)userdata;
- if (self && sInstances.count(self))
- {
- S32 price;
- LLUUID target;
- msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, target);
- if (target != self->mTargetUUID)
- {
- // This is a message for a different object's pay info
- return;
- }
- msg->getS32Fast(_PREHASH_ObjectData, _PREHASH_DefaultPayPrice, price);
- if (price == PAY_PRICE_HIDE)
- {
- self->childSetVisible("amount", false);
- self->childSetVisible("pay btn", false);
- self->childSetVisible("amount text", false);
- }
- else if (price == PAY_PRICE_DEFAULT)
- {
- self->childSetVisible("amount", true);
- self->childSetVisible("pay btn", true);
- self->childSetVisible("amount text", true);
- }
- else
- {
- // PAY_PRICE_HIDE and PAY_PRICE_DEFAULT are negative values. So we
- // take the absolute value here after we have checked for those
- // cases.
- self->childSetVisible("amount", true);
- self->childSetVisible("pay btn", true);
- self->childSetEnabled("pay btn", true);
- self->childSetVisible("amount text", true);
- self->childSetText("amount", llformat("%d", abs(price)));
- }
- S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_ButtonData);
- S32 i = 0;
- if (num_blocks > MAX_PAY_BUTTONS) num_blocks = MAX_PAY_BUTTONS;
- S32 max_pay_amount = 0;
- S32 padding_required = 0;
- for (i = 0; i < num_blocks; ++i)
- {
- S32 pay_button;
- msg->getS32Fast(_PREHASH_ButtonData, _PREHASH_PayButton,
- pay_button, i);
- if (pay_button > 0)
- {
- std::string button_str = "L$";
- button_str += LLLocale::getMonetaryString(pay_button);
- self->mQuickPayButton[i]->setLabelSelected(button_str);
- self->mQuickPayButton[i]->setLabelUnselected(button_str);
- self->mQuickPayButton[i]->setVisible(true);
- self->mQuickPayInfo[i]->mAmount = pay_button;
- if (pay_button > max_pay_amount)
- {
- max_pay_amount = pay_button;
- }
- }
- else
- {
- self->mQuickPayButton[i]->setVisible(false);
- }
- }
- // build a string containing the maximum value and calc nerw button
- // width from it.
- std::string balance_str = "L$";
- balance_str += LLLocale::getMonetaryString(max_pay_amount);
- static const LLFontGL* font = LLFontGL::getFontSansSerif();
- S32 new_button_width = font->getWidth(std::string(balance_str));
- new_button_width += 24; // padding
- // dialog is sized for 2 digit pay amounts - larger pay values need to
- // be scaled
- constexpr S32 threshold = 100000;
- if (max_pay_amount >= threshold)
- {
- S32 num_digits_threshold = (S32)log10f((F32)threshold) + 1;
- S32 num_digits_max = (S32)log10f((F32)max_pay_amount) + 1;
- // Calculate the extra width required by 2 buttons with max amount
- // and some commas
- padding_required = font->getWidth(std::string("0")) *
- (num_digits_max - num_digits_threshold +
- num_digits_max / 3);
- }
- // Change in button width
- S32 button_delta = new_button_width - FASTPAY_BUTTON_WIDTH;
- if (button_delta < 0)
- {
- button_delta = 0;
- }
- // now we know the maximum amount, we can resize all the buttons to be
- for (i = 0; i < num_blocks; ++i)
- {
- LLRect r;
- r = self->mQuickPayButton[i]->getRect();
- // RHS button colum needs to move further because LHS changed too
- if (i % 2)
- {
- r.setCenterAndSize(r.getCenterX() + (button_delta * 3) / 2 ,
- r.getCenterY(), r.getWidth() + button_delta,
- r.getHeight());
- }
- else
- {
- r.setCenterAndSize(r.getCenterX() + button_delta / 2,
- r.getCenterY(), r.getWidth() + button_delta,
- r.getHeight());
- }
- self->mQuickPayButton[i]->setRect(r);
- }
- for (i = num_blocks; i < MAX_PAY_BUTTONS; ++i)
- {
- self->mQuickPayButton[i]->setVisible(false);
- }
- self->reshape(self->getRect().getWidth() + padding_required,
- self->getRect().getHeight(), false);
- }
- msg->setHandlerFunc("PayPriceReply", NULL, NULL);
- }
- //static
- void LLFloaterPay::onCacheOwnerName(const LLUUID& owner_id,
- const std::string& full_name,
- bool is_group,
- LLFloaterPay* self)
- {
- if (!self || !sInstances.count(self))
- {
- return;
- }
- if (self->mTargetIsObject)
- {
- if (is_group)
- {
- self->childSetVisible("payee_group", true);
- self->childSetVisible("payee_resident", false);
- }
- else
- {
- self->childSetVisible("payee_group", false);
- self->childSetVisible("payee_resident", true);
- }
- }
- self->childSetTextArg("payeename", "[NAME]", full_name);
- }
- //static
- void LLFloaterPay::onCancel(void* data)
- {
- LLFloaterPay* self = (LLFloaterPay*)data;
- if (self)
- {
- self->close();
- }
- }
- //static
- void LLFloaterPay::onKeystroke(LLLineEditor*, void* data)
- {
- LLFloaterPay* self = (LLFloaterPay*)data;
- if (self)
- {
- // enable the Pay button when amount is non-empty and positive, disable
- // otherwise
- std::string amtstr = self->childGetText("amount");
- self->childSetEnabled("pay btn",
- !amtstr.empty() && atoi(amtstr.c_str()) > 0);
- }
- }
- //static
- void LLFloaterPay::onGive(void* data)
- {
- LLGiveMoneyInfo* info = (LLGiveMoneyInfo*)data;
- if (info && info->mFloater)
- {
- if (!info->mFloater->give(info->mAmount))
- {
- gNotifications.add("PayObjectFailed");
- }
- info->mFloater->close();
- }
- }
|