Skip to content
Snippets Groups Projects
Forked from Alchemy Viewer / Alchemy Viewer
15033 commits behind the upstream repository.
  • Nat Goodspeed's avatar
    5a260e0c
    SL-11216: Convert LLVersionInfo to an LLSingleton. · 5a260e0c
    Nat Goodspeed authored
    This changeset is meant to exemplify how to convert a "namespace" class whose
    methods are static -- and whose data are module-static -- to an LLSingleton.
    LLVersionInfo has no initClass() or cleanupClass() methods, but the general
    idea is the same.
    
    * Derive the class from LLSingleton<T>:
      class LLSomeSingleton: public LLSingleton<LLSomeSingleton> { ... };
    * Add LLSINGLETON(LLSomeSingleton); in the private section of the class. This
      usage implies a separate LLSomeSingleton::LLSomeSingleton() definition, as
      described in indra/llcommon/llsingleton.h.
    * Move module-scope data in the .cpp file to non-static class members. Change
      any sVariableName to mVariableName to avoid being outright misleading.
    * Make static class methods non-static. Remove '//static' comments from method
      definitions as needed.
    * For LLVersionInfo specifically, the 'const std::string&' return type was
      replaced with 'std::string'. Returning a reference to a static or a member,
      const or otherwise, is an anti-pattern: the interface constrains the
      implementation, prohibiting possibly later returning a temporary (an
      expression).
    * For LLVersionInfo specifically, 'const S32' return type was replaced with
      simple 'S32'. 'const' is just noise in that usage.
    * Simple member initialization (e.g. the original initializer expressions for
      static variables) can be done with member{ value } initializers (no examples
      here though).
    * Delete initClass() method.
    * LLSingleton's forté is of course lazy initialization. It might work to
      simply delete any calls to initClass(). But if there are side effects that
      must happen at that moment, replace LLSomeSingleton::initClass() with
      (void)LLSomeSingleton::instance();
    * Most initClass() initialization can be done in the constructor, as would
      normally be the case.
    * Initialization that might cause a circular LLSingleton reference should be
      moved to initSingleton(). Override 'void initSingleton();' should be private.
    * For LLVersionInfo specifically, certain initialization that used to be
      lazily performed was made unconditional, due to its low cost.
    * For LLVersionInfo specifically, certain initialization involved calling
      methods that have become non-static. This was moved to initSingleton()
      because, in a constructor body, 'this' does not yet point to the enclosing
      class.
    * Delete cleanupClass() method.
    * There is already a generic LLSingletonBase::deleteAll() call in
      LLAppViewer::cleanup(). It might work to let this new LLSingleton be cleaned
      up with all the rest. But if there are side effects that must happen at that
      moment, replace LLSomeSingleton::cleanupClass() with
      LLSomeSingleton::deleteSingleton(). That said, much of the benefit of
      converting to LLSingleton is deleteAll()'s guarantee that cross-LLSingleton
      dependencies will be properly honored: we're trying to migrate the code base
      away from the present fragile manual cleanup sequence.
    * Most cleanupClass() cleanup can be done in the destructor, as would normally
      be the case.
    * Cleanup that might throw an exception should be moved to cleanupSingleton().
      Override 'void cleanupSingleton();' should be private.
    * Within LLSomeSingleton methods, remove any existing
      LLSomeSingleton::methodName() qualification: simple methodName() is better.
    * In the rest of the code base, convert most LLSomeSingleton::methodName()
      references to LLSomeSingleton::instance().methodName(). (Prefer instance() to
      getInstance() because a reference does not admit the possibility of NULL.)
    * Of course, LLSomeSingleton::ENUM_VALUE can remain unchanged.
    
    In general, for many successive references to an LLSingleton instance, it
    can be useful to capture the instance() as in:
    
    auto& versionInfo{LLVersionInfo::instance()};
    // ... versionInfo.getVersion() ...
    
    We did not do that here only to simplify the code review.
    
    The STRINGIZE(expression) macro encapsulates:
    std::ostringstream out;
    out << expression;
    return out.str();
    We used that in a couple places.
    
    For LLVersionInfo specifically, lllogininstance_test.cpp used to dummy out a
    couple specific static methods. It's harder to dummy out
    LLSingleton::instance() references, so we add the real class to that test.
    5a260e0c
    History
    SL-11216: Convert LLVersionInfo to an LLSingleton.
    Nat Goodspeed authored
    This changeset is meant to exemplify how to convert a "namespace" class whose
    methods are static -- and whose data are module-static -- to an LLSingleton.
    LLVersionInfo has no initClass() or cleanupClass() methods, but the general
    idea is the same.
    
    * Derive the class from LLSingleton<T>:
      class LLSomeSingleton: public LLSingleton<LLSomeSingleton> { ... };
    * Add LLSINGLETON(LLSomeSingleton); in the private section of the class. This
      usage implies a separate LLSomeSingleton::LLSomeSingleton() definition, as
      described in indra/llcommon/llsingleton.h.
    * Move module-scope data in the .cpp file to non-static class members. Change
      any sVariableName to mVariableName to avoid being outright misleading.
    * Make static class methods non-static. Remove '//static' comments from method
      definitions as needed.
    * For LLVersionInfo specifically, the 'const std::string&' return type was
      replaced with 'std::string'. Returning a reference to a static or a member,
      const or otherwise, is an anti-pattern: the interface constrains the
      implementation, prohibiting possibly later returning a temporary (an
      expression).
    * For LLVersionInfo specifically, 'const S32' return type was replaced with
      simple 'S32'. 'const' is just noise in that usage.
    * Simple member initialization (e.g. the original initializer expressions for
      static variables) can be done with member{ value } initializers (no examples
      here though).
    * Delete initClass() method.
    * LLSingleton's forté is of course lazy initialization. It might work to
      simply delete any calls to initClass(). But if there are side effects that
      must happen at that moment, replace LLSomeSingleton::initClass() with
      (void)LLSomeSingleton::instance();
    * Most initClass() initialization can be done in the constructor, as would
      normally be the case.
    * Initialization that might cause a circular LLSingleton reference should be
      moved to initSingleton(). Override 'void initSingleton();' should be private.
    * For LLVersionInfo specifically, certain initialization that used to be
      lazily performed was made unconditional, due to its low cost.
    * For LLVersionInfo specifically, certain initialization involved calling
      methods that have become non-static. This was moved to initSingleton()
      because, in a constructor body, 'this' does not yet point to the enclosing
      class.
    * Delete cleanupClass() method.
    * There is already a generic LLSingletonBase::deleteAll() call in
      LLAppViewer::cleanup(). It might work to let this new LLSingleton be cleaned
      up with all the rest. But if there are side effects that must happen at that
      moment, replace LLSomeSingleton::cleanupClass() with
      LLSomeSingleton::deleteSingleton(). That said, much of the benefit of
      converting to LLSingleton is deleteAll()'s guarantee that cross-LLSingleton
      dependencies will be properly honored: we're trying to migrate the code base
      away from the present fragile manual cleanup sequence.
    * Most cleanupClass() cleanup can be done in the destructor, as would normally
      be the case.
    * Cleanup that might throw an exception should be moved to cleanupSingleton().
      Override 'void cleanupSingleton();' should be private.
    * Within LLSomeSingleton methods, remove any existing
      LLSomeSingleton::methodName() qualification: simple methodName() is better.
    * In the rest of the code base, convert most LLSomeSingleton::methodName()
      references to LLSomeSingleton::instance().methodName(). (Prefer instance() to
      getInstance() because a reference does not admit the possibility of NULL.)
    * Of course, LLSomeSingleton::ENUM_VALUE can remain unchanged.
    
    In general, for many successive references to an LLSingleton instance, it
    can be useful to capture the instance() as in:
    
    auto& versionInfo{LLVersionInfo::instance()};
    // ... versionInfo.getVersion() ...
    
    We did not do that here only to simplify the code review.
    
    The STRINGIZE(expression) macro encapsulates:
    std::ostringstream out;
    out << expression;
    return out.str();
    We used that in a couple places.
    
    For LLVersionInfo specifically, lllogininstance_test.cpp used to dummy out a
    couple specific static methods. It's harder to dummy out
    LLSingleton::instance() references, so we add the real class to that test.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
