phoenix-firestorm/indra/newview/llcurrencyuimanager.cpp

629 lines
16 KiB
C++

/**
* @file llcurrencyuimanager.cpp
* @brief LLCurrencyUIManager class implementation
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lluictrlfactory.h"
#include "lltextbox.h"
#include "lllineeditor.h"
#include "llresmgr.h" // for LLLocale
#include "lltrans.h"
#include "llviewercontrol.h"
#include "llversioninfo.h"
#include "llcurrencyuimanager.h"
// viewer includes
#include "llagent.h"
#include "llconfirmationmanager.h"
#include "llframetimer.h"
#include "lllineeditor.h"
#include "llviewchildren.h"
#include "llxmlrpctransaction.h"
#include "llviewernetwork.h"
#include "llpanel.h"
#include "stringize.h"
const F64 CURRENCY_ESTIMATE_FREQUENCY = 2.0;
// how long of a pause in typing a currency buy amount before an
// esimate is fetched from the server
class LLCurrencyUIManager::Impl
{
public:
Impl(LLPanel& dialog);
virtual ~Impl();
LLPanel& mPanel;
bool mHidden;
bool mError;
std::string mErrorMessage;
std::string mErrorURI;
std::string mZeroMessage;
// user's choices
S32 mUserCurrencyBuy;
bool mUserEnteredCurrencyBuy;
// from website
// pre-viewer 2.0, the server returned estimates as an
// integer US cents value, e.g., "1000" for $10.00
// post-viewer 2.0, the server may also return estimates
// as a string with currency embedded, e.g., "10.00 Euros"
bool mUSDCurrencyEstimated;
S32 mUSDCurrencyEstimatedCost;
bool mLocalCurrencyEstimated;
std::string mLocalCurrencyEstimatedCost;
bool mSupportsInternationalBilling;
std::string mSiteConfirm;
bool mBought;
enum TransactionType
{
TransactionNone,
TransactionCurrency,
TransactionBuy
};
TransactionType mTransactionType;
LLXMLRPCTransaction* mTransaction;
bool mCurrencyChanged;
LLFrameTimer mCurrencyKeyTimer;
void updateCurrencyInfo();
void finishCurrencyInfo();
void startCurrencyBuy(const std::string& password);
void finishCurrencyBuy();
void clearEstimate();
bool hasEstimate() const;
std::string getLocalEstimate() const;
void startTransaction(TransactionType type, const char* method, const LLSD& params);
// return true if update needed
bool checkTransaction();
void setError(const std::string& message, const std::string& uri);
void clearError();
// return true if update needed
bool considerUpdateCurrency();
void currencyKey(S32);
static void onCurrencyKey(LLLineEditor* caller, void* data);
void prepare();
void updateUI();
};
// is potentially not fully constructed.
LLCurrencyUIManager::Impl::Impl(LLPanel& dialog)
: mPanel(dialog),
mHidden(false),
mError(false),
mUserCurrencyBuy(2000), // note, this is a default, real value set in llfloaterbuycurrency.cpp
mUserEnteredCurrencyBuy(false),
mSupportsInternationalBilling(false),
mBought(false),
mTransactionType(TransactionNone), mTransaction(0),
mCurrencyChanged(false)
{
clearEstimate();
}
LLCurrencyUIManager::Impl::~Impl()
{
delete mTransaction;
}
void LLCurrencyUIManager::Impl::updateCurrencyInfo()
{
clearEstimate();
mBought = false;
mCurrencyChanged = false;
if (mUserCurrencyBuy == 0)
{
mLocalCurrencyEstimated = true;
return;
}
const LLVersionInfo& vi(LLVersionInfo::instance());
LLSD params = LLSD::emptyMap();
params["agentId"] = gAgent.getID().asString();
params["secureSessionId"] = gAgent.getSecureSessionID().asString();
params["language"] = LLUI::getLanguage();
params["currencyBuy"] = mUserCurrencyBuy;
params["viewerChannel"] = vi.getChannel();
params["viewerMajorVersion"] = vi.getMajor();
params["viewerMinorVersion"] = vi.getMinor();
params["viewerPatchVersion"] = vi.getPatch();
// With GitHub builds, the build number is too big to fit in a 32-bit int,
// and XMLRPC value doesn't deal with integers wider than int. Use string.
params["viewerBuildVersion"] = std::to_string(vi.getBuild());
startTransaction(TransactionCurrency, "getCurrencyQuote", params);
}
void LLCurrencyUIManager::Impl::finishCurrencyInfo()
{
const LLSD& result = mTransaction->response();
bool success = result["success"].asBoolean();
if (!success)
{
setError(
result["errorMessage"].asString(),
result["errorURI"].asString()
);
return;
}
const LLSD& currency = result["currency"];
// old XML-RPC server: estimatedCost = value in US cents
mUSDCurrencyEstimated = currency.has("estimatedCost");
if (mUSDCurrencyEstimated)
{
mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInteger();
}
// newer XML-RPC server: estimatedLocalCost = local currency string
mLocalCurrencyEstimated = currency.has("estimatedLocalCost");
if (mLocalCurrencyEstimated)
{
mLocalCurrencyEstimatedCost = currency["estimatedLocalCost"].asString();
mSupportsInternationalBilling = true;
}
S32 newCurrencyBuy = currency["currencyBuy"].asInteger();
if (newCurrencyBuy != mUserCurrencyBuy)
{
mUserCurrencyBuy = newCurrencyBuy;
mUserEnteredCurrencyBuy = false;
}
mSiteConfirm = result["confirm"].asString();
}
void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
{
const LLVersionInfo& vi(LLVersionInfo::instance());
LLSD params = LLSD::emptyMap();
params["agentId"] = gAgent.getID().asString();
params["secureSessionId"] = gAgent.getSecureSessionID().asString();
params["language"] = LLUI::getLanguage();
params["currencyBuy"] = mUserCurrencyBuy;
params["confirm"] = mSiteConfirm;
params["viewerChannel"] = vi.getChannel();
params["viewerMajorVersion"] = vi.getMajor();
params["viewerMinorVersion"] = vi.getMinor();
params["viewerPatchVersion"] = vi.getPatch();
// With GitHub builds, the build number is too big to fit in a 32-bit int,
// and XMLRPC value doesn't deal with integers wider than int. Use string.
params["viewerBuildVersion"] = std::to_string(vi.getBuild());
if (mUSDCurrencyEstimated)
{
params["estimatedCost"] = mUSDCurrencyEstimatedCost;
}
if (mLocalCurrencyEstimated)
{
params["estimatedLocalCost"] = mLocalCurrencyEstimatedCost;
}
if (!password.empty())
{
params["password"] = password;
}
startTransaction(TransactionBuy, "buyCurrency", params);
clearEstimate();
mCurrencyChanged = false;
}
void LLCurrencyUIManager::Impl::finishCurrencyBuy()
{
const LLSD& result = mTransaction->response();
bool success = result["success"].asBoolean();
if (!success)
{
setError(
result["errorMessage"].asString(),
result["errorURI"].asString()
);
}
else
{
mUserCurrencyBuy = 0;
mUserEnteredCurrencyBuy = false;
mBought = true;
}
}
void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
const char* method, const LLSD& params)
{
static std::string transactionURI;
if (transactionURI.empty())
{
transactionURI = LLGridManager::getInstance()->getHelperURI() + "currency.php";
}
delete mTransaction;
mTransactionType = type;
mTransaction = new LLXMLRPCTransaction(transactionURI, method, params);
clearError();
}
void LLCurrencyUIManager::Impl::clearEstimate()
{
mUSDCurrencyEstimated = false;
mUSDCurrencyEstimatedCost = 0;
mLocalCurrencyEstimated = false;
mLocalCurrencyEstimatedCost = "0";
}
bool LLCurrencyUIManager::Impl::hasEstimate() const
{
return (mUSDCurrencyEstimated || mLocalCurrencyEstimated);
}
std::string LLCurrencyUIManager::Impl::getLocalEstimate() const
{
if (mLocalCurrencyEstimated)
{
// we have the new-style local currency string
return mLocalCurrencyEstimatedCost;
}
if (mUSDCurrencyEstimated)
{
// we have the old-style USD-specific value
LLStringUtil::format_map_t args;
{
LLLocale locale_override(LLStringUtil::getLocale());
args["[AMOUNT]"] = llformat("%#.2f", mUSDCurrencyEstimatedCost / 100.0);
}
return LLTrans::getString("LocalEstimateUSD", args);
}
return "";
}
bool LLCurrencyUIManager::Impl::checkTransaction()
{
if (!mTransaction)
{
return false;
}
if (!mTransaction->process())
{
return false;
}
if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
{
setError(mTransaction->statusMessage(), mTransaction->statusURI());
}
else
{
switch (mTransactionType)
{
case TransactionCurrency:
finishCurrencyInfo();
break;
case TransactionBuy:
finishCurrencyBuy();
break;
default:;
}
}
delete mTransaction;
mTransaction = NULL;
mTransactionType = TransactionNone;
return true;
}
void LLCurrencyUIManager::Impl::setError(
const std::string& message, const std::string& uri)
{
mError = true;
mErrorMessage = message;
mErrorURI = uri;
}
void LLCurrencyUIManager::Impl::clearError()
{
mError = false;
mErrorMessage.clear();
mErrorURI.clear();
}
bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
{
if (mCurrencyChanged && !mTransaction &&
mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
{
updateCurrencyInfo();
return true;
}
return false;
}
void LLCurrencyUIManager::Impl::currencyKey(S32 value)
{
mUserEnteredCurrencyBuy = true;
mCurrencyKeyTimer.reset();
if (mUserCurrencyBuy == value)
{
return;
}
mUserCurrencyBuy = value;
if (hasEstimate())
{
clearEstimate();
//cannot just simply refresh the whole UI, as the edit field will
// get reset and the cursor will change...
mPanel.getChildView("currency_est")->setVisible(false);
mPanel.getChildView("getting_data")->setVisible(true);
}
mCurrencyChanged = true;
}
// static
void LLCurrencyUIManager::Impl::onCurrencyKey(LLLineEditor* caller, void* data)
{
S32 value = atoi(caller->getText().c_str());
LLCurrencyUIManager::Impl* self = (LLCurrencyUIManager::Impl*)data;
self->currencyKey(value);
}
void LLCurrencyUIManager::Impl::prepare()
{
LLLineEditor* lindenAmount = mPanel.getChild<LLLineEditor>("currency_amt");
if (lindenAmount)
{
lindenAmount->setPrevalidate(LLTextValidate::validateNonNegativeS32);
lindenAmount->setKeystrokeCallback(onCurrencyKey, this);
}
}
void LLCurrencyUIManager::Impl::updateUI()
{
if (mHidden)
{
mPanel.getChildView("currency_action")->setVisible(false);
mPanel.getChildView("currency_amt")->setVisible(false);
mPanel.getChildView("currency_est")->setVisible(false);
return;
}
mPanel.getChildView("currency_action")->setVisible(true);
LLLineEditor* lindenAmount = mPanel.getChild<LLLineEditor>("currency_amt");
if (lindenAmount)
{
lindenAmount->setVisible(true);
lindenAmount->setLabel(mZeroMessage);
if (!mUserEnteredCurrencyBuy)
{
if (mUserCurrencyBuy == 0)
{
lindenAmount->setText(LLStringUtil::null);
}
else
{
lindenAmount->setText(llformat("%d", mUserCurrencyBuy));
}
lindenAmount->selectAll();
}
}
std::string estimated = (mUserCurrencyBuy == 0) ? mPanel.getString("estimated_zero") : getLocalEstimate();
mPanel.getChild<LLUICtrl>("currency_est")->setTextArg("[LOCALAMOUNT]", estimated);
mPanel.getChildView("currency_est")->setVisible( hasEstimate() || mUserCurrencyBuy == 0);
mPanel.getChildView("currency_links")->setVisible( mSupportsInternationalBilling);
mPanel.getChildView("exchange_rate_note")->setVisible( mSupportsInternationalBilling);
if (mPanel.getChildView("buy_btn")->getEnabled()
||mPanel.getChildView("currency_est")->getVisible()
|| mPanel.getChildView("error_web")->getVisible())
{
mPanel.getChildView("getting_data")->setVisible(false);
}
}
LLCurrencyUIManager::LLCurrencyUIManager(LLPanel& dialog)
: impl(* new Impl(dialog))
{
}
LLCurrencyUIManager::~LLCurrencyUIManager()
{
delete &impl;
}
void LLCurrencyUIManager::setAmount(int amount, bool noEstimate)
{
impl.mUserCurrencyBuy = amount;
impl.mUserEnteredCurrencyBuy = false;
impl.updateUI();
impl.mCurrencyChanged = !noEstimate;
}
int LLCurrencyUIManager::getAmount()
{
return impl.mUserCurrencyBuy;
}
void LLCurrencyUIManager::setZeroMessage(const std::string& message)
{
impl.mZeroMessage = message;
}
void LLCurrencyUIManager::setUSDEstimate(int amount)
{
impl.mUSDCurrencyEstimatedCost = amount;
impl.mUSDCurrencyEstimated = true;
impl.updateUI();
impl.mCurrencyChanged = false;
}
int LLCurrencyUIManager::getUSDEstimate()
{
return impl.mUSDCurrencyEstimated ? impl.mUSDCurrencyEstimatedCost : 0;
}
void LLCurrencyUIManager::setLocalEstimate(const std::string &amount)
{
impl.mLocalCurrencyEstimatedCost = amount;
impl.mLocalCurrencyEstimated = true;
impl.updateUI();
impl.mCurrencyChanged = false;
}
std::string LLCurrencyUIManager::getLocalEstimate() const
{
return impl.getLocalEstimate();
}
void LLCurrencyUIManager::prepare()
{
impl.prepare();
}
void LLCurrencyUIManager::updateUI(bool show)
{
impl.mHidden = !show;
impl.updateUI();
}
bool LLCurrencyUIManager::process()
{
bool changed = false;
changed |= impl.checkTransaction();
changed |= impl.considerUpdateCurrency();
return changed;
}
void LLCurrencyUIManager::buy(const std::string& buy_msg)
{
if (!canBuy())
{
return;
}
LLUIString msg = buy_msg;
msg.setArg("[LINDENS]", llformat("%d", impl.mUserCurrencyBuy));
msg.setArg("[LOCALAMOUNT]", getLocalEstimate());
LLConfirmationManager::confirm(impl.mSiteConfirm,
msg,
impl,
&LLCurrencyUIManager::Impl::startCurrencyBuy);
}
bool LLCurrencyUIManager::inProcess()
{
return impl.mTransactionType != Impl::TransactionNone;
}
bool LLCurrencyUIManager::canCancel()
{
return !buying();
}
bool LLCurrencyUIManager::canBuy()
{
return !inProcess() && impl.hasEstimate() && impl.mUserCurrencyBuy > 0;
}
bool LLCurrencyUIManager::buying()
{
return impl.mTransactionType == Impl::TransactionBuy;
}
bool LLCurrencyUIManager::bought()
{
return impl.mBought;
}
void LLCurrencyUIManager::clearError()
{
impl.clearError();
}
bool LLCurrencyUIManager::hasError()
{
return impl.mError;
}
std::string LLCurrencyUIManager::errorMessage()
{
return impl.mErrorMessage;
}
std::string LLCurrencyUIManager::errorURI()
{
return impl.mErrorURI;
}