diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llfloaterbuyland.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llfloaterbuyland.cpp')
-rw-r--r-- | indra/newview/llfloaterbuyland.cpp | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp new file mode 100644 index 0000000000..6c8b7368a0 --- /dev/null +++ b/indra/newview/llfloaterbuyland.cpp @@ -0,0 +1,1342 @@ +/** + * @file llfloaterbuyland.cpp + * @brief LLFloaterBuyLand class implementation + * + * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterbuyland.h" + +// viewer includes +#include "llagent.h" +#include "llbutton.h" +#include "llcachename.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llconfirmationmanager.h" +#include "llcurrencyuimanager.h" +#include "llfloater.h" +#include "llfloatertools.h" +#include "llframetimer.h" +#include "lliconctrl.h" +#include "lllineeditor.h" +#include "llnotify.h" +#include "llparcel.h" +#include "llstatusbar.h" +#include "lltextbox.h" +#include "lltexturectrl.h" +#include "llviewchildren.h" +#include "llviewercontrol.h" +#include "llvieweruictrlfactory.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llviewertexteditor.h" +#include "llviewerwindow.h" +#include "llweb.h" +#include "llwindow.h" +#include "llworld.h" +#include "llxmlrpctransaction.h" +#include "viewer.h" +#include "roles_constants.h" + +// NOTE: This is duplicated in lldatamoney.cpp ... +const F32 GROUP_LAND_BONUS_FACTOR = 1.1f; +const F64 CURRENCY_ESTIMATE_FREQUENCY = 0.5; + // how long of a pause in typing a currency buy amount before an + // esimate is fetched from the server + +class LLFloaterBuyLandUI +: public LLFloater +{ +private: + LLFloaterBuyLandUI(); + virtual ~LLFloaterBuyLandUI(); + + LLViewerRegion* mRegion; + LLParcel* mParcel; + bool mIsClaim; + bool mIsForGroup; + + bool mCanBuy; + bool mCannotBuyIsError; + std::string mCannotBuyReason; + std::string mCannotBuyURI; + + bool mBought; + + // information about the agent + S32 mAgentCommittedTier; + S32 mAgentCashBalance; + bool mAgentHasNeverOwnedLand; + + // information about the parcel + bool mParcelValid; + bool mParcelIsForSale; + bool mParcelIsFirstLand; + bool mParcelIsGroupLand; + S32 mParcelGroupContribution; + S32 mParcelPrice; + S32 mParcelActualArea; + S32 mParcelBillableArea; + S32 mParcelSupportedObjects; + bool mParcelSoldWithObjects; + std::string mParcelLocation; + LLUUID mParcelSnapshot; + std::string mParcelSellerName; + + // user's choices + S32 mUserPlanChoice; + + // from website + bool mSiteValid; + bool mSiteMembershipUpgrade; + std::string mSiteMembershipAction; + std::vector<std::string> + mSiteMembershipPlanIDs; + std::vector<std::string> + mSiteMembershipPlanNames; + bool mSiteLandUseUpgrade; + std::string mSiteLandUseAction; + std::string mSiteConfirm; + + // values in current Preflight transaction... used to avoid extra + // preflights when the parcel manager goes update crazy + S32 mPreflightAskBillableArea; + S32 mPreflightAskCurrencyBuy; + + LLViewChildren mChildren; + LLCurrencyUIManager mCurrency; + + enum TransactionType + { + TransactionPreflight, + TransactionCurrency, + TransactionBuy + }; + LLXMLRPCTransaction* mTransaction; + TransactionType mTransactionType; + + LLViewerParcelMgr::ParcelBuyInfo* mParcelBuyInfo; + + static LLFloaterBuyLandUI* sInstance; + +public: + static LLFloaterBuyLandUI* soleInstance(bool createIfNeeded); + + void setForGroup(bool is_for_group); + void setParcel(LLViewerRegion* region, LLParcel* parcel); + + void updateAgentInfo(); + void updateParcelInfo(); + void updateCovenantInfo(); + static void onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data); + void updateCovenantText(const std::string& string, const LLUUID &asset_id); + void updateEstateName(const std::string& name); + void updateLastModified(const std::string& text); + void updateEstateOwnerName(const std::string& name); + void updateWebSiteInfo(); + void finishWebSiteInfo(); + + void runWebSitePrep(const std::string& password); + void finishWebSitePrep(); + void sendBuyLand(); + + void updateNames(); + + void refreshUI(); + + void startTransaction(TransactionType type, LLXMLRPCValue params); + bool checkTransaction(); + + void tellUserError(const std::string& message, const std::string& uri); + + virtual BOOL postBuild(); + + void startBuyPreConfirm(); + void startBuyPostConfirm(const std::string& password); + + static void onClickBuy(void* data); + static void onClickCancel(void* data); + static void onClickErrorWeb(void* data); + + virtual void draw(); + virtual BOOL canClose(); + virtual void onClose(bool app_quitting); + +private: + class SelectionObserver : public LLParcelObserver + { + public: + virtual void changed(); + }; +}; + +static void cacheNameUpdateRefreshesBuyLand(const LLUUID&, + const char*, const char*, BOOL, void* data) +{ + LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false); + if (ui) + { + ui->updateNames(); + } +} + +// static +void LLFloaterBuyLand::buyLand( + LLViewerRegion* region, LLParcel* parcel, bool is_for_group) +{ + if(is_for_group && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED)) + { + gViewerWindow->alertXml("OnlyOfficerCanBuyLand"); + return; + } + + LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(true); + ui->setForGroup(is_for_group); + ui->setParcel(region, parcel); + ui->open(); +} + +// static +void LLFloaterBuyLand::updateCovenantText(const std::string& string, const LLUUID &asset_id) +{ + LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE); + if (floater) + { + floater->updateCovenantText(string, asset_id); + } +} + +// static +void LLFloaterBuyLand::updateEstateName(const std::string& name) +{ + LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE); + if (floater) + { + floater->updateEstateName(name); + } +} + +// static +void LLFloaterBuyLand::updateLastModified(const std::string& text) +{ + LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE); + if (floater) + { + floater->updateLastModified(text); + } +} + +// static +void LLFloaterBuyLand::updateEstateOwnerName(const std::string& name) +{ + LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE); + if (floater) + { + floater->updateEstateOwnerName(name); + } +} + +// static +LLFloaterBuyLandUI* LLFloaterBuyLandUI::sInstance = NULL; + +// static +LLFloaterBuyLandUI* LLFloaterBuyLandUI::soleInstance(bool createIfNeeded) +{ +#if !LL_RELEASE_FOR_DOWNLOAD + if (createIfNeeded) + { + delete sInstance; + sInstance = NULL; + } +#endif + if (!sInstance && createIfNeeded) + { + sInstance = new LLFloaterBuyLandUI(); + + gUICtrlFactory->buildFloater(sInstance, "floater_buy_land.xml"); + sInstance->center(); + + static bool observingCacheName = false; + if (!observingCacheName) + { + gCacheName->addObserver(cacheNameUpdateRefreshesBuyLand); + observingCacheName = true; + } + + static SelectionObserver* parcelSelectionObserver = NULL; + if (!parcelSelectionObserver) + { + parcelSelectionObserver = new SelectionObserver; + gParcelMgr->addObserver(parcelSelectionObserver); + } + } + + return sInstance; +} + + +#if LL_WINDOWS +// passing 'this' during construction generates a warning. The callee +// only uses the pointer to hold a reference to 'this' which is +// already valid, so this call does the correct thing. Disable the +// warning so that we can compile without generating a warning. +#pragma warning(disable : 4355) +#endif +LLFloaterBuyLandUI::LLFloaterBuyLandUI() +: LLFloater("Buy Land"), + mParcel(0), + mBought(false), + mParcelValid(false), mSiteValid(false), + mChildren(*this), mCurrency(*this), mTransaction(0), + mParcelBuyInfo(0) +{ +} + +LLFloaterBuyLandUI::~LLFloaterBuyLandUI() +{ + delete mTransaction; + + gParcelMgr->deleteParcelBuy(mParcelBuyInfo); + + if (sInstance == this) + { + sInstance = NULL; + } +} + +void LLFloaterBuyLandUI::SelectionObserver::changed() +{ + LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false); + if (ui) + { + if (gParcelMgr->selectionEmpty()) + { + ui->close(); + } + else { + ui->setParcel( + gParcelMgr->getSelectionRegion(), + gParcelMgr->getSelectedParcel()); + } + } +} + + +void LLFloaterBuyLandUI::updateAgentInfo() +{ + mAgentCommittedTier = gStatusBar->getSquareMetersCommitted(); + mAgentCashBalance = gStatusBar->getBalance(); + mAgentHasNeverOwnedLand = mAgentCommittedTier == 0; + // FIXME: this is an approximation, see SL-10728 +} + +void LLFloaterBuyLandUI::updateParcelInfo() +{ + mParcelValid = mParcel && mRegion; + mParcelIsForSale = false; + mParcelIsFirstLand = false; + mParcelIsGroupLand = false; + mParcelGroupContribution = 0; + mParcelPrice = 0; + mParcelActualArea = 0; + mParcelBillableArea = 0; + mParcelSupportedObjects = 0; + mParcelSoldWithObjects = false; + mParcelLocation = ""; + mParcelSnapshot.setNull(); + mParcelSellerName = ""; + + mCanBuy = false; + mCannotBuyIsError = false; + + if (!mParcelValid) + { + mCannotBuyReason= childGetText("no_land_selected"); + return; + } + + if (gParcelMgr->getMultipleOwners()) + { + mCannotBuyReason = childGetText("multiple_parcels_selected"); + return; + } + + + const LLUUID& parcelOwner = mParcel->getOwnerID(); + + mIsClaim = mParcel->isPublic(); + if (!mIsClaim) + { + mParcelActualArea = mParcel->getArea(); + mParcelIsForSale = mParcel->getForSale(); + mParcelIsFirstLand = mParcel->getReservedForNewbie(); + mParcelIsGroupLand = mParcel->getIsGroupOwned(); + mParcelPrice = mParcelIsForSale ? mParcel->getSalePrice() : 0; + + if (mParcelIsGroupLand) + { + LLUUID group_id = mParcel->getGroupID(); + mParcelGroupContribution = gAgent.getGroupContribution(group_id); + } + } + else + { + mParcelActualArea = gParcelMgr->getClaimableArea(); + mParcelIsForSale = true; + mParcelPrice = mParcelActualArea * mParcel->getClaimPricePerMeter(); + } + + mParcelBillableArea = + llround(mRegion->getBillableFactor() * mParcelActualArea); + + mParcelSupportedObjects = mParcel->getMaxPrimCapacity(); + mParcelSoldWithObjects = mParcel->getSellWithObjects(); + + LLVector3 center = mParcel->getCenterpoint(); + mParcelLocation = llformat("%s %d,%d", + mRegion->getName().c_str(), + (int)center[VX], (int)center[VY] + ); + + mParcelSnapshot = mParcel->getSnapshotID(); + + updateNames(); + + bool haveEnoughCash = mParcelPrice <= mAgentCashBalance; + S32 cashBuy = haveEnoughCash ? 0 : (mParcelPrice - mAgentCashBalance); + mCurrency.setAmount(cashBuy, true); + mCurrency.setZeroMessage(haveEnoughCash ? childGetText("none_needed") : ""); + + // checks that we can buy the land + + if(mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED)) + { + mCannotBuyReason = childGetText("cant_buy_for_group"); + return; + } + + if (!mIsClaim) + { + const LLUUID& authorizedBuyer = mParcel->getAuthorizedBuyerID(); + const LLUUID buyer = gAgent.getID(); + const LLUUID newOwner = mIsForGroup ? gAgent.getGroupID() : buyer; + + if (!mParcelIsForSale + || (mParcelPrice == 0 && authorizedBuyer.isNull())) + { + + mCannotBuyReason = childGetText("parcel_not_for_sale"); + return; + } + + if (parcelOwner == newOwner) + { + if (mIsForGroup) + { + mCannotBuyReason = childGetText("group_already_owns"); + } + else + { + mCannotBuyReason = childGetText("you_already_own"); + } + return; + } + + if (!authorizedBuyer.isNull() && buyer != authorizedBuyer) + { + mCannotBuyReason = childGetText("set_to_sell_to_other"); + return; + } + } + else + { + if (mParcelActualArea == 0) + { + mCannotBuyReason = childGetText("no_public_land"); + return; + } + + if (gParcelMgr->hasOthersSelected()) + { + // Policy: Must not have someone else's land selected + mCannotBuyReason = childGetText("not_owned_by_you"); + return; + } + } + + /* + if ((mRegion->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL) + && !gAgent.isGodlike()) + { + mCannotBuyReason = llformat( + "The region %s does not allow transfer of land.", + mRegion->getName().c_str() ); + return; + } + */ + + if (mParcel->getReservedForNewbie()) + { + if (mIsForGroup) + { + mCannotBuyReason = childGetText("first_time_group"); + return; + } + + if (gStatusBar->getSquareMetersCommitted() > 0) + { + mCannotBuyReason == childGetText("first_time"); + return; + } + + // FIXME: There should be a check based on the database value + // indra.user.ever_owned_land, only that value never makes it + // to the viewer, see SL-10728 + } + + mCanBuy = true; +} + +void LLFloaterBuyLandUI::updateCovenantInfo() +{ + LLViewerRegion* region = gParcelMgr->getSelectionRegion(); + if(!region) return; + + LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text"); + if (region_name) + { + region_name->setText(region->getName()); + } + + LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause"); + if (resellable_clause) + { + if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL) + { + resellable_clause->setText(childGetText("can_not_resell")); + } + else + { + resellable_clause->setText(childGetText("can_resell")); + } + } + + LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause"); + if (changeable_clause) + { + if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES) + { + changeable_clause->setText(childGetText("can_change")); + } + else + { + changeable_clause->setText(childGetText("can_not_change")); + } + } + + LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant"); + if(check) + { + check->set(false); + check->setEnabled(true); + check->setCallbackUserData(this); + check->setCommitCallback(onChangeAgreeCovenant); + } + + LLTextBox* box = (LLTextBox*)getChildByName("covenant_text"); + if(box) + { + box->setVisible(FALSE); + } + + // send EstateCovenantInfo message + LLMessageSystem *msg = gMessageSystem; + msg->newMessage("EstateCovenantRequest"); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID()); + msg->sendReliable(region->getHost()); +} + +// static +void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data) +{ + LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)user_data; + if(self) + { + self->refreshUI(); + } +} + +void LLFloaterBuyLandUI::updateCovenantText(const std::string &string, const LLUUID& asset_id) +{ + LLViewerTextEditor* editor = (LLViewerTextEditor*)getChildByName("covenant_editor"); + if (editor) + { + editor->setHandleEditKeysDirectly(FALSE); + editor->setText(string); + + LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant"); + LLTextBox* box = (LLTextBox*)getChildByName("covenant_text"); + if(check && box) + { + if (asset_id.isNull()) + { + check->set(true); + check->setEnabled(false); + refreshUI(); + + // remove the line stating that you must agree + box->setVisible(FALSE); + } + else + { + check->setEnabled(true); + + // remove the line stating that you must agree + box->setVisible(TRUE); + } + } + } +} + +void LLFloaterBuyLandUI::updateEstateName(const std::string& name) +{ + LLTextBox* box = (LLTextBox*)getChildByName("estate_name_text"); + if (box) box->setText(name); +} + +void LLFloaterBuyLandUI::updateLastModified(const std::string& text) +{ + LLTextBox* editor = (LLTextBox*)getChildByName("covenant_timestamp_text"); + if (editor) editor->setText(text); +} + +void LLFloaterBuyLandUI::updateEstateOwnerName(const std::string& name) +{ + LLTextBox* box = (LLTextBox*)getChildByName("estate_owner_text"); + if (box) box->setText(name); +} + +void LLFloaterBuyLandUI::updateWebSiteInfo() +{ + S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea; + S32 askCurrencyBuy = mCurrency.getAmount(); + + if (mTransaction && mTransactionType == TransactionPreflight + && mPreflightAskBillableArea == askBillableArea + && mPreflightAskCurrencyBuy == askCurrencyBuy) + { + return; + } + + mPreflightAskBillableArea = askBillableArea; + mPreflightAskCurrencyBuy = askCurrencyBuy; + +#if 0 + // enable this code if you want the details to blank while we're talking + // to the web site... it's kind of jarring + mSiteValid = false; + mSiteMembershipUpgrade = false; + mSiteMembershipAction = "(waiting)"; + mSiteMembershipPlanIDs.clear(); + mSiteMembershipPlanNames.clear(); + mSiteLandUseUpgrade = false; + mSiteLandUseAction = "(waiting)"; + mSiteCurrencyEstimated = false; + mSiteCurrencyEstimatedCost = 0; +#endif + + LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct(); + keywordArgs.appendString("agentId", gAgent.getID().getString()); + keywordArgs.appendString("secureSessionId", + gAgent.getSecureSessionID().getString()); + keywordArgs.appendInt("billableArea", mPreflightAskBillableArea); + keywordArgs.appendInt("currencyBuy", mPreflightAskCurrencyBuy); + + LLXMLRPCValue params = LLXMLRPCValue::createArray(); + params.append(keywordArgs); + + startTransaction(TransactionPreflight, params); +} + +void LLFloaterBuyLandUI::finishWebSiteInfo() +{ + + LLXMLRPCValue result = mTransaction->responseValue(); + + mSiteValid = result["success"].asBool(); + if (!mSiteValid) + { + tellUserError( + result["errorMessage"].asString(), + result["errorURI"].asString() + ); + return; + } + + LLXMLRPCValue membership = result["membership"]; + mSiteMembershipUpgrade = membership["upgrade"].asBool(); + mSiteMembershipAction = membership["action"].asString(); + mSiteMembershipPlanIDs.clear(); + mSiteMembershipPlanNames.clear(); + LLXMLRPCValue levels = membership["levels"]; + for (LLXMLRPCValue level = levels.rewind(); + level.isValid(); + level = levels.next()) + { + mSiteMembershipPlanIDs.push_back(level["id"].asString()); + mSiteMembershipPlanNames.push_back(level["description"].asString()); + } + mUserPlanChoice = 0; + + LLXMLRPCValue landUse = result["landUse"]; + mSiteLandUseUpgrade = landUse["upgrade"].asBool(); + mSiteLandUseAction = landUse["action"].asString(); + + LLXMLRPCValue currency = result["currency"]; + mCurrency.setEstimate(currency["estimatedCost"].asInt()); + + mSiteConfirm = result["confirm"].asString(); +} + +void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password) +{ + if (!mCanBuy) + { + return; + } + + BOOL remove_contribution = childGetValue("remove_contribution").asBoolean(); + mParcelBuyInfo = gParcelMgr->setupParcelBuy(gAgent.getID(), gAgent.getSessionID(), + gAgent.getGroupID(), mIsForGroup, mIsClaim, remove_contribution); + + if (!mSiteMembershipUpgrade + && !mSiteLandUseUpgrade + && mCurrency.getAmount() == 0 + && mSiteConfirm != "password") + { + sendBuyLand(); + return; + } + + + std::string newLevel = "noChange"; + + if (mSiteMembershipUpgrade) + { + LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level"); + if (levels) + { + mUserPlanChoice = levels->getCurrentIndex(); + newLevel = mSiteMembershipPlanIDs[mUserPlanChoice]; + } + } + + LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct(); + keywordArgs.appendString("agentId", gAgent.getID().getString()); + keywordArgs.appendString("secureSessionId", + gAgent.getSecureSessionID().getString()); + keywordArgs.appendString("levelId", newLevel); + keywordArgs.appendInt("billableArea", + mIsForGroup ? 0 : mParcelBillableArea); + keywordArgs.appendInt("currencyBuy", mCurrency.getAmount()); + keywordArgs.appendInt("estimatedCost", mCurrency.getEstimate()); + keywordArgs.appendString("confirm", mSiteConfirm); + if (!password.empty()) + { + keywordArgs.appendString("password", password); + } + + LLXMLRPCValue params = LLXMLRPCValue::createArray(); + params.append(keywordArgs); + + startTransaction(TransactionBuy, params); +} + +void LLFloaterBuyLandUI::finishWebSitePrep() +{ + LLXMLRPCValue result = mTransaction->responseValue(); + + bool success = result["success"].asBool(); + if (!success) + { + tellUserError( + result["errorMessage"].asString(), + result["errorURI"].asString() + ); + return; + } + + sendBuyLand(); +} + +void LLFloaterBuyLandUI::sendBuyLand() +{ + gParcelMgr->sendParcelBuy(mParcelBuyInfo); + gParcelMgr->deleteParcelBuy(mParcelBuyInfo); + + mBought = true; +} + +void LLFloaterBuyLandUI::updateNames() +{ + if (!mParcelValid) + { + mParcelSellerName = ""; + return; + } + + if (mIsClaim) + { + mParcelSellerName = "Linden Lab"; + } + else if (mParcel->getIsGroupOwned()) + { + char groupName[DB_LAST_NAME_BUF_SIZE]; + + gCacheName->getGroupName(mParcel->getGroupID(), &groupName[0]); + mParcelSellerName = groupName; + } + else + { + char firstName[DB_LAST_NAME_BUF_SIZE]; + char lastName[DB_LAST_NAME_BUF_SIZE]; + + gCacheName->getName(mParcel->getOwnerID(), firstName, lastName); + mParcelSellerName = llformat("%s %s", firstName, lastName); + } +} + + +void LLFloaterBuyLandUI::startTransaction(TransactionType type, + LLXMLRPCValue params) +{ + delete mTransaction; + mTransaction = NULL; + + mTransactionType = type; + + // Select a URI and method appropriate for the transaction type. + static std::string transaction_uri; + if (transaction_uri.empty()) + { + transaction_uri = getHelperURI() + "landtool.php"; + } + + const char* method; + switch (mTransactionType) + { + case TransactionPreflight: + method = "preflightBuyLandPrep"; + break; + case TransactionBuy: + method = "buyLandPrep"; + break; + default: + llwarns << "LLFloaterBuyLandUI: Unknown transaction type!" << llendl; + return; + } + + mTransaction = new LLXMLRPCTransaction( + transaction_uri, + method, + params, + false /* don't use gzip */ + ); +} + +bool LLFloaterBuyLandUI::checkTransaction() +{ + if (!mTransaction) + { + return false; + } + + if (!mTransaction->process()) + { + return false; + } + + if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete) + { + tellUserError(mTransaction->statusMessage(), mTransaction->statusURI()); + } + else { + switch (mTransactionType) + { + case TransactionPreflight: finishWebSiteInfo(); break; + case TransactionBuy: finishWebSitePrep(); break; + default: ; + } + } + + delete mTransaction; + mTransaction = NULL; + + return true; +} + +void LLFloaterBuyLandUI::tellUserError( + const std::string& message, const std::string& uri) +{ + mCanBuy = false; + mCannotBuyIsError = true; + mCannotBuyReason = childGetText("fetching_error"); + mCannotBuyReason += message; + mCannotBuyURI = uri; +} + + +// virtual +BOOL LLFloaterBuyLandUI::postBuild() +{ + mCurrency.prepare(); + + childSetAction("buy_btn", onClickBuy, this); + childSetAction("cancel_btn", onClickCancel, this); + childSetAction("error_web", onClickErrorWeb, this); + + return TRUE; +} + +void LLFloaterBuyLandUI::setParcel(LLViewerRegion* region, LLParcel* parcel) +{ + if (mTransaction && mTransactionType == TransactionBuy) + { + // the user is buying, don't change the selection + return; + } + + mRegion = region; + mParcel = parcel; + + updateAgentInfo(); + updateParcelInfo(); + updateCovenantInfo(); + if (mCanBuy) + { + updateWebSiteInfo(); + } + refreshUI(); +} + +void LLFloaterBuyLandUI::setForGroup(bool forGroup) +{ + mIsForGroup = forGroup; +} + +void LLFloaterBuyLandUI::draw() +{ + LLFloater::draw(); + + bool needsUpdate = false; + needsUpdate |= checkTransaction(); + needsUpdate |= mCurrency.process(); + + if (mBought) + { + close(); + } + else if (needsUpdate) + { + if (mCanBuy && mCurrency.hasError()) + { + tellUserError(mCurrency.errorMessage(), mCurrency.errorURI()); + } + + refreshUI(); + } +} + +BOOL LLFloaterBuyLandUI::canClose() +{ + return (mTransaction ? FALSE : TRUE) && mCurrency.canCancel(); +} + +void LLFloaterBuyLandUI::onClose(bool app_quitting) +{ + LLFloater::onClose(app_quitting); + delete this; +} + + +void LLFloaterBuyLandUI::refreshUI() +{ + // section zero: title area + { + LLTextureCtrl* snapshot = LLViewerUICtrlFactory::getTexturePickerByName(this, "info_image"); + if (snapshot) + { + snapshot->setImageAssetID( + mParcelValid ? mParcelSnapshot : LLUUID::null); + } + + + + if (mParcelValid) + { + childSetText("info_parcel", mParcelLocation); + + + childSetTextArg("meters_supports_object", "[AMOUNT]", llformat("%d", mParcelActualArea)); + childSetTextArg("meters_supports_object", "[AMOUNT2]", llformat("%d", mParcelSupportedObjects)); + + childSetText("info_size", childGetText("meters_supports_object")); + + + childSetText("info_price", + llformat( + "L$ %d%s", + mParcelPrice, + mParcelSoldWithObjects + ? "\nsold with objects" + : "")); + childSetVisible("info_price", mParcelIsForSale); + } + else + { + childSetText("info_parcel", "(no parcel selected)"); + childSetText("info_size", ""); + childSetText("info_price", ""); + } + + childSetText("info_action", + mCanBuy + ? + mIsForGroup + ? childGetText("buying_for_group")//"Buying land for group:" + : childGetText("buying_will")//"Buying this land will:" + : + mCannotBuyIsError + ? childGetText("cannot_buy_now")//"Cannot buy now:" + : childGetText("not_for_sale")//"Not for sale:" + + ); + } + + bool showingError = !mCanBuy || !mSiteValid; + + // error section + if (showingError) + { + mChildren.setBadge("step_error", + mCannotBuyIsError + ? LLViewChildren::BADGE_ERROR + : LLViewChildren::BADGE_WARN); + + LLTextBox* message = LLUICtrlFactory::getTextBoxByName(this, "error_message"); + if (message) + { + message->setVisible(true); + message->setWrappedText( + !mCanBuy ? mCannotBuyReason : "(waiting for data)" + ); + } + + childSetVisible("error_web", + mCannotBuyIsError && !mCannotBuyURI.empty()); + } + else + { + childHide("step_error"); + childHide("error_message"); + childHide("error_web"); + } + + + // section one: account + if (!showingError) + { + mChildren.setBadge("step_1", + mSiteMembershipUpgrade + ? LLViewChildren::BADGE_NOTE + : LLViewChildren::BADGE_OK); + childSetText("account_action", mSiteMembershipAction); + childSetText("account_reason", + mSiteMembershipUpgrade + ? childGetText("must_upgrade") + : childGetText("cant_own_land") + ); + + LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level"); + if (levels) + { + levels->setVisible(mSiteMembershipUpgrade); + + levels->removeall(); + for(std::vector<std::string>::const_iterator i + = mSiteMembershipPlanNames.begin(); + i != mSiteMembershipPlanNames.end(); + ++i) + { + levels->add(*i); + } + + levels->setCurrentByIndex(mUserPlanChoice); + } + + childShow("step_1"); + childShow("account_action"); + childShow("account_reason"); + } + else + { + childHide("step_1"); + childHide("account_action"); + childHide("account_reason"); + childHide("account_level"); + } + + // section two: land use fees + if (!showingError) + { + mChildren.setBadge("step_2", + mSiteLandUseUpgrade + ? LLViewChildren::BADGE_NOTE + : LLViewChildren::BADGE_OK); + childSetText("land_use_action", mSiteLandUseAction); + + std::string message; + + if (mIsForGroup) + { + childSetTextArg("insufficient_land_credits", "[GROUP]", gAgent.mGroupName); + + + message += childGetText("insufficient_land_credits"); + + } + else if (mAgentHasNeverOwnedLand) + { + if (mParcelIsFirstLand) + { + message += childGetText("first_purchase"); + } + else + { + message += childGetText("first_time_but_not_first_land"); + } + } + else + { + + childSetTextArg("land_holdings", "[BUYER]", llformat("%d", mAgentCommittedTier)); + message += childGetText("land_holdings"); + + } + + if (!mParcelValid) + { + message += "(no parcel selected)"; + } + else if (mParcelBillableArea == mParcelActualArea) + { + childSetTextArg("parcel_meters", "[AMOUNT]", llformat("%d", mParcelActualArea)); + message += childGetText("parcel_meters"); + } + else + { + + if (mParcelBillableArea > mParcelActualArea) + { + childSetTextArg("premium_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) ); + message += childGetText("premium_land"); + } + else + { + childSetTextArg("discounted_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) ); + message += childGetText("discounted_land"); + } + } + + childSetWrappedText("land_use_reason", message); + + childShow("step_2"); + childShow("land_use_action"); + childShow("land_use_reason"); + } + else + { + childHide("step_2"); + childHide("land_use_action"); + childHide("land_use_reason"); + } + + // section three: purchase & currency + S32 finalBalance = mAgentCashBalance + mCurrency.getAmount() - mParcelPrice; + bool willHaveEnough = finalBalance >= 0; + bool haveEnough = mAgentCashBalance >= mParcelPrice; + S32 minContribution = llceil((F32)mParcelBillableArea / GROUP_LAND_BONUS_FACTOR); + bool groupContributionEnough = mParcelGroupContribution >= minContribution; + + mCurrency.updateUI(!showingError && !haveEnough); + + if (!showingError) + { + mChildren.setBadge("step_3", + !willHaveEnough + ? LLViewChildren::BADGE_WARN + : mCurrency.getAmount() > 0 + ? LLViewChildren::BADGE_NOTE + : LLViewChildren::BADGE_OK); + + childSetText("purchase_action", + llformat( + "Pay L$ %d to %s for this land", + mParcelPrice, + mParcelSellerName.c_str() + )); + childSetVisible("purchase_action", mParcelValid); + + std::string reasonString; + + if (haveEnough) + { + + childSetTextArg("have_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance)); + childSetText("currency_reason", childGetText("have_enough_lindens")); + } + else + { + childSetTextArg("not_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance)); + childSetTextArg("not_enough_lindens", "[AMOUNT2]", llformat("%d", mParcelPrice - mAgentCashBalance)); + + childSetText("currency_reason", childGetText("not_enough_lindens")); + + childSetTextArg("currency_est", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0)); + + } + + if (willHaveEnough) + { + + childSetTextArg("balance_left", "[AMOUNT]", llformat("%d", finalBalance)); + + childSetText("currency_balance", childGetText("balance_left")); + + } + else + { + + childSetTextArg("balance_needed", "[AMOUNT]", llformat("%d", mParcelPrice - mAgentCashBalance)); + + childSetText("currency_balance", childGetText("balance_needed")); + + } + + //remove_contribution not in XML - ?! + childSetValue("remove_contribution", LLSD(groupContributionEnough)); + childSetEnabled("remove_contribution", groupContributionEnough); + bool showRemoveContribution = mParcelIsGroupLand + && (mParcelGroupContribution > 0); + childSetText("remove_contribution", + llformat("Remove %d square meters of contribution from group", + minContribution)); + childSetVisible("remove_contribution", showRemoveContribution); + + childShow("step_3"); + childShow("purchase_action"); + childShow("currency_reason"); + childShow("currency_balance"); + } + else + { + childHide("step_3"); + childHide("purchase_action"); + childHide("currency_reason"); + childHide("currency_balance"); + childHide("remove_group_donation"); + } + + + bool agrees_to_covenant = false; + LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant"); + if (check) + { + agrees_to_covenant = check->get(); + } + + childSetEnabled("buy_btn", + mCanBuy && mSiteValid && willHaveEnough && !mTransaction && agrees_to_covenant); +} + +void LLFloaterBuyLandUI::startBuyPreConfirm() +{ + std::string action; + + if (mSiteMembershipUpgrade) + { + action += mSiteMembershipAction; + action += "\n"; + + LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level"); + if (levels) + { + action += " * "; + action += mSiteMembershipPlanNames[levels->getCurrentIndex()]; + action += "\n"; + } + } + if (mSiteLandUseUpgrade) + { + action += mSiteLandUseAction; + action += "\n"; + } + if (mCurrency.getAmount() > 0) + { + + childSetTextArg("buy_for_US", "[AMOUNT]", llformat("%d", mCurrency.getAmount())); + childSetTextArg("buy_for_US", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0)); + + action += childGetText("buy_for_US"); + } + + childSetTextArg("pay_to_for_land", "[AMOUNT]", llformat("%d", mParcelPrice)); + childSetTextArg("pay_to_for_land", "[SELLER]", mParcelSellerName.c_str()); + + action += childGetText("pay_to_for_land"); + + + LLConfirmationManager::confirm(mSiteConfirm, + action, + *this, + &LLFloaterBuyLandUI::startBuyPostConfirm); +} + +void LLFloaterBuyLandUI::startBuyPostConfirm(const std::string& password) +{ + runWebSitePrep(password); + + mCanBuy = false; + mCannotBuyReason = childGetText("processing"); + refreshUI(); +} + + +// static +void LLFloaterBuyLandUI::onClickBuy(void* data) +{ + LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data; + self->startBuyPreConfirm(); +} + +// static +void LLFloaterBuyLandUI::onClickCancel(void* data) +{ + LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data; + self->close(); +} + +// static +void LLFloaterBuyLandUI::onClickErrorWeb(void* data) +{ + LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data; + LLWeb::loadURLExternal(self->mCannotBuyURI); + self->close(); +} + |