llcurrencyuimanager.cpp 14.67 KiB
/** 
 * @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"


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, LLXMLRPCValue params);
	bool checkTransaction();
		// return true if update needed
	
	void setError(const std::string& message, const std::string& uri);
	void clearError();

	bool considerUpdateCurrency();
		// return true if update needed
	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;
	}
	
	LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
	keywordArgs.appendString("agentId", gAgent.getID().asString());
	keywordArgs.appendString(
		"secureSessionId",
		gAgent.getSecureSessionID().asString());
	keywordArgs.appendString("language", LLUI::getLanguage());
	keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
	keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
	keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild());
	
	LLXMLRPCValue params = LLXMLRPCValue::createArray();
	params.append(keywordArgs);

	startTransaction(TransactionCurrency, "getCurrencyQuote", params);
}

void LLCurrencyUIManager::Impl::finishCurrencyInfo()
{
	LLXMLRPCValue result = mTransaction->responseValue();

	bool success = result["success"].asBool();
	if (!success)
	{
		setError(
			result["errorMessage"].asString(),
			result["errorURI"].asString()
		);
		return;
	}
	
	LLXMLRPCValue currency = result["currency"];

	// old XML-RPC server: estimatedCost = value in US cents
	mUSDCurrencyEstimated = currency["estimatedCost"].isValid();
	if (mUSDCurrencyEstimated)
	{
		mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInt();
	}

	// newer XML-RPC server: estimatedLocalCost = local currency string
	mLocalCurrencyEstimated = currency["estimatedLocalCost"].isValid();
	if (mLocalCurrencyEstimated)
	{
		mLocalCurrencyEstimatedCost = currency["estimatedLocalCost"].asString();
		mSupportsInternationalBilling = true;
	}

	S32 newCurrencyBuy = currency["currencyBuy"].asInt();
	if (newCurrencyBuy != mUserCurrencyBuy)
	{
		mUserCurrencyBuy = newCurrencyBuy;
		mUserEnteredCurrencyBuy = false;
	}

	mSiteConfirm = result["confirm"].asString();
}

void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
{
	LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
	keywordArgs.appendString("agentId", gAgent.getID().asString());
	keywordArgs.appendString(
		"secureSessionId",
		gAgent.getSecureSessionID().asString());
	keywordArgs.appendString("language", LLUI::getLanguage());
	keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
	if (mUSDCurrencyEstimated)
	{
		keywordArgs.appendInt("estimatedCost", mUSDCurrencyEstimatedCost);
	}
	if (mLocalCurrencyEstimated)
	{
		keywordArgs.appendString("estimatedLocalCost", mLocalCurrencyEstimatedCost);
	}
	keywordArgs.appendString("confirm", mSiteConfirm);
	if (!password.empty())
	{
		keywordArgs.appendString("password", password);
	}
	keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
	keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild());

	LLXMLRPCValue params = LLXMLRPCValue::createArray();
	params.append(keywordArgs);

	startTransaction(TransactionBuy, "buyCurrency", params);

	clearEstimate();
	mCurrencyChanged = false;	
}

void LLCurrencyUIManager::Impl::finishCurrencyBuy()
{
	LLXMLRPCValue result = mTransaction->responseValue();

	bool success = result["success"].asBool();
	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, LLXMLRPCValue params)
{
	static std::string transactionURI;
	if (transactionURI.empty())
	{
		transactionURI = LLGridManager::getInstance()->getHelperURI() + "currency.php";
	}

	delete mTransaction;

	mTransactionType = type;
	mTransaction = new LLXMLRPCTransaction(
		transactionURI,
		method,
		params,
		false /* don't use gzip */
		);

	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 (!mZeroMessage.empty() && mUserCurrencyBuy == 0)
			{
				lindenAmount->setText(LLStringUtil::null);
			}
			else
			{
				lindenAmount->setText(llformat("%d", mUserCurrencyBuy));
			}

			lindenAmount->selectAll();
		}
	}

	mPanel.getChild<LLUICtrl>("currency_est")->setTextArg("[LOCALAMOUNT]", getLocalEstimate());
	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 impl.mTransactionType != Impl::TransactionBuy;
}

bool LLCurrencyUIManager::canBuy()
{
	return impl.mTransactionType == Impl::TransactionNone
		&& impl.hasEstimate()
		&& impl.mUserCurrencyBuy > 0;
}

bool LLCurrencyUIManager::buying()
{
	return impl.mTransactionType == Impl::TransactionBuy;
}

bool LLCurrencyUIManager::bought()
{
	return impl.mBought;
}

bool LLCurrencyUIManager::hasError()
{
	return impl.mError;
}

std::string LLCurrencyUIManager::errorMessage()
{
	return impl.mErrorMessage;
}

std::string LLCurrencyUIManager::errorURI()
{
	return impl.mErrorURI;
}