/** * @file llpanelgrouplandmoney.cpp * @brief Panel for group land and L$. * * $LicenseInfo:firstyear=2006&license=viewergpl$ * * Copyright (c) 2006-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 "llpanelgrouplandmoney.h" #include "lluiconstants.h" #include "roles_constants.h" #include "llparcel.h" #include "llqueryflags.h" #include "llagent.h" #include "lliconctrl.h" #include "lllineeditor.h" #include "llproductinforequest.h" #include "llscrolllistctrl.h" #include "lltextbox.h" #include "lltabcontainer.h" #include "lltrans.h" #include "lltransactiontypes.h" #include "lluictrlfactory.h" #include "llstatusbar.h" #include "llfloaterworldmap.h" #include "llviewermessage.h" #include "hippogridmanager.h" //////////////////////////////////////////////////////////////////////////// class LLGroupMoneyTabEventHandler { public: LLGroupMoneyTabEventHandler(LLButton* earlier_button, LLButton* later_button, LLTextEditor* text_editor, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id, S32 interval_length_days, S32 max_interval_days); virtual ~LLGroupMoneyTabEventHandler(); virtual void requestData(LLMessageSystem* msg); virtual void processReply(LLMessageSystem* msg, void** data); virtual void onClickEarlier(); virtual void onClickLater(); virtual void onClickTab(); static void clickEarlierCallback(void* data); static void clickLaterCallback(void* data); static void clickTabCallback(void* user_data, bool from_click); static LLMap sInstanceIDs; static std::map sTabsToHandlers; protected: class impl; impl* mImplementationp; }; class LLGroupMoneyDetailsTabEventHandler : public LLGroupMoneyTabEventHandler { public: LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id); virtual ~LLGroupMoneyDetailsTabEventHandler(); virtual void requestData(LLMessageSystem* msg); virtual void processReply(LLMessageSystem* msg, void** data); }; class LLGroupMoneySalesTabEventHandler : public LLGroupMoneyTabEventHandler { public: LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id); virtual ~LLGroupMoneySalesTabEventHandler(); virtual void requestData(LLMessageSystem* msg); virtual void processReply(LLMessageSystem* msg, void** data); }; class LLGroupMoneyPlanningTabEventHandler : public LLGroupMoneyTabEventHandler { public: LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editor, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id); virtual ~LLGroupMoneyPlanningTabEventHandler(); virtual void requestData(LLMessageSystem* msg); virtual void processReply(LLMessageSystem* msg, void** data); }; //////////////////////////////////////////////////////////////////////////// class LLPanelGroupLandMoney::impl { public: impl(LLPanelGroupLandMoney& panel, const LLUUID& group_id); //constructor virtual ~impl(); void requestGroupLandInfo(); int getStoredContribution(); void setYourContributionTextField(int contrib); void setYourMaxContributionTextBox(int max); virtual void onMapButton(); virtual bool applyContribution(); virtual void processGroupLand(LLMessageSystem* msg); static void mapCallback(void* data); static void contributionCommitCallback(LLUICtrl* ctrl, void* userdata); static void contributionKeystrokeCallback(LLLineEditor* caller, void* userdata); //member variables public: LLPanelGroupLandMoney& mPanel; LLTextBox* mGroupOverLimitTextp; LLIconCtrl* mGroupOverLimitIconp; LLLineEditor* mYourContributionEditorp; LLButton* mMapButtonp; LLGroupMoneyTabEventHandler* mMoneyDetailsTabEHp; LLGroupMoneyTabEventHandler* mMoneyPlanningTabEHp; LLGroupMoneyTabEventHandler* mMoneySalesTabEHp; LLScrollListCtrl* mGroupParcelsp; LLUUID mGroupID; LLUUID mTransID; bool mBeenActivated; bool mNeedsSendGroupLandRequest; bool mNeedsApply; std::string mCantViewParcelsText; std::string mCantViewAccountsText; }; //******************************************* //** LLPanelGroupLandMoney::impl Functions ** //******************************************* LLPanelGroupLandMoney::impl::impl(LLPanelGroupLandMoney& panel, const LLUUID& group_id) : mPanel(panel), mGroupID(group_id) { mTransID = LLUUID::null; mBeenActivated = false; mNeedsSendGroupLandRequest = true; mNeedsApply = false; mYourContributionEditorp = NULL; mMapButtonp = NULL; mGroupParcelsp = NULL; mGroupOverLimitTextp = NULL; mGroupOverLimitIconp = NULL; mMoneySalesTabEHp = NULL; mMoneyPlanningTabEHp = NULL; mMoneyDetailsTabEHp = NULL; } LLPanelGroupLandMoney::impl::~impl() { if ( mMoneySalesTabEHp ) delete mMoneySalesTabEHp; if ( mMoneyDetailsTabEHp ) delete mMoneyDetailsTabEHp; if ( mMoneyPlanningTabEHp ) delete mMoneyPlanningTabEHp; } void LLPanelGroupLandMoney::impl::requestGroupLandInfo() { U32 query_flags = DFQ_GROUP_OWNED; mTransID.generate(); mGroupParcelsp->deleteAllItems(); send_places_query(mGroupID, mTransID, "", query_flags, LLParcel::C_ANY, ""); } void LLPanelGroupLandMoney::impl::onMapButton() { LLScrollListItem* itemp; itemp = mGroupParcelsp->getFirstSelected(); if (!itemp) return; const LLScrollListCell* cellp; cellp = itemp->getColumn(itemp->getNumColumns() - 1); // hidden column is last F32 global_x = 0.f; F32 global_y = 0.f; sscanf(cellp->getValue().asString().c_str(), "%f %f", &global_x, &global_y); // Hack: Use the agent's z-height F64 global_z = gAgent.getPositionGlobal().mdV[VZ]; LLVector3d pos_global(global_x, global_y, global_z); gFloaterWorldMap->trackLocation(pos_global); LLFloaterWorldMap::show(NULL, TRUE); } bool LLPanelGroupLandMoney::impl::applyContribution() { // calculate max donation, which is sum of available and current. S32 your_contribution = 0; S32 sqm_avail; your_contribution = getStoredContribution(); sqm_avail = your_contribution; if(gStatusBar) { sqm_avail += gStatusBar->getSquareMetersLeft(); } // get new contribution and compare to available S32 new_contribution = atoi(mYourContributionEditorp->getText().c_str()); if( new_contribution != your_contribution && new_contribution >= 0 && new_contribution <= sqm_avail ) { // update group info and server if(!gAgent.setGroupContribution(mGroupID, new_contribution)) { // should never happen... llwarns << "Unable to set contribution." << llendl; return false; } } else { //TODO: throw up some error message here and return? right now we just //fail silently and force the previous value -jwolk new_contribution = your_contribution; } //set your contribution setYourContributionTextField(new_contribution); return true; } // Retrieves the land contribution for this agent that is currently // stored in the database, NOT what is currently entered in the text field int LLPanelGroupLandMoney::impl::getStoredContribution() { LLGroupData group_data; group_data.mContribution = 0; gAgent.getGroupData(mGroupID, group_data); return group_data.mContribution; } // Fills in the text field with the contribution, contrib void LLPanelGroupLandMoney::impl::setYourContributionTextField(int contrib) { std::string buffer = llformat("%d", contrib); if ( mYourContributionEditorp ) { mYourContributionEditorp->setText(buffer); } } void LLPanelGroupLandMoney::impl::setYourMaxContributionTextBox(int max) { mPanel.childSetTextArg("your_contribution_max_value", "[AMOUNT]", llformat("%d", max)); } //static void LLPanelGroupLandMoney::impl::mapCallback(void* data) { LLPanelGroupLandMoney::impl* selfp = (LLPanelGroupLandMoney::impl*) data; if ( selfp ) selfp->onMapButton(); } void LLPanelGroupLandMoney::impl::contributionCommitCallback(LLUICtrl* ctrl, void* userdata) { LLPanelGroupLandMoney* tabp = (LLPanelGroupLandMoney*) userdata; LLLineEditor* editorp = (LLLineEditor*) ctrl; if ( tabp && editorp ) { impl* self = tabp->mImplementationp; int your_contribution = 0; int new_contribution = 0; new_contribution= atoi(editorp->getText().c_str()); your_contribution = self->getStoredContribution(); //reset their junk data to be "good" data to us self->setYourContributionTextField(new_contribution); //check to see if they're contribution text has changed self->mNeedsApply = new_contribution != your_contribution; tabp->notifyObservers(); } } void LLPanelGroupLandMoney::impl::contributionKeystrokeCallback(LLLineEditor* caller, void* userdata) { impl::contributionCommitCallback(caller, userdata); } //static void LLPanelGroupLandMoney::impl::processGroupLand(LLMessageSystem* msg) { S32 count = msg->getNumberOfBlocks("QueryData"); if(count > 0) { S32 first_block = 0; LLUUID owner_id; LLUUID trans_id; msg->getUUID("QueryData", "OwnerID", owner_id, 0); msg->getUUID("TransactionData", "TransactionID", trans_id); if(owner_id.isNull()) { // special block which has total contribution ++first_block; S32 total_contribution; msg->getS32("QueryData", "ActualArea", total_contribution, 0); mPanel.childSetTextArg("total_contributed_land_value", "[AREA]", llformat("%d", total_contribution)); S32 committed; msg->getS32("QueryData", "BillableArea", committed, 0); mPanel.childSetTextArg("total_land_in_use_value", "[AREA]", llformat("%d", committed)); S32 available = total_contribution - committed; mPanel.childSetTextArg("land_available_value", "[AREA]", llformat("%d", available)); if ( mGroupOverLimitTextp && mGroupOverLimitIconp ) { mGroupOverLimitIconp->setVisible(available < 0); mGroupOverLimitTextp->setVisible(available < 0); } } if ( trans_id != mTransID ) return; // This power was removed to make group roles simpler //if ( !gAgent.hasPowerInGroup(mGroupID, GP_LAND_VIEW_OWNED) ) return; if (!gAgent.isInGroup(mGroupID)) return; //we updated more than just the available area special block if ( count > 1) { mMapButtonp->setEnabled(TRUE); } std::string name; std::string desc; S32 actual_area; S32 billable_area; U8 flags; F32 global_x; F32 global_y; std::string sim_name; std::string land_sku; std::string land_type; for(S32 i = first_block; i < count; ++i) { msg->getUUID("QueryData", "OwnerID", owner_id, i); msg->getString("QueryData", "Name", name, i); msg->getString("QueryData", "Desc", desc, i); msg->getS32("QueryData", "ActualArea", actual_area, i); msg->getS32("QueryData", "BillableArea", billable_area, i); msg->getU8("QueryData", "Flags", flags, i); msg->getF32("QueryData", "GlobalX", global_x, i); msg->getF32("QueryData", "GlobalY", global_y, i); msg->getString("QueryData", "SimName", sim_name, i); if ( msg->getSizeFast(_PREHASH_QueryData, i, _PREHASH_ProductSKU) > 0 ) { msg->getStringFast( _PREHASH_QueryData, _PREHASH_ProductSKU, land_sku, i); llinfos << "Land sku: " << land_sku << llendl; land_type = LLProductInfoRequestManager::instance().getDescriptionForSku(land_sku); } else { land_sku.clear(); land_type = LLTrans::getString("land_type_unknown"); } S32 region_x = llround(global_x) % REGION_WIDTH_UNITS; S32 region_y = llround(global_y) % REGION_WIDTH_UNITS; std::string location = sim_name + llformat(" (%d, %d)", region_x, region_y); std::string area; if(billable_area == actual_area) { area = llformat("%d", billable_area); } else { area = llformat("%d / %d", billable_area, actual_area); } std::string hidden; hidden = llformat("%f %f", global_x, global_y); LLSD row; row["columns"][0]["column"] = "name"; row["columns"][0]["value"] = name; row["columns"][0]["font"] = "SANSSERIFSMALL"; row["columns"][1]["column"] = "location"; row["columns"][1]["value"] = location; row["columns"][1]["font"] = "SANSSERIFSMALL"; row["columns"][2]["column"] = "area"; row["columns"][2]["value"] = area; row["columns"][2]["font"] = "SANSSERIFSMALL"; row["columns"][3]["column"] = "type"; row["columns"][3]["value"] = land_type; row["columns"][3]["font"] = "SANSSERIFSMALL"; // hidden is always last column row["columns"][4]["column"] = "hidden"; row["columns"][4]["value"] = hidden; mGroupParcelsp->addElement(row, ADD_SORTED); } } } //************************************* //** LLPanelGroupLandMoney Functions ** //************************************* //static void* LLPanelGroupLandMoney::createTab(void* data) { LLUUID* group_id = static_cast(data); return new LLPanelGroupLandMoney("panel group land money", *group_id); } //static LLMap LLPanelGroupLandMoney::sGroupIDs; LLPanelGroupLandMoney::LLPanelGroupLandMoney(const std::string& name, const LLUUID& group_id) : LLPanelGroupTab(name, group_id) { mImplementationp = new impl(*this, group_id); //problem what if someone has both the group floater open and the finder //open to the same group? Some maps that map group ids to panels //will then only be working for the last panel for a given group id :( LLPanelGroupLandMoney::sGroupIDs.addData(group_id, this); } LLPanelGroupLandMoney::~LLPanelGroupLandMoney() { delete mImplementationp; LLPanelGroupLandMoney::sGroupIDs.removeData(mGroupID); } void LLPanelGroupLandMoney::activate() { if ( !mImplementationp->mBeenActivated ) { //select the first tab LLTabContainer* tabp = getChild("group_money_tab_container"); if ( tabp ) { tabp->selectFirstTab(); mImplementationp->mBeenActivated = true; } setLabelArg("[CURRENCY]", gHippoGridManager->getConnectedGrid()->getCurrencySymbol()); childSetTextArg("group_money_heading", "[CURRENCY]", gHippoGridManager->getConnectedGrid()->getCurrencySymbol()); //fill in the max contribution //This calculation is unfortunately based on //the status bar's concept of how much land the user has //which can change dynamically if the user buys new land, gives //more land to a group, etc. //A race condition can occur if we want to update the UI's //concept of the user's max contribution before the status //bar has been updated from a change in the user's group contribution. //Since the max contribution should not change solely on changing //a user's group contribution, (it would only change through //purchasing of new land) this code is placed here //and only updated once to prevent the race condition //at the price of having stale data. //We need to have the status bar have observers //or find better way of distributing up to date land data. - jwolk S32 max_avail = mImplementationp->getStoredContribution(); if(gStatusBar) { max_avail += gStatusBar->getSquareMetersLeft(); } mImplementationp->setYourMaxContributionTextBox(max_avail); } update(GC_ALL); } void LLPanelGroupLandMoney::update(LLGroupChange gc) { if (gc != GC_ALL) return; //Don't update if it's the wrong panel! LLTabContainer* tabp = getChild("group_money_tab_container"); if ( tabp ) { LLPanel* panelp; LLGroupMoneyTabEventHandler* eh; panelp = tabp->getCurrentPanel(); //now pull the event handler associated with that L$ tab if ( panelp ) { eh = get_if_there(LLGroupMoneyTabEventHandler::sTabsToHandlers, panelp, (LLGroupMoneyTabEventHandler*)NULL); if ( eh ) eh->onClickTab(); } } mImplementationp->requestGroupLandInfo(); mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution()); } bool LLPanelGroupLandMoney::needsApply(std::string& mesg) { return mImplementationp->mNeedsApply; } bool LLPanelGroupLandMoney::apply(std::string& mesg) { if (!mImplementationp->applyContribution() ) { mesg = getString("land_contrib_error"); return false; } mImplementationp->mNeedsApply = false; notifyObservers(); return true; } void LLPanelGroupLandMoney::cancel() { //set the contribution back to the "stored value" mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution()); mImplementationp->mNeedsApply = false; notifyObservers(); } BOOL LLPanelGroupLandMoney::postBuild() { /* This power was removed to make group roles simpler bool has_parcel_view = gAgent.hasPowerInGroup(mGroupID, GP_LAND_VIEW_OWNED); bool has_accounting_view = gAgent.hasPowerInGroup(mGroupID, GP_ACCOUNTING_VIEW); */ bool can_view = gAgent.isInGroup(mGroupID); mImplementationp->mGroupOverLimitIconp = getChild("group_over_limit_icon"); mImplementationp->mGroupOverLimitTextp = getChild("group_over_limit_text"); mImplementationp->mYourContributionEditorp = getChild("your_contribution_line_editor"); if ( mImplementationp->mYourContributionEditorp ) { LLLineEditor* editor = mImplementationp->mYourContributionEditorp; editor->setCommitCallback(mImplementationp->contributionCommitCallback); editor->setKeystrokeCallback(mImplementationp->contributionKeystrokeCallback); editor->setCallbackUserData(this); } mImplementationp->mMapButtonp = getChild("map_button"); mImplementationp->mGroupParcelsp = getChild("group_parcel_list"); mImplementationp->mCantViewParcelsText = getString("cant_view_group_land_text"); mImplementationp->mCantViewAccountsText = getString("cant_view_group_accounting_text"); if ( mImplementationp->mMapButtonp ) { mImplementationp->mMapButtonp->setClickedCallback(LLPanelGroupLandMoney::impl::mapCallback, mImplementationp); } if ( mImplementationp->mGroupOverLimitTextp ) { mImplementationp->mGroupOverLimitTextp->setVisible(FALSE); } if ( mImplementationp->mGroupOverLimitIconp ) { mImplementationp->mGroupOverLimitIconp->setVisible(FALSE); } if ( mImplementationp->mMapButtonp ) { mImplementationp->mMapButtonp->setEnabled(FALSE); } if ( !can_view ) { if ( mImplementationp->mGroupParcelsp ) { mImplementationp->mGroupParcelsp->addCommentText( mImplementationp->mCantViewParcelsText); mImplementationp->mGroupParcelsp->setEnabled(FALSE); } } LLButton* earlierp, *laterp; LLTextEditor* textp; LLPanel* panelp; LLTabContainer* tabcp = getChild("group_money_tab_container"); if ( !can_view ) { if ( tabcp ) { S32 i; S32 tab_count = tabcp->getTabCount(); for (i = tab_count - 1; i >=0; --i) { tabcp->enableTabButton(i, false); } } } std::string loading_text = getString("loading_txt"); //pull out the widgets for the L$ details tab earlierp = getChild("earlier_details_button", true); laterp = getChild("later_details_button", true); textp = getChild("group_money_details_text", true); panelp = getChild("group_money_details_tab", true); if ( !can_view ) { textp->setText(mImplementationp->mCantViewAccountsText); } else { mImplementationp->mMoneyDetailsTabEHp = new LLGroupMoneyDetailsTabEventHandler(earlierp, laterp, textp, tabcp, panelp, loading_text, mGroupID); } textp = getChild("group_money_planning_text", true); panelp = getChild("group_money_planning_tab", true); if ( !can_view ) { textp->setText(mImplementationp->mCantViewAccountsText); } else { //Temporally disabled for DEV-11287. mImplementationp->mMoneyPlanningTabEHp = new LLGroupMoneyPlanningTabEventHandler(textp, tabcp, panelp, loading_text, mGroupID); } //pull out the widgets for the L$ sales tab earlierp = getChild("earlier_sales_button", true); laterp = getChild("later_sales_button", true); textp = getChild("group_money_sales_text", true); panelp = getChild("group_money_sales_tab", true); if ( !can_view ) { textp->setText(mImplementationp->mCantViewAccountsText); } else { mImplementationp->mMoneySalesTabEHp = new LLGroupMoneySalesTabEventHandler(earlierp, laterp, textp, tabcp, panelp, loading_text, mGroupID); } return LLPanelGroupTab::postBuild(); } BOOL LLPanelGroupLandMoney::isVisibleByAgent(LLAgent* agentp) { return mAllowEdit && agentp->isInGroup(mGroupID); } void LLPanelGroupLandMoney::processPlacesReply(LLMessageSystem* msg, void**) { LLUUID group_id; msg->getUUID("AgentData", "QueryID", group_id); LLPanelGroupLandMoney* selfp = sGroupIDs.getIfThere(group_id); if(!selfp) { llinfos << "Group Panel Land " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << ' ' << group_id << " no longer in existence." << llendl; return; } selfp->mImplementationp->processGroupLand(msg); } //************************************************* //** LLGroupMoneyTabEventHandler::impl Functions ** //************************************************* class LLGroupMoneyTabEventHandler::impl { public: impl(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLPanel* tabpanelp, const std::string& loading_text, const LLUUID& group_id, S32 interval_length_days, S32 max_interval_days); ~impl(); bool getCanClickLater(); bool getCanClickEarlier(); void updateButtons(); //member variables public: LLUUID mGroupID; LLUUID mPanelID; LLPanel* mTabPanelp; int mIntervalLength; int mMaxInterval; int mCurrentInterval; LLTextEditor* mTextEditorp; LLButton* mEarlierButtonp; LLButton* mLaterButtonp; std::string mLoadingText; }; LLGroupMoneyTabEventHandler::impl::impl(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLPanel* tabpanelp, const std::string& loading_text, const LLUUID& group_id, S32 interval_length_days, S32 max_interval_days) { mGroupID = group_id; mPanelID.generate(); mIntervalLength = interval_length_days; mMaxInterval = max_interval_days; mCurrentInterval = 0; mTextEditorp = text_editorp; mEarlierButtonp = earlier_buttonp; mLaterButtonp = later_buttonp; mTabPanelp = tabpanelp; mLoadingText = loading_text; } LLGroupMoneyTabEventHandler::impl::~impl() { } bool LLGroupMoneyTabEventHandler::impl::getCanClickEarlier() { return (mCurrentInterval < mMaxInterval); } bool LLGroupMoneyTabEventHandler::impl::getCanClickLater() { return ( mCurrentInterval > 0 ); } void LLGroupMoneyTabEventHandler::impl::updateButtons() { if ( mEarlierButtonp ) { mEarlierButtonp->setEnabled(getCanClickEarlier()); } if ( mLaterButtonp ) { mLaterButtonp->setEnabled(getCanClickLater()); } } //******************************************* //** LLGroupMoneyTabEventHandler Functions ** //******************************************* LLMap LLGroupMoneyTabEventHandler::sInstanceIDs; std::map LLGroupMoneyTabEventHandler::sTabsToHandlers; LLGroupMoneyTabEventHandler::LLGroupMoneyTabEventHandler(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id, S32 interval_length_days, S32 max_interval_days) { mImplementationp = new impl(earlier_buttonp, later_buttonp, text_editorp, panelp, loading_text, group_id, interval_length_days, max_interval_days); if ( earlier_buttonp ) { earlier_buttonp->setClickedCallback(clickEarlierCallback, this); } if ( later_buttonp ) { later_buttonp->setClickedCallback(clickLaterCallback, this); } mImplementationp->updateButtons(); if ( tab_containerp && panelp ) { tab_containerp->setTabChangeCallback(panelp, clickTabCallback); tab_containerp->setTabUserData(panelp, this); } sInstanceIDs.addData(mImplementationp->mPanelID, this); sTabsToHandlers[panelp] = this; } LLGroupMoneyTabEventHandler::~LLGroupMoneyTabEventHandler() { sInstanceIDs.removeData(mImplementationp->mPanelID); sTabsToHandlers.erase(mImplementationp->mTabPanelp); delete mImplementationp; } void LLGroupMoneyTabEventHandler::onClickTab() { requestData(gMessageSystem); } void LLGroupMoneyTabEventHandler::requestData(LLMessageSystem* msg) { //do nothing } void LLGroupMoneyTabEventHandler::processReply(LLMessageSystem* msg, void** data) { //do nothing } void LLGroupMoneyTabEventHandler::onClickEarlier() { if ( mImplementationp->mTextEditorp) { mImplementationp->mTextEditorp->setText(mImplementationp->mLoadingText); } mImplementationp->mCurrentInterval++; mImplementationp->updateButtons(); requestData(gMessageSystem); } void LLGroupMoneyTabEventHandler::onClickLater() { if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(mImplementationp->mLoadingText); } mImplementationp->mCurrentInterval--; mImplementationp->updateButtons(); requestData(gMessageSystem); } //static void LLGroupMoneyTabEventHandler::clickEarlierCallback(void* data) { LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data; if ( selfp ) selfp->onClickEarlier(); } //static void LLGroupMoneyTabEventHandler::clickLaterCallback(void* data) { LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data; if ( selfp ) selfp->onClickLater(); } //static void LLGroupMoneyTabEventHandler::clickTabCallback(void* data, bool from_click) { LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data; if ( selfp ) selfp->onClickTab(); } //************************************************** //** LLGroupMoneyDetailsTabEventHandler Functions ** //************************************************** LLGroupMoneyDetailsTabEventHandler::LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id) : LLGroupMoneyTabEventHandler(earlier_buttonp, later_buttonp, text_editorp, tab_containerp, panelp, loading_text, group_id, SUMMARY_INTERVAL, SUMMARY_MAX) { } LLGroupMoneyDetailsTabEventHandler::~LLGroupMoneyDetailsTabEventHandler() { } void LLGroupMoneyDetailsTabEventHandler::requestData(LLMessageSystem* msg) { msg->newMessageFast(_PREHASH_GroupAccountDetailsRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID ); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID ); msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength ); msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval); gAgent.sendReliableMessage(); if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(mImplementationp->mLoadingText); } LLGroupMoneyTabEventHandler::requestData(msg); } void LLGroupMoneyDetailsTabEventHandler::processReply(LLMessageSystem* msg, void** data) { LLUUID group_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id ); if (mImplementationp->mGroupID != group_id) { llwarns << "Group Account details not for this group!" << llendl; return; } std::string start_date; S32 interval_days; S32 current_interval; msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval ); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, start_date); if ( interval_days != mImplementationp->mIntervalLength || current_interval != mImplementationp->mCurrentInterval ) { llinfos << "Out of date details packet " << interval_days << " " << current_interval << llendl; return; } std::string text = start_date; text.append("\n\n"); S32 total_amount = 0; S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData); for(S32 i = 0; i < transactions; i++) { S32 amount = 0; std::string desc; msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Description, desc, i ); msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i); if (amount != 0) { text.append(llformat("%-24s %6d\n", desc.c_str(), amount)); } else { // skip it } total_amount += amount; } text.append(1, '\n'); text.append(llformat("%-24s %6d\n", "Total", total_amount)); if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(text); } } //static void LLPanelGroupLandMoney::processGroupAccountDetailsReply(LLMessageSystem* msg, void** data) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) { llwarns << "Got group " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << " history reply for another agent!" << llendl; return; } LLUUID request_id; msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id ); LLGroupMoneyTabEventHandler* selfp = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id); if (!selfp) { llwarns << "GroupAccountDetails recieved for non-existent group panel." << llendl; return; } selfp->processReply(msg, data); } //************************************************ //** LLGroupMoneySalesTabEventHandler Functions ** //************************************************ LLGroupMoneySalesTabEventHandler::LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp, LLButton* later_buttonp, LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id) : LLGroupMoneyTabEventHandler(earlier_buttonp, later_buttonp, text_editorp, tab_containerp, panelp, loading_text, group_id, SUMMARY_INTERVAL, SUMMARY_MAX) { } LLGroupMoneySalesTabEventHandler::~LLGroupMoneySalesTabEventHandler() { } void LLGroupMoneySalesTabEventHandler::requestData(LLMessageSystem* msg) { msg->newMessageFast(_PREHASH_GroupAccountTransactionsRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID ); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID ); msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength ); msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval); gAgent.sendReliableMessage(); if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(mImplementationp->mLoadingText); } LLGroupMoneyTabEventHandler::requestData(msg); } void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg, void** data) { LLUUID group_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id ); if (mImplementationp->mGroupID != group_id) { llwarns << "Group Account Transactions not for this group!" << llendl; return; } std::string text = mImplementationp->mTextEditorp->getText(); std::string start_date; S32 interval_days; S32 current_interval; msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval ); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, start_date); if (interval_days != mImplementationp->mIntervalLength || current_interval != mImplementationp->mCurrentInterval) { llinfos << "Out of date details packet " << interval_days << " " << current_interval << llendl; return; } // If this is the first packet, clear the text, don't append. // Start with the date. if (text == mImplementationp->mLoadingText) { text = start_date + "\n\n"; } S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData); if (transactions == 0) { text.append("(none)"); } else { for(S32 i = 0; i < transactions; i++) { std::string time; S32 type = 0; S32 amount = 0; std::string user; std::string item; msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Time, time, i); msg->getStringFast(_PREHASH_HistoryData, _PREHASH_User, user, i ); msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Type, type, i); msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Item, item, i ); msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i); if (amount != 0) { std::string verb; switch(type) { case TRANS_OBJECT_SALE: verb = "bought"; break; case TRANS_GIFT: verb = "paid you"; break; case TRANS_PAY_OBJECT: verb = "paid into"; break; case TRANS_LAND_PASS_SALE: verb = "bought pass to"; break; case TRANS_EVENT_FEE: verb = "paid fee for event"; break; case TRANS_EVENT_PRIZE: verb = "paid prize for event"; break; default: verb = ""; break; } std::string line = llformat("%s %6d - %s %s %s\n", time.c_str(), amount, user.c_str(), verb.c_str(), item.c_str()); text.append(line); } } } if ( mImplementationp->mTextEditorp) { mImplementationp->mTextEditorp->setText(text); } } //static void LLPanelGroupLandMoney::processGroupAccountTransactionsReply(LLMessageSystem* msg, void** data) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) { llwarns << "Got group " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << " history reply for another agent!" << llendl; return; } LLUUID request_id; msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id ); LLGroupMoneyTabEventHandler* self; self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id); if (!self) { llwarns << "GroupAccountTransactions recieved for non-existent group panel." << llendl; return; } self->processReply(msg, data); } //*************************************************** //** LLGroupMoneyPlanningTabEventHandler Functions ** //*************************************************** LLGroupMoneyPlanningTabEventHandler::LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editorp, LLTabContainer* tab_containerp, LLPanel* panelp, const std::string& loading_text, const LLUUID& group_id) : LLGroupMoneyTabEventHandler(NULL, NULL, text_editorp, tab_containerp, panelp, loading_text, group_id, SUMMARY_INTERVAL, SUMMARY_MAX) { } LLGroupMoneyPlanningTabEventHandler::~LLGroupMoneyPlanningTabEventHandler() { } void LLGroupMoneyPlanningTabEventHandler::requestData(LLMessageSystem* msg) { msg->newMessageFast(_PREHASH_GroupAccountSummaryRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID ); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID ); msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength); msg->addS32Fast(_PREHASH_CurrentInterval, 0); //planning has 0 interval gAgent.sendReliableMessage(); if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(mImplementationp->mLoadingText); } LLGroupMoneyTabEventHandler::requestData(msg); } void LLGroupMoneyPlanningTabEventHandler::processReply(LLMessageSystem* msg, void** data) { LLUUID group_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id ); if (mImplementationp->mGroupID != group_id) { llwarns << "Group Account Summary received not for this group!" << llendl; return; } std::string text; std::string start_date; std::string last_stipend_date; std::string next_stipend_date; S32 interval_days; S32 current_interval; S32 balance; S32 total_credits; S32 total_debits; S32 cur_object_tax; S32 cur_light_tax; S32 cur_land_tax; S32 cur_group_tax; S32 cur_parcel_dir_fee; S32 cur_total_tax; S32 proj_object_tax; S32 proj_light_tax; S32 proj_land_tax; S32 proj_group_tax; S32 proj_parcel_dir_fee; S32 proj_total_tax; S32 non_exempt_members; msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_Balance, balance ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalCredits, total_credits ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalDebits, total_debits ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxCurrent, cur_object_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxCurrent, cur_light_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxCurrent, cur_land_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxCurrent, cur_group_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeCurrent, cur_parcel_dir_fee ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxEstimate, proj_object_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxEstimate, proj_light_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxEstimate, proj_land_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxEstimate, proj_group_tax ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeEstimate, proj_parcel_dir_fee ); msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_NonExemptMembers, non_exempt_members ); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, start_date); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_LastTaxDate, last_stipend_date); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_TaxDate, next_stipend_date); cur_total_tax = cur_object_tax + cur_light_tax + cur_land_tax + cur_group_tax + cur_parcel_dir_fee; proj_total_tax = proj_object_tax + proj_light_tax + proj_land_tax + proj_group_tax + proj_parcel_dir_fee; if (interval_days != mImplementationp->mIntervalLength || current_interval != mImplementationp->mCurrentInterval) { llinfos << "Out of date summary packet " << interval_days << " " << current_interval << llendl; return; } text.append("Summary for this week, beginning on "); text.append(start_date); if (current_interval == 0) { text.append("The next stipend day is "); text.append(next_stipend_date); text.append("\n\n"); text.append(llformat("%-24s%s%6d\n", "Balance", gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), balance)); text.append(1, '\n'); } // [DEV-29503] Hide the individual info since // non_exempt_member here is a wrong choice to calculate individual shares. // text.append( " Group Individual Share\n"); // text.append(llformat( "%-24s %6d %6d \n", "Credits", total_credits, (S32)floor((F32)total_credits/(F32)non_exempt_members))); // text.append(llformat( "%-24s %6d %6d \n", "Debits", total_debits, (S32)floor((F32)total_debits/(F32)non_exempt_members))); // text.append(llformat( "%-24s %6d %6d \n", "Total", total_credits + total_debits, (S32)floor((F32)(total_credits + total_debits)/(F32)non_exempt_members))); text.append( " Group\n"); text.append(llformat( "%-24s %6d\n", "Credits", total_credits)); text.append(llformat( "%-24s %6d\n", "Debits", total_debits)); text.append(llformat( "%-24s %6d\n", "Total", total_credits + total_debits)); if ( mImplementationp->mTextEditorp ) { mImplementationp->mTextEditorp->setText(text); } } //static void LLPanelGroupLandMoney::processGroupAccountSummaryReply(LLMessageSystem* msg, void** data) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) { llwarns << "Got group " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << " history reply for another agent!" << llendl; return; } LLUUID request_id; msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id ); LLGroupMoneyTabEventHandler* self; self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id); if (!self) { llwarns << "GroupAccountSummary recieved for non-existent group " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << " planning tab." << llendl; return; } self->processReply(msg, data); }