/**
 * @file llfloaterland.cpp
 * @brief "About Land" floater, allowing display and editing of land parcel properties.
 *
 * $LicenseInfo:firstyear=2002&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 <sstream>
#include <time.h>

#include "llfloaterland.h"

#include "llavatarnamecache.h"
#include "llfocusmgr.h"
#include "llnotificationsutil.h"
#include "llparcel.h"
#include "message.h"

#include "llagent.h"
#include "llagentaccess.h"
#include "llappviewer.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llfloaterreg.h"
#include "llfloateravatarpicker.h"
#include "llfloaterauction.h"
#include "llfloaterbanduration.h"
#include "llfloatergroups.h"
#include "llfloaterscriptlimits.h"
#include "llavataractions.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
#include "llpanellandaudio.h"
#include "llpanellandmedia.h"
#include "llradiogroup.h"
#include "llresmgr.h"                   // getMonetaryString
#include "llscrolllistctrl.h"
#include "llscrolllistitem.h"
#include "llscrolllistcell.h"
#include "llselectmgr.h"
#include "llslurl.h"
#include "llspinctrl.h"
#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltexturectrl.h"
#include "lluiconstants.h"
#include "lluictrlfactory.h"
#include "llviewertexturelist.h"        // LLUIImageList
#include "llviewermessage.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewertexteditor.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
#include "roles_constants.h"
#include "lltrans.h"
#include "llpanelexperiencelisteditor.h"
#include "llpanelexperiencepicker.h"
#include "llpanelenvironment.h"
#include "llexperiencecache.h"

#include "llgroupactions.h"
#include "llenvironment.h"

const F64 COVENANT_REFRESH_TIME_SEC = 60.0f;

static std::string OWNER_ONLINE     = "0";
static std::string OWNER_OFFLINE    = "1";
static std::string OWNER_GROUP      = "2";
static std::string MATURITY         = "[MATURITY]";

// constants used in callbacks below - syntactic sugar.
static const bool BUY_GROUP_LAND = true;
static const bool BUY_PERSONAL_LAND = false;
LLPointer<LLParcelSelection> LLPanelLandGeneral::sSelectionForBuyPass = NULL;

// Statics
LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
S32 LLFloaterLand::sLastTab = 0;

// Local classes
class LLParcelSelectionObserver : public LLParcelObserver
{
public:
    virtual void changed() { LLFloaterLand::refreshAll(); }
};

// class needed to get full access to textbox inside checkbox, because LLCheckBoxCtrl::setLabel() has string as its argument.
// It was introduced while implementing EXT-4706
class LLCheckBoxWithTBAcess : public LLCheckBoxCtrl
{
public:
    LLTextBox* getTextBox()
    {
        return mLabel;
    }
};


class LLPanelLandExperiences
    :   public LLPanel
{
public:
    LLPanelLandExperiences(LLSafeHandle<LLParcelSelection>& parcelp);
    virtual bool postBuild();
    void refresh();

    void experienceAdded(const LLUUID& id, U32 xp_type, U32 access_type);
    void experienceRemoved(const LLUUID& id, U32 access_type);
protected:
    LLPanelExperienceListEditor* setupList( const char* control_name, U32 xp_type, U32 access_type );
    void refreshPanel(LLPanelExperienceListEditor* panel, U32 xp_type);

    LLSafeHandle<LLParcelSelection>&    mParcel;


    LLPanelExperienceListEditor* mAllowed;
    LLPanelExperienceListEditor* mBlocked;
};


class LLPanelLandEnvironment
    : public LLPanelEnvironmentInfo
{
public:
                        LLPanelLandEnvironment(LLSafeHandle<LLParcelSelection>& parcelp);

    virtual bool        isRegion() const override { return false; }
    virtual bool        isLargeEnough() override
    {
        LLParcel *parcelp = mParcel->getParcel();
        return ((parcelp) ? (parcelp->getArea() >= MINIMUM_PARCEL_SIZE) : false);
    }

    virtual bool        postBuild() override;
    virtual void        refresh() override;

    virtual LLParcel *  getParcel() override;

    virtual bool        canEdit() override;
    virtual S32         getParcelId() override;

protected:
    virtual void        refreshFromSource() override;

    bool                isSameRegion();

    LLSafeHandle<LLParcelSelection> &   mParcel;
    S32                 mLastParcelId;
};


// inserts maturity info(icon and text) into target textbox
// names_floater - pointer to floater which contains strings with maturity icons filenames
// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY]
void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse);

//---------------------------------------------------------------------------
// LLFloaterLand
//---------------------------------------------------------------------------

void send_parcel_select_objects(S32 parcel_local_id, U32 return_type,
                                uuid_list_t* return_ids = NULL)
{
    LLMessageSystem *msg = gMessageSystem;

    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if (!region) return;

    // Since new highlight will be coming in, drop any highlights
    // that exist right now.
    LLSelectMgr::getInstance()->unhighlightAll();

    msg->newMessageFast(_PREHASH_ParcelSelectObjects);
    msg->nextBlockFast(_PREHASH_AgentData);
    msg->addUUIDFast(_PREHASH_AgentID,  gAgent.getID());
    msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
    msg->nextBlockFast(_PREHASH_ParcelData);
    msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
    msg->addU32Fast(_PREHASH_ReturnType, return_type);

    // Throw all return ids into the packet.
    // TODO: Check for too many ids.
    if (return_ids)
    {
        uuid_list_t::iterator end = return_ids->end();
        for (uuid_list_t::iterator it = return_ids->begin();
             it != end;
             ++it)
        {
            msg->nextBlockFast(_PREHASH_ReturnIDs);
            msg->addUUIDFast(_PREHASH_ReturnID, (*it));
        }
    }
    else
    {
        // Put in a null key so that the message is complete.
        msg->nextBlockFast(_PREHASH_ReturnIDs);
        msg->addUUIDFast(_PREHASH_ReturnID, LLUUID::null);
    }

    msg->sendReliable(region->getHost());
}

LLParcel* LLFloaterLand::getCurrentSelectedParcel()
{
    return mParcel->getParcel();
};

//static
LLPanelLandObjects* LLFloaterLand::getCurrentPanelLandObjects()
{
    LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
    if(land_instance)
    {
        return land_instance->mPanelObjects;
    }
    else
    {
        return NULL;
    }
}

//static
LLPanelLandCovenant* LLFloaterLand::getCurrentPanelLandCovenant()
{
    LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
    if(land_instance)
    {
        return land_instance->mPanelCovenant;
    }
    else
    {
        return NULL;
    }
}

// static
void LLFloaterLand::refreshAll()
{
    LLFloaterLand* land_instance = LLFloaterReg::findTypedInstance<LLFloaterLand>("about_land");
    if(land_instance)
    {
        land_instance->refresh();
    }
}

void LLFloaterLand::onOpen(const LLSD& key)
{
    // moved from triggering show instance in llviwermenu.cpp

    if (LLViewerParcelMgr::getInstance()->selectionEmpty())
    {
        LLViewerParcelMgr::getInstance()->selectParcelAt(gAgent.getPositionGlobal());
    }

    // Done automatically when the selected parcel's properties arrive
    // (and hence we have the local id).
    // LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_RENTER);

    mParcel = LLViewerParcelMgr::getInstance()->getFloatingParcelSelection();

    // Refresh even if not over a region so we don't get an
    // uninitialized dialog. The dialog is 0-region aware.
    refresh();
}

void LLFloaterLand::onVisibilityChanged(const LLSD& visible)
{
    if (!visible.asBoolean())
    {
        // Might have been showing owned objects
        LLSelectMgr::getInstance()->unhighlightAll();

        // Save which panel we had open
        sLastTab = mTabLand->getCurrentPanelIndex();
    }
}


LLFloaterLand::LLFloaterLand(const LLSD& seed)
:   LLFloater(seed)
{
    mFactoryMap["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
    mFactoryMap["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
    mFactoryMap["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this);
    mFactoryMap["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this);
    mFactoryMap["land_audio_panel"] =   LLCallbackMap(createPanelLandAudio, this);
    mFactoryMap["land_media_panel"] =   LLCallbackMap(createPanelLandMedia, this);
    mFactoryMap["land_access_panel"] =  LLCallbackMap(createPanelLandAccess, this);
    mFactoryMap["land_experiences_panel"] = LLCallbackMap(createPanelLandExperiences, this);
    mFactoryMap["land_environment_panel"] = LLCallbackMap(createPanelLandEnvironment, this);

    sObserver = new LLParcelSelectionObserver();
    LLViewerParcelMgr::getInstance()->addObserver( sObserver );
}

bool LLFloaterLand::postBuild()
{
    setVisibleCallback(boost::bind(&LLFloaterLand::onVisibilityChanged, this, _2));

    LLTabContainer* tab = getChild<LLTabContainer>("landtab");

    mTabLand = (LLTabContainer*) tab;

    if (tab)
    {
        tab->selectTab(sLastTab);
    }

    return true;
}


// virtual
LLFloaterLand::~LLFloaterLand()
{
    LLViewerParcelMgr::getInstance()->removeObserver( sObserver );
    delete sObserver;
    sObserver = NULL;
}

// public
void LLFloaterLand::refresh()
{
    mPanelGeneral->refresh();
    mPanelObjects->refresh();
    mPanelOptions->refresh();
    mPanelAudio->refresh();
    mPanelMedia->refresh();
    mPanelAccess->refresh();
    mPanelCovenant->refresh();
    mPanelExperiences->refresh();
    mPanelEnvironment->refresh();
}



void* LLFloaterLand::createPanelLandGeneral(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelGeneral = new LLPanelLandGeneral(self->mParcel);
    return self->mPanelGeneral;
}

// static
void* LLFloaterLand::createPanelLandCovenant(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelCovenant = new LLPanelLandCovenant(self->mParcel);
    return self->mPanelCovenant;
}


// static
void* LLFloaterLand::createPanelLandObjects(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelObjects = new LLPanelLandObjects(self->mParcel);
    return self->mPanelObjects;
}

// static
void* LLFloaterLand::createPanelLandOptions(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelOptions = new LLPanelLandOptions(self->mParcel);
    return self->mPanelOptions;
}

// static
void* LLFloaterLand::createPanelLandAudio(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelAudio = new LLPanelLandAudio(self->mParcel);
    return self->mPanelAudio;
}

// static
void* LLFloaterLand::createPanelLandMedia(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelMedia = new LLPanelLandMedia(self->mParcel);
    return self->mPanelMedia;
}

// static
void* LLFloaterLand::createPanelLandAccess(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelAccess = new LLPanelLandAccess(self->mParcel);
    return self->mPanelAccess;
}

// static
void* LLFloaterLand::createPanelLandExperiences(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelExperiences = new LLPanelLandExperiences(self->mParcel);
    return self->mPanelExperiences;
}

//static
void* LLFloaterLand::createPanelLandEnvironment(void* data)
{
    LLFloaterLand* self = (LLFloaterLand*)data;
    self->mPanelEnvironment = new LLPanelLandEnvironment(self->mParcel);
    return self->mPanelEnvironment;
}


//---------------------------------------------------------------------------
// LLPanelLandGeneral
//---------------------------------------------------------------------------


LLPanelLandGeneral::LLPanelLandGeneral(LLParcelSelectionHandle& parcel)
:   LLPanel(),
    mUncheckedSell(false),
    mParcel(parcel)
{
}

bool LLPanelLandGeneral::postBuild()
{
    mEditName = getChild<LLLineEditor>("Name");
    mEditName->setCommitCallback(onCommitAny, this);
    getChild<LLLineEditor>("Name")->setPrevalidate(LLTextValidate::validateASCIIPrintableNoPipe);

    mEditDesc = getChild<LLTextEditor>("Description");
    mEditDesc->setCommitOnFocusLost(true);
    mEditDesc->setCommitCallback(onCommitAny, this);
    mEditDesc->setContentTrusted(false);
    // No prevalidate function - historically the prevalidate function was broken,
    // allowing residents to put in characters like U+2661 WHITE HEART SUIT, so
    // preserve that ability.

    mTextSalePending = getChild<LLTextBox>("SalePending");
    mTextOwnerLabel = getChild<LLTextBox>("Owner:");
    mTextOwner = getChild<LLTextBox>("OwnerText");
    mTextOwner->setIsFriendCallback(LLAvatarActions::isFriend);

    mContentRating = getChild<LLTextBox>("ContentRatingText");
    mLandType = getChild<LLTextBox>("LandTypeText");

    mBtnProfile = getChild<LLButton>("Profile...");
    mBtnProfile->setClickedCallback(boost::bind(&LLPanelLandGeneral::onClickProfile, this));


    mTextGroupLabel = getChild<LLTextBox>("Group:");
    mTextGroup = getChild<LLTextBox>("GroupText");


    mBtnSetGroup = getChild<LLButton>("Set...");
    mBtnSetGroup->setCommitCallback(boost::bind(&LLPanelLandGeneral::onClickSetGroup, this));


    mCheckDeedToGroup = getChild<LLCheckBoxCtrl>( "check deed");
    childSetCommitCallback("check deed", onCommitAny, this);


    mBtnDeedToGroup = getChild<LLButton>("Deed...");
    mBtnDeedToGroup->setClickedCallback(onClickDeed, this);


    mCheckContributeWithDeed = getChild<LLCheckBoxCtrl>( "check contrib");
    childSetCommitCallback("check contrib", onCommitAny, this);



    mSaleInfoNotForSale = getChild<LLTextBox>("Not for sale.");

    mSaleInfoForSale1 = getChild<LLTextBox>("For Sale: Price L$[PRICE].");


    mBtnSellLand = getChild<LLButton>("Sell Land...");
    mBtnSellLand->setClickedCallback(onClickSellLand, this);

    mSaleInfoForSale2 = getChild<LLTextBox>("For sale to");

    mSaleInfoForSaleObjects = getChild<LLTextBox>("Sell with landowners objects in parcel.");

    mSaleInfoForSaleNoObjects = getChild<LLTextBox>("Selling with no objects in parcel.");


    mBtnStopSellLand = getChild<LLButton>("Cancel Land Sale");
    mBtnStopSellLand->setClickedCallback(onClickStopSellLand, this);


    mTextClaimDateLabel = getChild<LLTextBox>("Claimed:");
    mTextClaimDate = getChild<LLTextBox>("DateClaimText");


    mTextPriceLabel = getChild<LLTextBox>("PriceLabel");
    mTextPrice = getChild<LLTextBox>("PriceText");


    mTextDwell = getChild<LLTextBox>("DwellText");

    mBtnBuyLand = getChild<LLButton>("Buy Land...");
    mBtnBuyLand->setClickedCallback(onClickBuyLand, (void*)&BUY_PERSONAL_LAND);


    mBtnBuyGroupLand = getChild<LLButton>("Buy For Group...");
    mBtnBuyGroupLand->setClickedCallback(onClickBuyLand, (void*)&BUY_GROUP_LAND);


    mBtnBuyPass = getChild<LLButton>("Buy Pass...");
    mBtnBuyPass->setClickedCallback(onClickBuyPass, this);

    mBtnReleaseLand = getChild<LLButton>("Abandon Land...");
    mBtnReleaseLand->setClickedCallback(onClickRelease, NULL);

    mBtnReclaimLand = getChild<LLButton>("Reclaim Land...");
    mBtnReclaimLand->setClickedCallback(onClickReclaim, NULL);

    mBtnStartAuction = getChild<LLButton>("Linden Sale...");
    mBtnStartAuction->setClickedCallback(onClickStartAuction, this);

    mBtnScriptLimits = getChild<LLButton>("Scripts...");

    if(gDisconnected)
    {
        return true;
    }

    // note: on region change this will not be re checked, should not matter on Agni as
    // 99% of the time all regions will return the same caps. In case of an erroneous setting
    // to enabled the floater will just throw an error when trying to get it's cap
    std::string url = gAgent.getRegionCapability("LandResources");
    if (!url.empty())
    {
        if(mBtnScriptLimits)
        {
            mBtnScriptLimits->setClickedCallback(onClickScriptLimits, this);
        }
    }
    else
    {
        if(mBtnScriptLimits)
        {
            mBtnScriptLimits->setVisible(false);
        }
    }

    return true;
}


// virtual
LLPanelLandGeneral::~LLPanelLandGeneral()
{ }


// public
void LLPanelLandGeneral::refresh()
{
    mEditName->setEnabled(false);
    mEditName->setText(LLStringUtil::null);

    mEditDesc->setEnabled(false);
    mEditDesc->setText(getString("no_selection_text"));

    mTextSalePending->setText(LLStringUtil::null);
    mTextSalePending->setEnabled(false);

    mBtnDeedToGroup->setEnabled(false);
    mBtnSetGroup->setEnabled(false);
    mBtnStartAuction->setEnabled(false);

    mCheckDeedToGroup   ->set(false);
    mCheckDeedToGroup   ->setEnabled(false);
    mCheckContributeWithDeed->set(false);
    mCheckContributeWithDeed->setEnabled(false);

    mTextOwner->setText(LLStringUtil::null);
    mContentRating->setText(LLStringUtil::null);
    mLandType->setText(LLStringUtil::null);
    mBtnProfile->setLabel(getString("profile_text"));
    mBtnProfile->setEnabled(false);

    mTextClaimDate->setText(LLStringUtil::null);
    mTextGroup->setText(LLStringUtil::null);
    mTextPrice->setText(LLStringUtil::null);

    mSaleInfoForSale1->setVisible(false);
    mSaleInfoForSale2->setVisible(false);
    mSaleInfoForSaleObjects->setVisible(false);
    mSaleInfoForSaleNoObjects->setVisible(false);
    mSaleInfoNotForSale->setVisible(false);
    mBtnSellLand->setVisible(false);
    mBtnStopSellLand->setVisible(false);

    mTextPriceLabel->setText(LLStringUtil::null);
    mTextDwell->setText(LLStringUtil::null);

    mBtnBuyLand->setEnabled(false);
    mBtnScriptLimits->setEnabled(false);
    mBtnBuyGroupLand->setEnabled(false);
    mBtnReleaseLand->setEnabled(false);
    mBtnReclaimLand->setEnabled(false);
    mBtnBuyPass->setEnabled(false);

    if(gDisconnected)
    {
        return;
    }

    mBtnStartAuction->setVisible(gAgent.isGodlike());
    bool region_owner = false;
    LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if(regionp && (regionp->getOwner() == gAgent.getID()))
    {
        region_owner = true;
        mBtnReleaseLand->setVisible(false);
        mBtnReclaimLand->setVisible(true);
    }
    else
    {
        mBtnReleaseLand->setVisible(true);
        mBtnReclaimLand->setVisible(false);
    }
    LLParcel *parcel = mParcel->getParcel();
    if (parcel)
    {
        // something selected, hooray!
        bool is_leased = (LLParcel::OS_LEASED == parcel->getOwnershipStatus());
        bool region_xfer = false;
        if(regionp
           && !(regionp->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL)))
        {
            region_xfer = true;
        }

        if (regionp)
        {
            insert_maturity_into_textbox(mContentRating, gFloaterView->getParentFloater(this), MATURITY);
            mLandType->setText(regionp->getLocalizedSimProductName());
        }

        // estate owner/manager cannot edit other parts of the parcel
        bool estate_manager_sellable = !parcel->getAuctionID()
                                        && gAgent.canManageEstate()
                                        // estate manager/owner can only sell parcels owned by estate owner
                                        && regionp
                                        && (parcel->getOwnerID() == regionp->getOwner());
        bool owner_sellable = region_xfer && !parcel->getAuctionID()
                            && LLViewerParcelMgr::isParcelModifiableByAgent(
                                        parcel, GP_LAND_SET_SALE_INFO);
        bool can_be_sold = owner_sellable || estate_manager_sellable;

        const LLUUID &owner_id = parcel->getOwnerID();
        bool is_public = parcel->isPublic();

        // Is it owned?
        if (is_public)
        {
            mTextSalePending->setText(LLStringUtil::null);
            mTextSalePending->setEnabled(false);
            mTextOwner->setText(getString("public_text"));
            mTextOwner->setEnabled(false);
            mBtnProfile->setEnabled(false);
            mTextClaimDate->setText(LLStringUtil::null);
            mTextClaimDate->setEnabled(false);
            mTextGroup->setText(getString("none_text"));
            mTextGroup->setEnabled(false);
            mBtnStartAuction->setEnabled(false);
        }
        else
        {
            if(!is_leased && (owner_id == gAgent.getID()))
            {
                mTextSalePending->setText(getString("need_tier_to_modify"));
                mTextSalePending->setEnabled(true);
            }
            else if(parcel->getAuctionID())
            {
                mTextSalePending->setText(getString("auction_id_text"));
                mTextSalePending->setTextArg("[ID]", llformat("%u", parcel->getAuctionID()));
                mTextSalePending->setEnabled(true);
            }
            else
            {
                // not the owner, or it is leased
                mTextSalePending->setText(LLStringUtil::null);
                mTextSalePending->setEnabled(false);
            }
            //refreshNames();
            mTextOwner->setEnabled(true);

            // We support both group and personal profiles
            mBtnProfile->setEnabled(true);

            if (parcel->getGroupID().isNull())
            {
                // Not group owned, so "Profile"
                mBtnProfile->setLabel(getString("profile_text"));

                mTextGroup->setText(getString("none_text"));
                mTextGroup->setEnabled(false);
            }
            else
            {
                // Group owned, so "Info"
                mBtnProfile->setLabel(getString("info_text"));

                //mTextGroup->setText("HIPPOS!");//parcel->getGroupName());
                mTextGroup->setEnabled(true);
            }

            // Display claim date
            time_t claim_date = parcel->getClaimDate();
            std::string claim_date_str = getString("time_stamp_template");
            LLSD substitution;
            substitution["datetime"] = (S32) claim_date;
            LLStringUtil::format (claim_date_str, substitution);
            mTextClaimDate->setText(claim_date_str);
            mTextClaimDate->setEnabled(is_leased);

            bool enable_auction = (gAgent.getGodLevel() >= GOD_LIAISON)
                                  && (owner_id == GOVERNOR_LINDEN_ID)
                                  && (parcel->getAuctionID() == 0);
            mBtnStartAuction->setEnabled(enable_auction);
        }

        // Display options
        bool can_edit_identity = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_IDENTITY);
        mEditName->setEnabled(can_edit_identity);
        mEditDesc->setEnabled(can_edit_identity);
        mEditDesc->setParseURLs(!can_edit_identity);

        bool can_edit_agent_only = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_NO_POWERS);
        mBtnSetGroup->setEnabled(can_edit_agent_only && !parcel->getIsGroupOwned());

        const LLUUID& group_id = parcel->getGroupID();

        // Can only allow deeding if you own it and it's got a group.
        bool enable_deed = (owner_id == gAgent.getID()
                            && group_id.notNull()
                            && gAgent.isInGroup(group_id));
        // You don't need special powers to allow your object to
        // be deeded to the group.
        mCheckDeedToGroup->setEnabled(enable_deed);
        mCheckDeedToGroup->set( parcel->getAllowDeedToGroup() );
        mCheckContributeWithDeed->setEnabled(enable_deed && parcel->getAllowDeedToGroup());
        mCheckContributeWithDeed->set(parcel->getContributeWithDeed());

        // Actually doing the deeding requires you to have GP_LAND_DEED
        // powers in the group.
        bool can_deed = gAgent.hasPowerInGroup(group_id, GP_LAND_DEED);
        mBtnDeedToGroup->setEnabled(   parcel->getAllowDeedToGroup()
                                    && group_id.notNull()
                                    && can_deed
                                    && !parcel->getIsGroupOwned()
                                    );

        mEditName->setText( parcel->getName() );
        mEditDesc->setText( parcel->getDesc() );

        bool for_sale = parcel->getForSale();

        mBtnSellLand->setVisible(false);
        mBtnStopSellLand->setVisible(false);

        // show pricing information
        S32 area;
        S32 claim_price;
        S32 rent_price;
        F32 dwell = DWELL_NAN;
        LLViewerParcelMgr::getInstance()->getDisplayInfo(&area,
                                 &claim_price,
                                 &rent_price,
                                 &for_sale,
                                 &dwell);
        // Area
        LLUIString price = getString("area_size_text");
        price.setArg("[AREA]", llformat("%d",area));
        mTextPriceLabel->setText(getString("area_text"));
        mTextPrice->setText(price.getString());

        if (dwell == DWELL_NAN)
        {
            mTextDwell->setText(LLTrans::getString("LoadingData"));
        }
        else
        {
            mTextDwell->setText(llformat("%.0f", dwell));
        }

        if (for_sale)
        {
            mSaleInfoForSale1->setVisible(true);
            mSaleInfoForSale2->setVisible(true);
            if (parcel->getSellWithObjects())
            {
                mSaleInfoForSaleObjects->setVisible(true);
                mSaleInfoForSaleNoObjects->setVisible(false);
            }
            else
            {
                mSaleInfoForSaleObjects->setVisible(false);
                mSaleInfoForSaleNoObjects->setVisible(true);
            }
            mSaleInfoNotForSale->setVisible(false);

            F32 cost_per_sqm = 0.0f;
            if (area > 0)
            {
                cost_per_sqm = (F32)parcel->getSalePrice() / (F32)area;
            }

            S32 price = parcel->getSalePrice();
            mSaleInfoForSale1->setTextArg("[PRICE]", LLResMgr::getInstance()->getMonetaryString(price));
            mSaleInfoForSale1->setTextArg("[PRICE_PER_SQM]", llformat("%.1f", cost_per_sqm));
            if (can_be_sold)
            {
                mBtnStopSellLand->setVisible(true);
            }
        }
        else
        {
            mSaleInfoForSale1->setVisible(false);
            mSaleInfoForSale2->setVisible(false);
            mSaleInfoForSaleObjects->setVisible(false);
            mSaleInfoForSaleNoObjects->setVisible(false);
            mSaleInfoNotForSale->setVisible(true);
            if (can_be_sold)
            {
                mBtnSellLand->setVisible(true);
            }
        }

        refreshNames();

        mBtnBuyLand->setEnabled(
            LLViewerParcelMgr::getInstance()->canAgentBuyParcel(parcel, false));
        mBtnScriptLimits->setEnabled(true);
//          LLViewerParcelMgr::getInstance()->canAgentBuyParcel(parcel, false));
        mBtnBuyGroupLand->setEnabled(
            LLViewerParcelMgr::getInstance()->canAgentBuyParcel(parcel, true));

        if(region_owner)
        {
            mBtnReclaimLand->setEnabled(
                !is_public && (parcel->getOwnerID() != gAgent.getID()));
        }
        else
        {
            bool is_owner_release = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_RELEASE);
            bool is_manager_release = (gAgent.canManageEstate() &&
                                    regionp &&
                                    (parcel->getOwnerID() != regionp->getOwner()));
            bool can_release = is_owner_release || is_manager_release;
            mBtnReleaseLand->setEnabled( can_release );
        }

        bool use_pass = parcel->getOwnerID()!= gAgent.getID() && parcel->getParcelFlag(PF_USE_PASS_LIST) && !LLViewerParcelMgr::getInstance()->isCollisionBanned();;
        mBtnBuyPass->setEnabled(use_pass);

    }
}

// public
void LLPanelLandGeneral::refreshNames()
{
    LLParcel *parcel = mParcel->getParcel();
    if (!parcel)
    {
        mTextOwner->setText(LLStringUtil::null);
        mTextGroup->setText(LLStringUtil::null);
        return;
    }

    std::string owner;
    if (parcel->getIsGroupOwned())
    {
        owner = getString("group_owned_text");
    }
    else
    {
        // Figure out the owner's name
        owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString();
    }

    if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus())
    {
        owner += getString("sale_pending_text");
    }
    mTextOwner->setText(owner);

    std::string group;
    if (!parcel->getGroupID().isNull())
    {
        group = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString();
    }
    mTextGroup->setText(group);

    if (parcel->getForSale())
    {
        const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
        if(auth_buyer_id.notNull())
        {
          std::string name;
          name = LLSLURL("agent", auth_buyer_id, "inspect").getSLURLString();
          mSaleInfoForSale2->setTextArg("[BUYER]", name);
        }
        else
        {
            mSaleInfoForSale2->setTextArg("[BUYER]", getString("anyone"));
        }
    }
}


// virtual
void LLPanelLandGeneral::draw()
{
    LLPanel::draw();
}

void LLPanelLandGeneral::onClickSetGroup()
{
    LLFloater* parent_floater = gFloaterView->getParentFloater(this);

    LLFloaterGroupPicker* fg =  LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID()));
    if (fg)
    {
        fg->setSelectGroupCallback( boost::bind(&LLPanelLandGeneral::setGroup, this, _1 ));
        if (parent_floater)
        {
            LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
            fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
            parent_floater->addDependentFloater(fg);
        }
    }
}

void LLPanelLandGeneral::onClickProfile()
{
    LLParcel* parcel = mParcel->getParcel();
    if (!parcel) return;

    if (parcel->getIsGroupOwned())
    {
        const LLUUID& group_id = parcel->getGroupID();
        LLGroupActions::show(group_id);
    }
    else
    {
        const LLUUID& avatar_id = parcel->getOwnerID();
        LLAvatarActions::showProfile(avatar_id);
    }
}

// public
void LLPanelLandGeneral::setGroup(const LLUUID& group_id)
{
    LLParcel* parcel = mParcel->getParcel();
    if (!parcel) return;

    // Set parcel properties and send message
    parcel->setGroupID(group_id);
    //parcel->setGroupName(group_name);
    //mTextGroup->setText(group_name);

    // Send update
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate(parcel);

    // Update UI
    refresh();
}

// static
void LLPanelLandGeneral::onClickBuyLand(void* data)
{
    bool* for_group = (bool*)data;
    LLViewerParcelMgr::getInstance()->startBuyLand(*for_group);
}

// static
void LLPanelLandGeneral::onClickScriptLimits(void* data)
{
    LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)data;
    LLParcel* parcel = panelp->mParcel->getParcel();
    if(parcel != NULL)
    {
        LLFloaterReg::showInstance("script_limits");
    }
}

// static
void LLPanelLandGeneral::onClickDeed(void*)
{
    //LLParcel* parcel = mParcel->getParcel();
    //if (parcel)
    //{
    LLViewerParcelMgr::getInstance()->startDeedLandToGroup();
    //}
}

// static
void LLPanelLandGeneral::onClickRelease(void*)
{
    LLViewerParcelMgr::getInstance()->startReleaseLand();
}

// static
void LLPanelLandGeneral::onClickReclaim(void*)
{
    LL_DEBUGS() << "LLPanelLandGeneral::onClickReclaim()" << LL_ENDL;
    LLViewerParcelMgr::getInstance()->reclaimParcel();
}

// static
bool LLPanelLandGeneral::enableBuyPass(void* data)
{
    LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)data;
    LLParcel* parcel = panelp != NULL ? panelp->mParcel->getParcel() : LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
    return (parcel != NULL) && (parcel->getParcelFlag(PF_USE_PASS_LIST) && !LLViewerParcelMgr::getInstance()->isCollisionBanned());
}


// static
void LLPanelLandGeneral::onClickBuyPass(void* data)
{
    LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)data;
    LLParcel* parcel = panelp != NULL ? panelp->mParcel->getParcel() : LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();

    if (!parcel) return;

    S32 pass_price = parcel->getPassPrice();
    std::string parcel_name = parcel->getName();
    F32 pass_hours = parcel->getPassHours();

    std::string cost, time;
    cost = llformat("%d", pass_price);
    time = llformat("%.2f", pass_hours);

    LLSD args;
    args["COST"] = cost;
    args["PARCEL_NAME"] = parcel_name;
    args["TIME"] = time;

    // creating pointer on selection to avoid deselection of parcel until we are done with buying pass (EXT-6464)
    sSelectionForBuyPass = LLViewerParcelMgr::getInstance()->getParcelSelection();
    LLNotificationsUtil::add("LandBuyPass", args, LLSD(), cbBuyPass);
}

// static
void LLPanelLandGeneral::onClickStartAuction(void* data)
{
    LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)data;
    LLParcel* parcelp = panelp->mParcel->getParcel();
    if(parcelp)
    {
        if(parcelp->getForSale())
        {
            LLNotificationsUtil::add("CannotStartAuctionAlreadyForSale");
        }
        else
        {
            //LLFloaterAuction::showInstance();
            LLFloaterReg::showInstance("auction");
        }
    }
}

// static
bool LLPanelLandGeneral::cbBuyPass(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    if (0 == option)
    {
        // User clicked OK
        LLViewerParcelMgr::getInstance()->buyPass();
    }
    // we are done with buying pass, additional selection is no longer needed
    sSelectionForBuyPass = NULL;
    return false;
}

// static
void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata)
{
    LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)userdata;

    LLParcel* parcel = panelp->mParcel->getParcel();
    if (!parcel)
    {
        return;
    }

    // Extract data from UI
    std::string name = panelp->mEditName->getText();
    std::string desc = panelp->mEditDesc->getText();

    // Valid data from UI

    // Stuff data into selected parcel
    parcel->setName(name);
    parcel->setDesc(desc);

    bool allow_deed_to_group= panelp->mCheckDeedToGroup->get();
    bool contribute_with_deed = panelp->mCheckContributeWithDeed->get();

    parcel->setParcelFlag(PF_ALLOW_DEED_TO_GROUP, allow_deed_to_group);
    parcel->setContributeWithDeed(contribute_with_deed);

    // Send update to server
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );

    // Might have changed properties, so let's redraw!
    panelp->refresh();
}

// static
void LLPanelLandGeneral::onClickSellLand(void* data)
{
    LLViewerParcelMgr::getInstance()->startSellLand();
    LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)data;
    panelp->refresh();
}

// static
void LLPanelLandGeneral::onClickStopSellLand(void* data)
{
    LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)data;
    LLParcel* parcel = panelp->mParcel->getParcel();

    parcel->setParcelFlag(PF_FOR_SALE, false);
    parcel->setSalePrice(0);
    parcel->setAuthorizedBuyerID(LLUUID::null);

    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate(parcel);
}

//---------------------------------------------------------------------------
// LLPanelLandObjects
//---------------------------------------------------------------------------
LLPanelLandObjects::LLPanelLandObjects(LLParcelSelectionHandle& parcel)
    :   LLPanel(),

        mParcel(parcel),
        mParcelObjectBonus(NULL),
        mSWTotalObjects(NULL),
        mObjectContribution(NULL),
        mTotalObjects(NULL),
        mOwnerObjects(NULL),
        mBtnShowOwnerObjects(NULL),
        mBtnReturnOwnerObjects(NULL),
        mGroupObjects(NULL),
        mBtnShowGroupObjects(NULL),
        mBtnReturnGroupObjects(NULL),
        mOtherObjects(NULL),
        mBtnShowOtherObjects(NULL),
        mBtnReturnOtherObjects(NULL),
        mSelectedObjects(NULL),
        mCleanOtherObjectsTime(NULL),
        mOtherTime(0),
        mBtnRefresh(NULL),
        mBtnReturnOwnerList(NULL),
        mOwnerList(NULL),
        mFirstReply(true),
        mSelectedCount(0),
        mSelectedIsGroup(false)
{
}



bool LLPanelLandObjects::postBuild()
{

    mFirstReply = true;
    mParcelObjectBonus = getChild<LLTextBox>("parcel_object_bonus");
    mSWTotalObjects = getChild<LLTextBox>("objects_available");
    mObjectContribution = getChild<LLTextBox>("object_contrib_text");
    mTotalObjects = getChild<LLTextBox>("total_objects_text");
    mOwnerObjects = getChild<LLTextBox>("owner_objects_text");

    mBtnShowOwnerObjects = getChild<LLButton>("ShowOwner");
    mBtnShowOwnerObjects->setClickedCallback(onClickShowOwnerObjects, this);

    mBtnReturnOwnerObjects = getChild<LLButton>("ReturnOwner...");
    mBtnReturnOwnerObjects->setClickedCallback(onClickReturnOwnerObjects, this);

    mGroupObjects = getChild<LLTextBox>("group_objects_text");
    mBtnShowGroupObjects = getChild<LLButton>("ShowGroup");
    mBtnShowGroupObjects->setClickedCallback(onClickShowGroupObjects, this);

    mBtnReturnGroupObjects = getChild<LLButton>("ReturnGroup...");
    mBtnReturnGroupObjects->setClickedCallback(onClickReturnGroupObjects, this);

    mOtherObjects = getChild<LLTextBox>("other_objects_text");
    mBtnShowOtherObjects = getChild<LLButton>("ShowOther");
    mBtnShowOtherObjects->setClickedCallback(onClickShowOtherObjects, this);

    mBtnReturnOtherObjects = getChild<LLButton>("ReturnOther...");
    mBtnReturnOtherObjects->setClickedCallback(onClickReturnOtherObjects, this);

    mSelectedObjects = getChild<LLTextBox>("selected_objects_text");
    mCleanOtherObjectsTime = getChild<LLLineEditor>("clean other time");

    mCleanOtherObjectsTime->setFocusLostCallback(boost::bind(onLostFocus, _1, this));
    mCleanOtherObjectsTime->setCommitCallback(onCommitClean, this);
    getChild<LLLineEditor>("clean other time")->setPrevalidate(LLTextValidate::validateNonNegativeS32);

    mBtnRefresh = getChild<LLButton>("Refresh List");
    mBtnRefresh->setClickedCallback(onClickRefresh, this);

    mBtnReturnOwnerList = getChild<LLButton>("Return objects...");
    mBtnReturnOwnerList->setClickedCallback(onClickReturnOwnerList, this);

    mIconAvatarOnline = LLUIImageList::getInstance()->getUIImage("icon_avatar_online.tga", 0);
    mIconAvatarOffline = LLUIImageList::getInstance()->getUIImage("icon_avatar_offline.tga", 0);
    mIconGroup = LLUIImageList::getInstance()->getUIImage("icon_group.tga", 0);

    mOwnerList = getChild<LLNameListCtrl>("owner list");
    mOwnerList->setIsFriendCallback(LLAvatarActions::isFriend);
    mOwnerList->sortByColumnIndex(3, false);
    childSetCommitCallback("owner list", onCommitList, this);
    mOwnerList->setDoubleClickCallback(onDoubleClickOwner, this);
    mOwnerList->setContextMenu(LLScrollListCtrl::MENU_AVATAR);

    return true;
}




// virtual
LLPanelLandObjects::~LLPanelLandObjects()
{ }

// static
void LLPanelLandObjects::onDoubleClickOwner(void *userdata)
{
    LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;

    LLScrollListItem* item = self->mOwnerList->getFirstSelected();
    if (item)
    {
        LLUUID owner_id = item->getUUID();
        // Look up the selected name, for future dialog box use.
        const LLScrollListCell* cell;
        cell = item->getColumn(1);
        if (!cell)
        {
            return;
        }
        // Is this a group?
        bool is_group = cell->getValue().asString() == OWNER_GROUP;
        if (is_group)
        {
            LLGroupActions::show(owner_id);
        }
        else
        {
            LLAvatarActions::showProfile(owner_id);
        }
    }
}

// public
void LLPanelLandObjects::refresh()
{
    LLParcel *parcel = mParcel->getParcel();

    mBtnShowOwnerObjects->setEnabled(false);
    mBtnShowGroupObjects->setEnabled(false);
    mBtnShowOtherObjects->setEnabled(false);
    mBtnReturnOwnerObjects->setEnabled(false);
    mBtnReturnGroupObjects->setEnabled(false);
    mBtnReturnOtherObjects->setEnabled(false);
    mCleanOtherObjectsTime->setEnabled(false);
    mBtnRefresh->           setEnabled(false);
    mBtnReturnOwnerList->   setEnabled(false);

    mSelectedOwners.clear();
    mOwnerList->deleteAllItems();
    mOwnerList->setEnabled(false);

    if (!parcel || gDisconnected)
    {
        mSWTotalObjects->setTextArg("[COUNT]", llformat("%d", 0));
        mSWTotalObjects->setTextArg("[TOTAL]", llformat("%d", 0));
        mSWTotalObjects->setTextArg("[AVAILABLE]", llformat("%d", 0));
        mObjectContribution->setTextArg("[COUNT]", llformat("%d", 0));
        mTotalObjects->setTextArg("[COUNT]", llformat("%d", 0));
        mOwnerObjects->setTextArg("[COUNT]", llformat("%d", 0));
        mGroupObjects->setTextArg("[COUNT]", llformat("%d", 0));
        mOtherObjects->setTextArg("[COUNT]", llformat("%d", 0));
        mSelectedObjects->setTextArg("[COUNT]", llformat("%d", 0));
    }
    else
    {
        S32 sw_max = parcel->getSimWideMaxPrimCapacity();
        S32 sw_total = parcel->getSimWidePrimCount();
        S32 max = ll_round(parcel->getMaxPrimCapacity() * parcel->getParcelPrimBonus());
        S32 total = parcel->getPrimCount();
        S32 owned = parcel->getOwnerPrimCount();
        S32 group = parcel->getGroupPrimCount();
        S32 other = parcel->getOtherPrimCount();
        S32 selected = parcel->getSelectedPrimCount();
        F32 parcel_object_bonus = parcel->getParcelPrimBonus();
        mOtherTime = parcel->getCleanOtherTime();

        // Can't have more than region max tasks, regardless of parcel
        // object bonus factor.
        LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
        if (region)
        {
            S32 max_tasks_per_region = (S32)region->getMaxTasks();
            sw_max = llmin(sw_max, max_tasks_per_region);
            max = llmin(max, max_tasks_per_region);
        }

        if (parcel_object_bonus != 1.0f)
        {
            mParcelObjectBonus->setVisible(true);
            mParcelObjectBonus->setTextArg("[BONUS]", llformat("%.2f", parcel_object_bonus));
        }
        else
        {
            mParcelObjectBonus->setVisible(false);
        }

        if (sw_total > sw_max)
        {
            mSWTotalObjects->setText(getString("objects_deleted_text"));
            mSWTotalObjects->setTextArg("[DELETED]", llformat("%d", sw_total - sw_max));
        }
        else
        {
            mSWTotalObjects->setText(getString("objects_available_text"));
            mSWTotalObjects->setTextArg("[AVAILABLE]", llformat("%d", sw_max - sw_total));
        }
        mSWTotalObjects->setTextArg("[COUNT]", llformat("%d", sw_total));
        mSWTotalObjects->setTextArg("[MAX]", llformat("%d", sw_max));

        mObjectContribution->setTextArg("[COUNT]", llformat("%d", max));
        mTotalObjects->setTextArg("[COUNT]", llformat("%d", total));
        mOwnerObjects->setTextArg("[COUNT]", llformat("%d", owned));
        mGroupObjects->setTextArg("[COUNT]", llformat("%d", group));
        mOtherObjects->setTextArg("[COUNT]", llformat("%d", other));
        mSelectedObjects->setTextArg("[COUNT]", llformat("%d", selected));
        mCleanOtherObjectsTime->setText(llformat("%d", mOtherTime));

        bool can_return_owned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_OWNED);
        bool can_return_group_set = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_SET);
        bool can_return_other = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_NON_GROUP);

        if (can_return_owned || can_return_group_set || can_return_other)
        {
            if (owned && can_return_owned)
            {
                mBtnShowOwnerObjects->setEnabled(true);
                mBtnReturnOwnerObjects->setEnabled(true);
            }
            if (group && can_return_group_set)
            {
                mBtnShowGroupObjects->setEnabled(true);
                mBtnReturnGroupObjects->setEnabled(true);
            }
            if (other && can_return_other)
            {
                mBtnShowOtherObjects->setEnabled(true);
                mBtnReturnOtherObjects->setEnabled(true);
            }

            mCleanOtherObjectsTime->setEnabled(true);
            mBtnRefresh->setEnabled(true);
        }
    }
}

// virtual
void LLPanelLandObjects::draw()
{
    LLPanel::draw();
}

void send_other_clean_time_message(S32 parcel_local_id, S32 other_clean_time)
{
    LLMessageSystem *msg = gMessageSystem;

    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if (!region) return;

    msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
    msg->nextBlockFast(_PREHASH_AgentData);
    msg->addUUIDFast(_PREHASH_AgentID,  gAgent.getID());
    msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
    msg->nextBlockFast(_PREHASH_ParcelData);
    msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
    msg->addS32Fast(_PREHASH_OtherCleanTime, other_clean_time);

    msg->sendReliable(region->getHost());
}

void send_return_objects_message(S32 parcel_local_id, S32 return_type,
                                 uuid_list_t* owner_ids = NULL)
{
    LLMessageSystem *msg = gMessageSystem;

    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if (!region) return;

    msg->newMessageFast(_PREHASH_ParcelReturnObjects);
    msg->nextBlockFast(_PREHASH_AgentData);
    msg->addUUIDFast(_PREHASH_AgentID,  gAgent.getID());
    msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
    msg->nextBlockFast(_PREHASH_ParcelData);
    msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
    msg->addU32Fast(_PREHASH_ReturnType, (U32) return_type);

    // Dummy task id, not used
    msg->nextBlock("TaskIDs");
    msg->addUUID("TaskID", LLUUID::null);

    // Throw all return ids into the packet.
    // TODO: Check for too many ids.
    if (owner_ids)
    {
        uuid_list_t::iterator end = owner_ids->end();
        for (uuid_list_t::iterator it = owner_ids->begin();
             it != end;
             ++it)
        {
            msg->nextBlockFast(_PREHASH_OwnerIDs);
            msg->addUUIDFast(_PREHASH_OwnerID, (*it));
        }
    }
    else
    {
        msg->nextBlockFast(_PREHASH_OwnerIDs);
        msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null);
    }

    msg->sendReliable(region->getHost());
}

bool LLPanelLandObjects::callbackReturnOwnerObjects(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    LLParcel *parcel = mParcel->getParcel();
    if (0 == option)
    {
        if (parcel)
        {
            LLUUID owner_id = parcel->getOwnerID();
            LLSD args;
            if (owner_id == gAgentID)
            {
                LLNotificationsUtil::add("OwnedObjectsReturned");
            }
            else
            {
                args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString();
                LLNotificationsUtil::add("OtherObjectsReturned", args);
            }
            send_return_objects_message(parcel->getLocalID(), RT_OWNER);
        }
    }

    LLSelectMgr::getInstance()->unhighlightAll();
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );
    refresh();
    return false;
}

bool LLPanelLandObjects::callbackReturnGroupObjects(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    LLParcel *parcel = mParcel->getParcel();
    if (0 == option)
    {
        if (parcel)
        {
            std::string group_name;
            gCacheName->getGroupName(parcel->getGroupID(), group_name);
            LLSD args;
            args["GROUPNAME"] = group_name;
            LLNotificationsUtil::add("GroupObjectsReturned", args);
            send_return_objects_message(parcel->getLocalID(), RT_GROUP);
        }
    }
    LLSelectMgr::getInstance()->unhighlightAll();
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );
    refresh();
    return false;
}

bool LLPanelLandObjects::callbackReturnOtherObjects(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    LLParcel *parcel = mParcel->getParcel();
    if (0 == option)
    {
        if (parcel)
        {
            LLNotificationsUtil::add("UnOwnedObjectsReturned");
            send_return_objects_message(parcel->getLocalID(), RT_OTHER);
        }
    }
    LLSelectMgr::getInstance()->unhighlightAll();
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );
    refresh();
    return false;
}

bool LLPanelLandObjects::callbackReturnOwnerList(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    LLParcel *parcel = mParcel->getParcel();
    if (0 == option)
    {
        if (parcel)
        {
            // Make sure we have something selected.
            uuid_list_t::iterator selected = mSelectedOwners.begin();
            if (selected != mSelectedOwners.end())
            {
                LLSD args;
                if (mSelectedIsGroup)
                {
                    args["GROUPNAME"] = mSelectedName;
                    LLNotificationsUtil::add("GroupObjectsReturned", args);
                }
                else
                {
                    args["NAME"] = mSelectedName;
                    LLNotificationsUtil::add("OtherObjectsReturned2", args);
                }

                send_return_objects_message(parcel->getLocalID(), RT_LIST, &(mSelectedOwners));
            }
        }
    }
    LLSelectMgr::getInstance()->unhighlightAll();
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );
    refresh();
    return false;
}


// static
void LLPanelLandObjects::onClickReturnOwnerList(void* userdata)
{
    LLPanelLandObjects  *self = (LLPanelLandObjects *)userdata;

    LLParcel* parcelp = self->mParcel->getParcel();
    if (!parcelp) return;

    // Make sure we have something selected.
    if (self->mSelectedOwners.empty())
    {
        return;
    }
    //uuid_list_t::iterator selected_itr = self->mSelectedOwners.begin();
    //if (selected_itr == self->mSelectedOwners.end()) return;

    send_parcel_select_objects(parcelp->getLocalID(), RT_LIST, &(self->mSelectedOwners));

    LLSD args;
    args["NAME"] = self->mSelectedName;
    args["N"] = llformat("%d",self->mSelectedCount);
    if (self->mSelectedIsGroup)
    {
        LLNotificationsUtil::add("ReturnObjectsDeededToGroup", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOwnerList, self, _1, _2));
    }
    else
    {
        LLNotificationsUtil::add("ReturnObjectsOwnedByUser", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOwnerList, self, _1, _2));
    }
}


// static
void LLPanelLandObjects::onClickRefresh(void* userdata)
{
    LLPanelLandObjects *self = (LLPanelLandObjects*)userdata;

    LLMessageSystem *msg = gMessageSystem;

    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel) return;

    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if (!region) return;

    self->mBtnRefresh->setEnabled(false);

    // ready the list for results
    self->mOwnerList->deleteAllItems();
    self->mOwnerList->setCommentText(LLTrans::getString("Searching"));
    self->mOwnerList->setEnabled(false);
    self->mFirstReply = true;

    // send the message
    msg->newMessageFast(_PREHASH_ParcelObjectOwnersRequest);
    msg->nextBlockFast(_PREHASH_AgentData);
    msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
    msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
    msg->nextBlockFast(_PREHASH_ParcelData);
    msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID());

    msg->sendReliable(region->getHost());
}

// static
void LLPanelLandObjects::processParcelObjectOwnersReply(LLMessageSystem *msg, void **)
{
    LLPanelLandObjects* self = LLFloaterLand::getCurrentPanelLandObjects();

    if (!self)
    {
        LL_WARNS() << "Received message for nonexistent LLPanelLandObject"
                << LL_ENDL;
        return;
    }

    const LLFontGL* FONT = LLFontGL::getFontSansSerif();

    // Extract all of the owners.
    S32 rows = msg->getNumberOfBlocksFast(_PREHASH_Data);
    //uuid_list_t return_ids;
    LLUUID  owner_id;
    bool    is_group_owned;
    S32     object_count;
    U32     most_recent_time = 0;
    bool    is_online;
    std::string object_count_str;
    //bool b_need_refresh = false;

    // If we were waiting for the first reply, clear the "Searching..." text.
    if (self->mFirstReply)
    {
        self->mOwnerList->deleteAllItems();
        self->mFirstReply = false;
    }

    for(S32 i = 0; i < rows; ++i)
    {
        msg->getUUIDFast(_PREHASH_Data, _PREHASH_OwnerID,       owner_id,       i);
        msg->getBOOLFast(_PREHASH_Data, _PREHASH_IsGroupOwned,  is_group_owned, i);
        msg->getS32Fast (_PREHASH_Data, _PREHASH_Count,         object_count,   i);
        msg->getBOOLFast(_PREHASH_Data, _PREHASH_OnlineStatus,  is_online,      i);
        if(msg->has("DataExtended"))
        {
            msg->getU32("DataExtended", "TimeStamp", most_recent_time, i);
        }

        if (owner_id.isNull())
        {
            continue;
        }

        LLNameListCtrl::NameItem item_params;
        item_params.value = owner_id;
        item_params.target = is_group_owned ? LLNameListCtrl::GROUP : LLNameListCtrl::INDIVIDUAL;

        if (is_group_owned)
        {
            item_params.columns.add().type("icon").value(self->mIconGroup->getName()).column("type");
            item_params.columns.add().value(OWNER_GROUP).font(FONT).column("online_status");
        }
        else if (is_online)
        {
            item_params.columns.add().type("icon").value(self->mIconAvatarOnline->getName()).column("type");
            item_params.columns.add().value(OWNER_ONLINE).font(FONT).column("online_status");
        }
        else  // offline
        {
            item_params.columns.add().type("icon").value(self->mIconAvatarOffline->getName()).column("type");
            item_params.columns.add().value(OWNER_OFFLINE).font(FONT).column("online_status");
        }

        // Placeholder for name.
        LLAvatarName av_name;
        LLAvatarNameCache::get(owner_id, &av_name);
        item_params.columns.add().value(av_name.getCompleteName()).font(FONT).column("name");

        object_count_str = llformat("%d", object_count);
        item_params.columns.add().value(object_count_str).font(FONT).column("count");
        item_params.columns.add().value(LLDate((double)most_recent_time)).font(FONT).column("mostrecent").type("date");

        self->mOwnerList->addNameItemRow(item_params);
        LL_DEBUGS() << "object owner " << owner_id << " (" << (is_group_owned ? "group" : "agent")
                << ") owns " << object_count << " objects." << LL_ENDL;
    }

    // check for no results
    if (0 == self->mOwnerList->getItemCount())
    {
        self->mOwnerList->setCommentText(LLTrans::getString("NoneFound"));
    }
    else
    {
        self->mOwnerList->setEnabled(true);
    }

    self->mBtnRefresh->setEnabled(true);
}

// static
void LLPanelLandObjects::onCommitList(LLUICtrl* ctrl, void* data)
{
    LLPanelLandObjects* self = (LLPanelLandObjects*)data;

    if (false == self->mOwnerList->getCanSelect())
    {
        return;
    }
    LLScrollListItem *item = self->mOwnerList->getFirstSelected();
    if (item)
    {
        // Look up the selected name, for future dialog box use.
        const LLScrollListCell* cell;
        cell = item->getColumn(1);
        if (!cell)
        {
            return;
        }
        // Is this a group?
        self->mSelectedIsGroup = cell->getValue().asString() == OWNER_GROUP;
        cell = item->getColumn(2);
        self->mSelectedName = cell->getValue().asString();
        cell = item->getColumn(3);
        self->mSelectedCount = atoi(cell->getValue().asString().c_str());

        // Set the selection, and enable the return button.
        self->mSelectedOwners.clear();
        self->mSelectedOwners.insert(item->getUUID());
        self->mBtnReturnOwnerList->setEnabled(true);

        // Highlight this user's objects
        clickShowCore(self, RT_LIST, &(self->mSelectedOwners));
    }
}

// static
void LLPanelLandObjects::clickShowCore(LLPanelLandObjects* self, S32 return_type, uuid_list_t* list)
{
    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel) return;

    send_parcel_select_objects(parcel->getLocalID(), return_type, list);
}

// static
void LLPanelLandObjects::onClickShowOwnerObjects(void* userdata)
{
    clickShowCore((LLPanelLandObjects*)userdata, RT_OWNER);
}

// static
void LLPanelLandObjects::onClickShowGroupObjects(void* userdata)
{
    clickShowCore((LLPanelLandObjects*)userdata, (RT_GROUP));
}

// static
void LLPanelLandObjects::onClickShowOtherObjects(void* userdata)
{
    clickShowCore((LLPanelLandObjects*)userdata, RT_OTHER);
}

// static
void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata)
{
    S32 owned = 0;

    LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata;
    LLParcel* parcel = panelp->mParcel->getParcel();
    if (!parcel) return;

    owned = parcel->getOwnerPrimCount();

    send_parcel_select_objects(parcel->getLocalID(), RT_OWNER);

    LLUUID owner_id = parcel->getOwnerID();

    LLSD args;
    args["N"] = llformat("%d",owned);

    if (owner_id == gAgent.getID())
    {
        LLNotificationsUtil::add("ReturnObjectsOwnedBySelf", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOwnerObjects, panelp, _1, _2));
    }
    else
    {
        args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString();
        LLNotificationsUtil::add("ReturnObjectsOwnedByUser", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOwnerObjects, panelp, _1, _2));
    }
}

// static
void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata)
{
    LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata;
    LLParcel* parcel = panelp->mParcel->getParcel();
    if (!parcel) return;

    send_parcel_select_objects(parcel->getLocalID(), RT_GROUP);

    std::string group_name;
    gCacheName->getGroupName(parcel->getGroupID(), group_name);

    LLSD args;
    args["NAME"] = group_name;
    args["N"] = llformat("%d", parcel->getGroupPrimCount());

    // create and show confirmation textbox
    LLNotificationsUtil::add("ReturnObjectsDeededToGroup", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnGroupObjects, panelp, _1, _2));
}

// static
void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata)
{
    S32 other = 0;

    LLPanelLandObjects* panelp = (LLPanelLandObjects*)userdata;
    LLParcel* parcel = panelp->mParcel->getParcel();
    if (!parcel) return;

    other = parcel->getOtherPrimCount();

    send_parcel_select_objects(parcel->getLocalID(), RT_OTHER);

    LLSD args;
    args["N"] = llformat("%d", other);

    if (parcel->getIsGroupOwned())
    {
        std::string group_name;
        gCacheName->getGroupName(parcel->getGroupID(), group_name);
        args["NAME"] = group_name;

        LLNotificationsUtil::add("ReturnObjectsNotOwnedByGroup", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOtherObjects, panelp, _1, _2));
    }
    else
    {
        LLUUID owner_id = parcel->getOwnerID();

        if (owner_id == gAgent.getID())
        {
            LLNotificationsUtil::add("ReturnObjectsNotOwnedBySelf", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOtherObjects, panelp, _1, _2));
        }
        else
        {
            args["NAME"] = LLSLURL("agent", owner_id, "completename").getSLURLString();
            LLNotificationsUtil::add("ReturnObjectsNotOwnedByUser", args, LLSD(), boost::bind(&LLPanelLandObjects::callbackReturnOtherObjects, panelp, _1, _2));
        }
    }
}

// static
void LLPanelLandObjects::onLostFocus(LLFocusableElement* caller, void* user_data)
{
    onCommitClean((LLUICtrl*)caller, user_data);
}

// static
void LLPanelLandObjects::onCommitClean(LLUICtrl *caller, void* user_data)
{
    LLPanelLandObjects  *lop = (LLPanelLandObjects *)user_data;
    LLParcel* parcel = lop->mParcel->getParcel();
    if (parcel)
    {
        S32 return_time = atoi(lop->mCleanOtherObjectsTime->getText().c_str());
        // Only send return time if it has changed
        if (return_time != lop->mOtherTime)
        {
            lop->mOtherTime = return_time;

        parcel->setCleanOtherTime(lop->mOtherTime);
        send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime);
    }
}
}


//---------------------------------------------------------------------------
// LLPanelLandOptions
//---------------------------------------------------------------------------

LLPanelLandOptions::LLPanelLandOptions(LLParcelSelectionHandle& parcel)
:   LLPanel(),
    mCheckEditObjects(NULL),
    mCheckEditGroupObjects(NULL),
    mCheckAllObjectEntry(NULL),
    mCheckGroupObjectEntry(NULL),
    mCheckSafe(NULL),
    mCheckFly(NULL),
    mCheckGroupScripts(NULL),
    mCheckOtherScripts(NULL),
    mCheckShowDirectory(NULL),
    mCategoryCombo(NULL),
    mLandingTypeCombo(NULL),
    mSnapshotCtrl(NULL),
    mLocationText(NULL),
    mSeeAvatarsText(NULL),
    mSetBtn(NULL),
    mClearBtn(NULL),
    mMatureCtrl(NULL),
    mPushRestrictionCtrl(NULL),
    mSeeAvatarsCtrl(NULL),
    mParcel(parcel)
{
}


bool LLPanelLandOptions::postBuild()
{
    mCheckEditObjects = getChild<LLCheckBoxCtrl>( "edit objects check");
    childSetCommitCallback("edit objects check", onCommitAny, this);

    mCheckEditGroupObjects = getChild<LLCheckBoxCtrl>( "edit group objects check");
    childSetCommitCallback("edit group objects check", onCommitAny, this);

    mCheckAllObjectEntry = getChild<LLCheckBoxCtrl>( "all object entry check");
    childSetCommitCallback("all object entry check", onCommitAny, this);

    mCheckGroupObjectEntry = getChild<LLCheckBoxCtrl>( "group object entry check");
    childSetCommitCallback("group object entry check", onCommitAny, this);

    mCheckGroupScripts = getChild<LLCheckBoxCtrl>( "check group scripts");
    childSetCommitCallback("check group scripts", onCommitAny, this);


    mCheckFly = getChild<LLCheckBoxCtrl>( "check fly");
    childSetCommitCallback("check fly", onCommitAny, this);


    mCheckOtherScripts = getChild<LLCheckBoxCtrl>( "check other scripts");
    childSetCommitCallback("check other scripts", onCommitAny, this);


    mCheckSafe = getChild<LLCheckBoxCtrl>( "check safe");
    childSetCommitCallback("check safe", onCommitAny, this);


    mPushRestrictionCtrl = getChild<LLCheckBoxCtrl>( "PushRestrictCheck");
    childSetCommitCallback("PushRestrictCheck", onCommitAny, this);

    mSeeAvatarsCtrl = getChild<LLCheckBoxCtrl>( "SeeAvatarsCheck");
    childSetCommitCallback("SeeAvatarsCheck", onCommitAny, this);

    mSeeAvatarsText = getChild<LLTextBox>("allow_see_label");
    if (mSeeAvatarsText)
    {
        mSeeAvatarsText->setShowCursorHand(false);
        mSeeAvatarsText->setSoundFlags(LLView::MOUSE_UP);
        mSeeAvatarsText->setClickedCallback(boost::bind(&toggleSeeAvatars, this));
    }

    mCheckShowDirectory = getChild<LLCheckBoxCtrl>( "ShowDirectoryCheck");
    childSetCommitCallback("ShowDirectoryCheck", onCommitAny, this);


    mCategoryCombo = getChild<LLComboBox>( "land category");
    childSetCommitCallback("land category", onCommitAny, this);


    mMatureCtrl = getChild<LLCheckBoxCtrl>( "MatureCheck");
    childSetCommitCallback("MatureCheck", onCommitAny, this);

    if (gAgent.wantsPGOnly())
    {
        // Disable these buttons if they are PG (Teen) users
        mMatureCtrl->setVisible(false);
        mMatureCtrl->setEnabled(false);
    }


    mSnapshotCtrl = getChild<LLTextureCtrl>("snapshot_ctrl");
    if (mSnapshotCtrl)
    {
        mSnapshotCtrl->setCommitCallback( onCommitAny, this );
        mSnapshotCtrl->setAllowNoTexture ( true );
        mSnapshotCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
        mSnapshotCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
    }
    else
    {
        LL_WARNS() << "LLUICtrlFactory::getTexturePickerByName() returned NULL for 'snapshot_ctrl'" << LL_ENDL;
    }


    mLocationText = getChild<LLTextBox>("landing_point");

    mSetBtn = getChild<LLButton>("Set");
    mSetBtn->setClickedCallback(onClickSet, this);


    mClearBtn = getChild<LLButton>("Clear");
    mClearBtn->setClickedCallback(onClickClear, this);


    mLandingTypeCombo = getChild<LLComboBox>( "landing type");
    childSetCommitCallback("landing type", onCommitAny, this);

    return true;
}


// virtual
LLPanelLandOptions::~LLPanelLandOptions()
{ }


// virtual
void LLPanelLandOptions::refresh()
{
    refreshSearch();

    LLParcel *parcel = mParcel->getParcel();
    if (!parcel || gDisconnected)
    {
        mCheckEditObjects   ->set(false);
        mCheckEditObjects   ->setEnabled(false);

        mCheckEditGroupObjects  ->set(false);
        mCheckEditGroupObjects  ->setEnabled(false);

        mCheckAllObjectEntry    ->set(false);
        mCheckAllObjectEntry    ->setEnabled(false);

        mCheckGroupObjectEntry  ->set(false);
        mCheckGroupObjectEntry  ->setEnabled(false);

        mCheckSafe          ->set(false);
        mCheckSafe          ->setEnabled(false);

        mCheckFly           ->set(false);
        mCheckFly           ->setEnabled(false);

        mCheckGroupScripts  ->set(false);
        mCheckGroupScripts  ->setEnabled(false);

        mCheckOtherScripts  ->set(false);
        mCheckOtherScripts  ->setEnabled(false);

        mPushRestrictionCtrl->set(false);
        mPushRestrictionCtrl->setEnabled(false);

        mSeeAvatarsCtrl->set(true);
        mSeeAvatarsCtrl->setEnabled(false);
        mSeeAvatarsText->setEnabled(false);

        mLandingTypeCombo->setCurrentByIndex(0);
        mLandingTypeCombo->setEnabled(false);

        mSnapshotCtrl->setImageAssetID(LLUUID::null);
        mSnapshotCtrl->setEnabled(false);

        mLocationText->setTextArg("[LANDING]", getString("landing_point_none"));
        mSetBtn->setEnabled(false);
        mClearBtn->setEnabled(false);

        mMatureCtrl->setEnabled(false);
    }
    else
    {
        // something selected, hooray!

        // Display options
        bool can_change_options = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS);
        mCheckEditObjects   ->set( parcel->getAllowModify() );
        mCheckEditObjects   ->setEnabled( can_change_options );

        mCheckEditGroupObjects  ->set( parcel->getAllowGroupModify() ||  parcel->getAllowModify());
        mCheckEditGroupObjects  ->setEnabled( can_change_options && !parcel->getAllowModify() );        // If others edit is enabled, then this is explicitly enabled.

        mCheckAllObjectEntry    ->set( parcel->getAllowAllObjectEntry() );
        mCheckAllObjectEntry    ->setEnabled( can_change_options );

        mCheckGroupObjectEntry  ->set( parcel->getAllowGroupObjectEntry() ||  parcel->getAllowAllObjectEntry());
        mCheckGroupObjectEntry  ->setEnabled( can_change_options && !parcel->getAllowAllObjectEntry() );

        mCheckSafe          ->set( !parcel->getAllowDamage() );
        mCheckSafe          ->setEnabled( can_change_options );

        mCheckFly           ->set( parcel->getAllowFly() );
        mCheckFly           ->setEnabled( can_change_options );

        mCheckGroupScripts  ->set( parcel->getAllowGroupScripts() || parcel->getAllowOtherScripts());
        mCheckGroupScripts  ->setEnabled( can_change_options && !parcel->getAllowOtherScripts());

        mCheckOtherScripts  ->set( parcel->getAllowOtherScripts() );
        mCheckOtherScripts  ->setEnabled( can_change_options );

        mPushRestrictionCtrl->set( parcel->getRestrictPushObject() );
        if(parcel->getRegionPushOverride())
        {
            mPushRestrictionCtrl->setLabel(getString("push_restrict_region_text"));
            mPushRestrictionCtrl->setEnabled(false);
            mPushRestrictionCtrl->set(true);
        }
        else
        {
            mPushRestrictionCtrl->setLabel(getString("push_restrict_text"));
            mPushRestrictionCtrl->setEnabled(can_change_options);
        }

        mSeeAvatarsCtrl->set(parcel->getSeeAVs());
        mSeeAvatarsCtrl->setEnabled(can_change_options && parcel->getHaveNewParcelLimitData());
        mSeeAvatarsText->setEnabled(can_change_options && parcel->getHaveNewParcelLimitData());

        bool can_change_landing_point = LLViewerParcelMgr::isParcelModifiableByAgent(parcel,
                                                        GP_LAND_SET_LANDING_POINT);
        mLandingTypeCombo->setCurrentByIndex((S32)parcel->getLandingType());
        mLandingTypeCombo->setEnabled( can_change_landing_point );

        bool can_change_identity =
                LLViewerParcelMgr::isParcelModifiableByAgent(
                    parcel, GP_LAND_CHANGE_IDENTITY);
        mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
        mSnapshotCtrl->setEnabled( can_change_identity );

        // find out where we're looking and convert that to an angle in degrees on a regular compass (not the internal representation)
        LLVector3 user_look_at = parcel->getUserLookAt();
        U32 user_look_at_angle = ( (U32)( ( atan2(user_look_at[1], -user_look_at[0]) + F_PI * 2 ) * RAD_TO_DEG + 0.5) - 90) % 360;

        LLVector3 pos = parcel->getUserLocation();
        if (pos.isExactlyZero())
        {
            mLocationText->setTextArg("[LANDING]", getString("landing_point_none"));
        }
        else
        {
            mLocationText->setTextArg("[LANDING]",llformat("%d, %d, %d (%d\xC2\xB0)",
                                                           ll_round(pos.mV[VX]),
                                                           ll_round(pos.mV[VY]),
                                                           ll_round(pos.mV[VZ]),
                                                           user_look_at_angle));
        }

        mSetBtn->setEnabled( can_change_landing_point );
        mClearBtn->setEnabled( can_change_landing_point );

        if (gAgent.wantsPGOnly())
        {
            // Disable these buttons if they are PG (Teen) users
            mMatureCtrl->setVisible(false);
            mMatureCtrl->setEnabled(false);
        }
        else
        {
            // not teen so fill in the data for the maturity control
            mMatureCtrl->setVisible(true);
            LLStyle::Params style;
            style.image(LLUI::getUIImage(gFloaterView->getParentFloater(this)->getString("maturity_icon_moderate")));
            LLCheckBoxWithTBAcess* fullaccess_mature_ctrl = (LLCheckBoxWithTBAcess*)mMatureCtrl;
            fullaccess_mature_ctrl->getTextBox()->setText(LLStringExplicit(""));
            fullaccess_mature_ctrl->getTextBox()->appendImageSegment(style);
            fullaccess_mature_ctrl->getTextBox()->appendText(getString("mature_check_mature"), false);
            fullaccess_mature_ctrl->setToolTip(getString("mature_check_mature_tooltip"));
            fullaccess_mature_ctrl->reshape(fullaccess_mature_ctrl->getRect().getWidth(), fullaccess_mature_ctrl->getRect().getHeight(), false);

            // they can see the checkbox, but its disposition depends on the
            // state of the region
            LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
            if (regionp)
            {
                if (regionp->getSimAccess() == SIM_ACCESS_PG)
                {
                    mMatureCtrl->setEnabled(false);
                    mMatureCtrl->set(false);
                }
                else if (regionp->getSimAccess() == SIM_ACCESS_MATURE)
                {
                    mMatureCtrl->setEnabled(can_change_identity);
                    mMatureCtrl->set(parcel->getMaturePublish());
                }
                else if (regionp->getSimAccess() == SIM_ACCESS_ADULT)
                {
                    mMatureCtrl->setEnabled(false);
                    mMatureCtrl->set(true);
                    mMatureCtrl->setLabel(getString("mature_check_adult"));
                    mMatureCtrl->setToolTip(getString("mature_check_adult_tooltip"));
                }
            }
        }
    }
}

// virtual
void LLPanelLandOptions::draw()
{
    LLPanel::draw();
}


// private
void LLPanelLandOptions::refreshSearch()
{
    LLParcel *parcel = mParcel->getParcel();
    if (!parcel || gDisconnected)
    {
        mCheckShowDirectory->set(false);
        mCheckShowDirectory->setEnabled(false);

        const std::string& none_string = LLParcel::getCategoryString(LLParcel::C_NONE);
        mCategoryCombo->setValue(none_string);
        mCategoryCombo->setEnabled(false);
        return;
    }

    LLViewerRegion* region =
            LLViewerParcelMgr::getInstance()->getSelectionRegion();

    bool can_change =
            LLViewerParcelMgr::isParcelModifiableByAgent(
                parcel, GP_LAND_FIND_PLACES)
            && region
            && !(region->getRegionFlag(REGION_FLAGS_BLOCK_PARCEL_SEARCH));

    bool show_directory = parcel->getParcelFlag(PF_SHOW_DIRECTORY);
    mCheckShowDirectory->set(show_directory);

    // Set by string in case the order in UI doesn't match the order by index.
    LLParcel::ECategory cat = parcel->getCategory();
    const std::string& category_string = LLParcel::getCategoryString(cat);
    mCategoryCombo->setValue(category_string);

    std::string tooltip;
    bool enable_show_directory = false;
    // Parcels <= 128 square meters cannot be listed in search, in an
    // effort to reduce search spam from small parcels.  See also
    // the search crawler "grid-crawl.py" in secondlife.com/doc/app/search/ JC
    const S32 MIN_PARCEL_AREA_FOR_SEARCH = 128;
    bool large_enough = parcel->getArea() >= MIN_PARCEL_AREA_FOR_SEARCH;
    if (large_enough)
    {
        if (can_change)
        {
            tooltip = getString("search_enabled_tooltip");
            enable_show_directory = true;
        }
        else
        {
            tooltip = getString("search_disabled_permissions_tooltip");
            enable_show_directory = false;
        }
    }
    else
    {
        // not large enough to include in search
        if (can_change)
        {
            if (show_directory)
            {
                // parcels that are too small, but are still in search for
                // legacy reasons, need to have the check box enabled so
                // the owner can delist the parcel. JC
                tooltip = getString("search_enabled_tooltip");
                enable_show_directory = true;
            }
            else
            {
                tooltip = getString("search_disabled_small_tooltip");
                enable_show_directory = false;
            }
        }
        else
        {
            // both too small and don't have permission, so just
            // show the permissions as the reason (which is probably
            // the more common case) JC
            tooltip = getString("search_disabled_permissions_tooltip");
            enable_show_directory = false;
        }
    }
    mCheckShowDirectory->setToolTip(tooltip);
    mCategoryCombo->setToolTip(tooltip);
    mCheckShowDirectory->setEnabled(enable_show_directory);
    mCategoryCombo->setEnabled(enable_show_directory);
}


// static
void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata)
{
    LLPanelLandOptions *self = (LLPanelLandOptions *)userdata;

    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel)
    {
        return;
    }

    // Extract data from UI
    bool create_objects     = self->mCheckEditObjects->get();
    bool create_group_objects   = self->mCheckEditGroupObjects->get() || self->mCheckEditObjects->get();
    bool all_object_entry       = self->mCheckAllObjectEntry->get();
    bool group_object_entry = self->mCheckGroupObjectEntry->get() || self->mCheckAllObjectEntry->get();
    bool allow_terraform    = false; // removed from UI so always off now - self->mCheckEditLand->get();
    bool allow_damage       = !self->mCheckSafe->get();
    bool allow_fly          = self->mCheckFly->get();
    bool allow_landmark     = true; // cannot restrict landmark creation
    bool allow_other_scripts    = self->mCheckOtherScripts->get();
    bool allow_group_scripts    = self->mCheckGroupScripts->get() || allow_other_scripts;
    bool allow_publish      = false;
    bool mature_publish     = self->mMatureCtrl->get();
    bool push_restriction   = self->mPushRestrictionCtrl->get();
    bool see_avs            = self->mSeeAvatarsCtrl->get();
    bool show_directory     = self->mCheckShowDirectory->get();
    // we have to get the index from a lookup, not from the position in the dropdown!
    S32  category_index     = LLParcel::getCategoryFromString(self->mCategoryCombo->getSelectedValue());
    S32  landing_type_index = self->mLandingTypeCombo->getCurrentIndex();
    LLUUID snapshot_id      = self->mSnapshotCtrl->getImageAssetID();
    LLViewerRegion* region;
    region = LLViewerParcelMgr::getInstance()->getSelectionRegion();

    if (region && region->getAllowDamage())
    {   // Damage is allowed on the region - server will always allow scripts
        if ( (!allow_other_scripts && parcel->getParcelFlag(PF_ALLOW_OTHER_SCRIPTS)) ||
             (!allow_group_scripts && parcel->getParcelFlag(PF_ALLOW_GROUP_SCRIPTS)) )
        {   // Don't allow turning off "Run Scripts" if damage is allowed in the region
            self->mCheckOtherScripts->set(parcel->getParcelFlag(PF_ALLOW_OTHER_SCRIPTS));   // Restore UI to actual settings
            self->mCheckGroupScripts->set(parcel->getParcelFlag(PF_ALLOW_GROUP_SCRIPTS));
            LLNotificationsUtil::add("UnableToDisableOutsideScripts");
            return;
        }
    }

    // Push data into current parcel
    parcel->setParcelFlag(PF_CREATE_OBJECTS, create_objects);
    parcel->setParcelFlag(PF_CREATE_GROUP_OBJECTS, create_group_objects);
    parcel->setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, all_object_entry);
    parcel->setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, group_object_entry);
    parcel->setParcelFlag(PF_ALLOW_TERRAFORM, allow_terraform);
    parcel->setParcelFlag(PF_ALLOW_DAMAGE, allow_damage);
    parcel->setParcelFlag(PF_ALLOW_FLY, allow_fly);
    parcel->setParcelFlag(PF_ALLOW_LANDMARK, allow_landmark);
    parcel->setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, allow_group_scripts);
    parcel->setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, allow_other_scripts);
    parcel->setParcelFlag(PF_SHOW_DIRECTORY, show_directory);
    parcel->setParcelFlag(PF_ALLOW_PUBLISH, allow_publish);
    parcel->setParcelFlag(PF_MATURE_PUBLISH, mature_publish);
    parcel->setParcelFlag(PF_RESTRICT_PUSHOBJECT, push_restriction);
    parcel->setCategory((LLParcel::ECategory)category_index);
    parcel->setLandingType((LLParcel::ELandingType)landing_type_index);
    parcel->setSnapshotID(snapshot_id);
    parcel->setSeeAVs(see_avs);

    // Send current parcel data upstream to server
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );

    // Might have changed properties, so let's redraw!
    self->refresh();
}


// static
void LLPanelLandOptions::onClickSet(void* userdata)
{
    LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;

    LLParcel* selected_parcel = self->mParcel->getParcel();
    if (!selected_parcel) return;

    LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
    if (!agent_parcel) return;

    if (agent_parcel->getLocalID() != selected_parcel->getLocalID())
    {
        LLNotificationsUtil::add("MustBeInParcel");
        return;
    }

    LLVector3 pos_region = gAgent.getPositionAgent();
    selected_parcel->setUserLocation(pos_region);
    selected_parcel->setUserLookAt(gAgent.getFrameAgent().getAtAxis());

    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate(selected_parcel);

    self->refresh();
}

void LLPanelLandOptions::onClickClear(void* userdata)
{
    LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;

    LLParcel* selected_parcel = self->mParcel->getParcel();
    if (!selected_parcel) return;

    // yes, this magic number of 0,0,0 means that it is clear
    LLVector3 zero_vec(0.f, 0.f, 0.f);
    selected_parcel->setUserLocation(zero_vec);
    selected_parcel->setUserLookAt(zero_vec);

    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate(selected_parcel);

    self->refresh();
}

void LLPanelLandOptions::toggleSeeAvatars(void* userdata)
{
    LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;
    if (self)
    {
        self->getChild<LLCheckBoxCtrl>("SeeAvatarsCheck")->toggle();
        self->getChild<LLCheckBoxCtrl>("SeeAvatarsCheck")->setBtnFocus();
        self->onCommitAny(NULL, userdata);
    }
}
//---------------------------------------------------------------------------
// LLPanelLandAccess
//---------------------------------------------------------------------------

LLPanelLandAccess::LLPanelLandAccess(LLParcelSelectionHandle& parcel)
    : LLPanel(),
      mParcel(parcel)
{
}


bool LLPanelLandAccess::postBuild()
{
    mPaymentInfoCheck = getChild<LLUICtrl>("limit_payment");
    mPaymentInfoCheck->setCommitCallback(onCommitAny, this);
    mAgeVerifiedCheck = getChild<LLUICtrl>("limit_age_verified");
    mAgeVerifiedCheck->setCommitCallback(onCommitAny, this);
    mTemporaryPassCheck = getChild<LLUICtrl>("PassCheck");
    mTemporaryPassCheck->setCommitCallback(onCommitAny, this);
    mPublicAccessCheck = getChild<LLUICtrl>("public_access");
    mPublicAccessCheck->setCommitCallback(onCommitPublicAccess, this);
    mGroupAccessCheck = getChild<LLUICtrl>("GroupCheck");
    mGroupAccessCheck->setCommitCallback(onCommitGroupCheck, this);
    mTemporaryPassCombo = getChild<LLComboBox>("pass_combo");
    mGroupAccessCheck->setCommitCallback(onCommitAny, this);
    mTemporaryPassPriceSpin = getChild<LLUICtrl>("PriceSpin");
    mGroupAccessCheck->setCommitCallback(onCommitAny, this);
    mTemporaryPassHourSpin = getChild<LLUICtrl>("HoursSpin");
    mGroupAccessCheck->setCommitCallback(onCommitAny, this);

    mAllowText = getChild<LLUICtrl>("AllowedText");
    mBanText = getChild<LLUICtrl>("BanCheck");

    mBtnAddAllowed = getChild<LLButton>("add_allowed");
    mBtnAddAllowed->setCommitCallback(boost::bind(&LLPanelLandAccess::onClickAddAccess, this));
    mBtnRemoveAllowed = getChild<LLButton>("remove_allowed");
    mBtnRemoveAllowed->setCommitCallback(boost::bind(&LLPanelLandAccess::onClickRemoveAccess, this));
    mBtnAddBanned = getChild<LLButton>("add_banned");
    mBtnAddBanned->setCommitCallback(boost::bind(&LLPanelLandAccess::onClickAddBanned, this));
    mBtnRemoveBanned = getChild<LLButton>("remove_banned");
    mBtnRemoveBanned->setCommitCallback(boost::bind(&LLPanelLandAccess::onClickRemoveBanned, this));

    mListAccess = getChild<LLNameListCtrl>("AccessList");
    if (mListAccess)
    {
        mListAccess->sortByColumnIndex(0, true); // ascending
        mListAccess->setContextMenu(LLScrollListCtrl::MENU_AVATAR);
    }

    mListBanned = getChild<LLNameListCtrl>("BannedList");
    if (mListBanned)
    {
        mListBanned->sortByColumnIndex(0, true); // ascending
        mListBanned->setContextMenu(LLScrollListCtrl::MENU_AVATAR);
        mListBanned->setAlternateSort();
    }

    return true;
}


LLPanelLandAccess::~LLPanelLandAccess()
{
}

void LLPanelLandAccess::refresh()
{
    LLFloater* parent_floater = gFloaterView->getParentFloater(this);
    LLParcel *parcel = mParcel->getParcel();

    // Display options
    if (parcel && !gDisconnected)
    {
        bool use_access_list = parcel->getParcelFlag(PF_USE_ACCESS_LIST);
        bool use_group = parcel->getParcelFlag(PF_USE_ACCESS_GROUP);
        bool public_access = !use_access_list;

        if (parcel->getRegionAllowAccessOverride())
        {
            mPublicAccessCheck->setValue(public_access);
            mGroupAccessCheck->setValue(use_group);
        }
        else
        {
            mPublicAccessCheck->setValue(true);
            mGroupAccessCheck->setValue(false);
        }
        std::string group_name;
        gCacheName->getGroupName(parcel->getGroupID(), group_name);
        mGroupAccessCheck->setLabelArg("[GROUP]", group_name );

        // Allow list
        if (mListAccess)
        {
            // Clear the sort order so we don't re-sort on every add.
            mListAccess->clearSortOrder();
            mListAccess->deleteAllItems();
            auto count = parcel->mAccessList.size();
            mAllowText->setTextArg("[COUNT]", llformat("%d", count));
            mAllowText->setTextArg("[MAX]", llformat("%d",PARCEL_MAX_ACCESS_LIST));

            mListAccess->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count));
            mListAccess->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST));

            for (LLAccessEntry::map::const_iterator cit = parcel->mAccessList.begin();
                 cit != parcel->mAccessList.end(); ++cit)
            {
                const LLAccessEntry& entry = (*cit).second;
                std::string prefix;
                if (entry.mTime != 0)
                {
                    LLStringUtil::format_map_t args;
                    S32 now = (S32)time(NULL);
                    S32 seconds = entry.mTime - now;
                    if (seconds < 0) seconds = 0;
                    prefix.assign(" (");
                    if (seconds >= 120)
                    {
                        args["[MINUTES]"] = llformat("%d", (seconds/60));
                        std::string buf = parent_floater->getString ("Minutes", args);
                        prefix.append(buf);
                    }
                    else if (seconds >= 60)
                    {
                        prefix.append("1 " + parent_floater->getString("Minute"));
                    }
                    else
                    {
                        args["[SECONDS]"] = llformat("%d", seconds);
                        std::string buf = parent_floater->getString ("Seconds", args);
                        prefix.append(buf);
                    }
                    prefix.append(" " + parent_floater->getString("Remaining") + ") ");
                }
                mListAccess->addNameItem(entry.mID, ADD_DEFAULT, true, "", prefix);
            }
            mListAccess->sortByName(true);
        }

        // Ban List
        if(mListBanned)
        {
            // Clear the sort order so we don't re-sort on every add.
            mListBanned->clearSortOrder();
            mListBanned->deleteAllItems();
            auto count = parcel->mBanList.size();
            mBanText->setTextArg("[COUNT]", llformat("%d",count));
            mBanText->setTextArg("[MAX]", llformat("%d",PARCEL_MAX_ACCESS_LIST));

            mListBanned->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count));
            mListBanned->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST));

            for (LLAccessEntry::map::const_iterator cit = parcel->mBanList.begin();
                 cit != parcel->mBanList.end(); ++cit)
            {
                const LLAccessEntry& entry = (*cit).second;
                std::string duration;
                S32 seconds = -1;
                if (entry.mTime != 0)
                {
                    LLStringUtil::format_map_t args;
                    S32 now = (S32)time(NULL);
                    seconds = entry.mTime - now;
                    if (seconds < 0) seconds = 0;

                    if (seconds >= 7200)
                    {
                        args["[HOURS]"] = llformat("%d", (seconds / 3600));
                        duration = parent_floater->getString("Hours", args);
                    }
                    else if (seconds >= 3600)
                    {
                        duration = "1 " + parent_floater->getString("Hour");
                    }
                    else if (seconds >= 120)
                    {
                        args["[MINUTES]"] = llformat("%d", (seconds / 60));
                        duration = parent_floater->getString("Minutes", args);
                    }
                    else if (seconds >= 60)
                    {
                        duration = "1 " + parent_floater->getString("Minute");
                    }
                    else
                    {
                        args["[SECONDS]"] = llformat("%d", seconds);
                        duration = parent_floater->getString("Seconds", args);
                    }
                }
                else
                {
                    duration = parent_floater->getString("Always");
                }
                LLSD item;
                item["id"] = entry.mID;
                LLSD& columns = item["columns"];
                columns[0]["column"] = "name"; // to be populated later
                columns[1]["column"] = "duration";
                columns[1]["value"] = duration;
                columns[1]["alt_value"] = entry.mTime != 0 ? std::to_string(seconds) : "Always";
                mListBanned->addElement(item);
            }
            mListBanned->sortByName(true);
        }

        if(parcel->getRegionDenyAnonymousOverride())
        {
            mPaymentInfoCheck->setValue(true);
            mPaymentInfoCheck->setLabelArg("[ESTATE_PAYMENT_LIMIT]", getString("access_estate_defined") );
        }
        else
        {
            mPaymentInfoCheck->setValue((parcel->getParcelFlag(PF_DENY_ANONYMOUS)));
            mPaymentInfoCheck->setLabelArg("[ESTATE_PAYMENT_LIMIT]", std::string() );
        }
        if(parcel->getRegionDenyAgeUnverifiedOverride())
        {
            mAgeVerifiedCheck->setValue(true);
            mAgeVerifiedCheck->setLabelArg("[ESTATE_AGE_LIMIT]", getString("access_estate_defined") );
        }
        else
        {
            mAgeVerifiedCheck->setValue((parcel->getParcelFlag(PF_DENY_AGEUNVERIFIED)));
            mAgeVerifiedCheck->setLabelArg("[ESTATE_AGE_LIMIT]", std::string() );
        }

        bool use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST);
        mTemporaryPassCheck->setValue(use_pass);
        if (mTemporaryPassCombo)
        {
            if (public_access || !use_pass)
            {
                mTemporaryPassCombo->selectByValue("anyone");
            }
        }

        S32 pass_price = parcel->getPassPrice();
        mTemporaryPassPriceSpin->setValue((F32)pass_price);

        F32 pass_hours = parcel->getPassHours();
        mTemporaryPassHourSpin->setValue(pass_hours);
    }
    else
    {
        mPublicAccessCheck->setValue(false);
        mPaymentInfoCheck->setValue(false);
        mAgeVerifiedCheck->setValue(false);
        mGroupAccessCheck->setValue(false);
        mGroupAccessCheck->setLabelArg("[GROUP]", LLStringUtil::null );
        mTemporaryPassCheck->setValue(false);
        mTemporaryPassPriceSpin->setValue((F32)PARCEL_PASS_PRICE_DEFAULT);
        mTemporaryPassHourSpin->setValue(PARCEL_PASS_HOURS_DEFAULT );
        mListAccess->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",0));
        mListAccess->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",0));
        mListBanned->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",0));
        mListBanned->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",0));
    }
}

void LLPanelLandAccess::refresh_ui()
{
    mPublicAccessCheck->setEnabled(false);
    mPaymentInfoCheck->setEnabled(false);
    mAgeVerifiedCheck->setEnabled(false);
    mGroupAccessCheck->setEnabled(false);
    mTemporaryPassCheck->setEnabled(false);
    mTemporaryPassCombo->setEnabled(false);
    mTemporaryPassPriceSpin->setEnabled(false);
    mTemporaryPassHourSpin->setEnabled(false);
    mListAccess->setEnabled(false);
    mListBanned->setEnabled(false);
    mBtnAddAllowed->setEnabled(false);
    mBtnRemoveAllowed->setEnabled(false);
    mBtnAddBanned->setEnabled(false);
    mBtnRemoveBanned->setEnabled(false);

    LLParcel *parcel = mParcel->getParcel();
    if (parcel && !gDisconnected)
    {
        bool can_manage_allowed = false;
        bool can_manage_banned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_BANNED);

        if (parcel->getRegionAllowAccessOverride())
        {   // Estate owner may have disabled allowing the parcel owner from managing access.
            can_manage_allowed = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_ALLOWED);
        }

        mPublicAccessCheck->setEnabled(can_manage_allowed);
        bool public_access = mPublicAccessCheck->getValue().asBoolean();
        if (public_access)
        {
            bool override = false;
            if(parcel->getRegionDenyAnonymousOverride())
            {
                override = true;
                mPaymentInfoCheck->setEnabled(false);
            }
            else
            {
                mPaymentInfoCheck->setEnabled(can_manage_allowed);
            }
            if(parcel->getRegionDenyAgeUnverifiedOverride())
            {
                override = true;
                mAgeVerifiedCheck->setEnabled(false);
            }
            else
            {
                mAgeVerifiedCheck->setEnabled(can_manage_allowed);
            }
            mTemporaryPassCheck->setEnabled(false);
            mTemporaryPassCombo->setEnabled(false);
            mListAccess->setEnabled(false);
        }
        else
        {
            mPaymentInfoCheck->setEnabled(false);
            mAgeVerifiedCheck->setEnabled(false);

            bool sell_passes = mTemporaryPassCheck->getValue().asBoolean();
            mTemporaryPassCheck->setEnabled(can_manage_allowed);
            if (sell_passes)
            {
                mTemporaryPassCombo->setEnabled(can_manage_allowed);
                mTemporaryPassPriceSpin->setEnabled(can_manage_allowed);
                mTemporaryPassHourSpin->setEnabled(can_manage_allowed);
            }
        }
        std::string group_name;
        if (gCacheName->getGroupName(parcel->getGroupID(), group_name))
        {
            bool can_allow_groups = !public_access || (public_access && (mPaymentInfoCheck->getValue().asBoolean() ^ mAgeVerifiedCheck->getValue().asBoolean()));
            mGroupAccessCheck->setEnabled(can_manage_allowed && can_allow_groups);
        }
        mListAccess->setEnabled(can_manage_allowed);
        auto allowed_list_count = parcel->mAccessList.size();
        mBtnAddAllowed->setEnabled(can_manage_allowed && allowed_list_count < PARCEL_MAX_ACCESS_LIST);
        bool has_selected = (mListAccess && mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0);
        mBtnRemoveAllowed->setEnabled(can_manage_allowed && has_selected);

        mListBanned->setEnabled(can_manage_banned);
        auto banned_list_count = parcel->mBanList.size();
        mBtnAddBanned->setEnabled(can_manage_banned && banned_list_count < PARCEL_MAX_ACCESS_LIST);
        has_selected = (mListBanned && mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0);
        mBtnRemoveBanned->setEnabled(can_manage_banned && has_selected);
    }
}


// public
void LLPanelLandAccess::refreshNames()
{
    LLParcel* parcel = mParcel->getParcel();
    std::string group_name;
    if(parcel)
    {
        gCacheName->getGroupName(parcel->getGroupID(), group_name);
    }
    mGroupAccessCheck->setLabelArg("[GROUP]", group_name);
}


// virtual
void LLPanelLandAccess::draw()
{
    refresh_ui();
    refreshNames();
    LLPanel::draw();
}

// static
void LLPanelLandAccess::onCommitPublicAccess(LLUICtrl *ctrl, void *userdata)
{
    LLPanelLandAccess *self = (LLPanelLandAccess *)userdata;
    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel)
    {
        return;
    }

    onCommitAny(ctrl, userdata);
}

void LLPanelLandAccess::onCommitGroupCheck(LLUICtrl *ctrl, void *userdata)
{
    LLPanelLandAccess *self = (LLPanelLandAccess *)userdata;
    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel)
    {
        return;
    }

    bool use_pass_list = !self->mPublicAccessCheck->getValue().asBoolean();
    bool use_access_group = self->mGroupAccessCheck->getValue().asBoolean();
    LLCtrlSelectionInterface* passcombo = self->mTemporaryPassCombo;
    if (passcombo)
    {
        if (use_access_group && use_pass_list)
        {
            if (passcombo->getSelectedValue().asString() == "group")
            {
                passcombo->selectByValue("anyone");
            }
        }
    }

    onCommitAny(ctrl, userdata);
}

// static
void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata)
{
    LLPanelLandAccess *self = (LLPanelLandAccess *)userdata;

    LLParcel* parcel = self->mParcel->getParcel();
    if (!parcel)
    {
        return;
    }

    // Extract data from UI
    bool public_access = self->mPublicAccessCheck->getValue().asBoolean();
    bool use_access_group = self->mGroupAccessCheck->getValue().asBoolean();
    if (use_access_group)
    {
        std::string group_name;
        if (!gCacheName->getGroupName(parcel->getGroupID(), group_name))
        {
            use_access_group = false;
        }
    }

    bool limit_payment = false, limit_age_verified = false;
    bool use_access_list = false;
    bool use_pass_list = false;

    if (public_access)
    {
        use_access_list = false;
        limit_payment = self->mPaymentInfoCheck->getValue().asBoolean();
        limit_age_verified = self->mAgeVerifiedCheck->getValue().asBoolean();
    }
    else
    {
        use_access_list = true;
        use_pass_list = self->mTemporaryPassCheck->getValue().asBoolean();
        LLCtrlSelectionInterface* passcombo = self->mTemporaryPassCombo;
        if (passcombo)
        {
            if (use_access_group && use_pass_list)
            {
                if (passcombo->getSelectedValue().asString() == "group")
                {
                    use_access_group = false;
                }
            }
        }
    }

    S32 pass_price = llfloor((F32)self->mTemporaryPassPriceSpin->getValue().asReal());
    F32 pass_hours = (F32)self->mTemporaryPassHourSpin->getValue().asReal();

    // Push data into current parcel
    parcel->setParcelFlag(PF_USE_ACCESS_GROUP,  use_access_group);
    parcel->setParcelFlag(PF_USE_ACCESS_LIST,   use_access_list);
    parcel->setParcelFlag(PF_USE_PASS_LIST,     use_pass_list);
    parcel->setParcelFlag(PF_USE_BAN_LIST,      true);
    parcel->setParcelFlag(PF_DENY_ANONYMOUS,    limit_payment);
    parcel->setParcelFlag(PF_DENY_AGEUNVERIFIED, limit_age_verified);

    parcel->setPassPrice( pass_price );
    parcel->setPassHours( pass_hours );

    // Send current parcel data upstream to server
    LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel );

    // Might have changed properties, so let's redraw!
    self->refresh();
}

void LLPanelLandAccess::onClickAddAccess()
{
    LLFloater * root_floater = gFloaterView->getParentFloater(this);
    LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
        boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1), false, false, false, root_floater->getName(), mBtnAddAllowed);
    if (picker)
    {
        root_floater->addDependentFloater(picker);
    }
}

void LLPanelLandAccess::callbackAvatarCBAccess(const uuid_vec_t& ids)
{
    if (!ids.empty())
    {
        LLUUID id = ids[0];
        LLParcel* parcel = mParcel->getParcel();
        if (parcel && parcel->addToAccessList(id, 0))
        {
            U32 lists_to_update = AL_ACCESS;
            // agent was successfully added to access list
            // but we also need to check ban list to ensure that agent will not be in two lists simultaneously
            if(parcel->removeFromBanList(id))
            {
                lists_to_update |= AL_BAN;
            }
            LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(lists_to_update);
            refresh();
        }
    }
}

void LLPanelLandAccess::onClickRemoveAccess()
{
    if (mListAccess)
    {
        LLParcel* parcel = mParcel->getParcel();
        if (parcel)
        {
            std::vector<LLScrollListItem*> names = mListAccess->getAllSelected();
            for (std::vector<LLScrollListItem*>::iterator iter = names.begin();
                 iter != names.end(); )
            {
                LLScrollListItem* item = *iter++;
                const LLUUID& agent_id = item->getUUID();
                parcel->removeFromAccessList(agent_id);
            }
            LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(AL_ACCESS);
            refresh();
        }
    }
}

void LLPanelLandAccess::onClickAddBanned()
{
    LLFloater * root_floater = gFloaterView->getParentFloater(this);
    LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
        boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1), true, false, false, root_floater->getName(), mBtnAddBanned);
    if (picker)
    {
        root_floater->addDependentFloater(picker);
    }
}

// static
void LLPanelLandAccess::callbackAvatarCBBanned(const uuid_vec_t& ids)
{
    LLFloater * root_floater = gFloaterView->getParentFloater(this);
    LLFloaterBanDuration* duration_floater = LLFloaterBanDuration::show(
        boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned2, this, _1, _2), ids);
    if (duration_floater)
    {
        root_floater->addDependentFloater(duration_floater);
    }
}

void LLPanelLandAccess::callbackAvatarCBBanned2(const uuid_vec_t& ids, S32 duration)
{
    LLParcel* parcel = mParcel->getParcel();
    if (!parcel) return;

    U32 lists_to_update = 0;

    for (uuid_vec_t::const_iterator it = ids.begin(); it < ids.end(); it++)
    {
        LLUUID id = *it;
        if (parcel->addToBanList(id, duration))
        {
            lists_to_update |= AL_BAN;
            // agent was successfully added to ban list
            // but we also need to check access list to ensure that agent will not be in two lists simultaneously
            if (parcel->removeFromAccessList(id))
            {
                lists_to_update |= AL_ACCESS;
            }
        }
    }
    if (lists_to_update > 0)
    {
        LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(lists_to_update);
        refresh();
    }
}

void LLPanelLandAccess::onClickRemoveBanned()
{
    if (mListBanned)
    {
        LLParcel* parcel = mParcel->getParcel();
        if (parcel)
        {
            std::vector<LLScrollListItem*> names = mListBanned->getAllSelected();
            for (std::vector<LLScrollListItem*>::iterator iter = names.begin();
                 iter != names.end(); )
            {
                LLScrollListItem* item = *iter++;
                const LLUUID& agent_id = item->getUUID();
                parcel->removeFromBanList(agent_id);
            }
            LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(AL_BAN);
            refresh();
        }
    }
}

//---------------------------------------------------------------------------
// LLPanelLandCovenant
//---------------------------------------------------------------------------
LLPanelLandCovenant::LLPanelLandCovenant(LLParcelSelectionHandle& parcel)
    : LLPanel(),
      mParcel(parcel),
      mNextUpdateTime(0)
{
}

LLPanelLandCovenant::~LLPanelLandCovenant()
{
}

bool LLPanelLandCovenant::postBuild()
{
    mLastRegionID = LLUUID::null;
    mNextUpdateTime = 0;
    mTextEstateOwner = getChild<LLTextBox>("estate_owner_text");
    mTextEstateOwner->setIsFriendCallback(LLAvatarActions::isFriend);
    return true;
}

// virtual
void LLPanelLandCovenant::refresh()
{
    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if(!region || gDisconnected) return;

    LLTextBox* region_name = getChild<LLTextBox>("region_name_text");
    if (region_name)
    {
        region_name->setText(region->getName());
    }

    LLTextBox* region_landtype = getChild<LLTextBox>("region_landtype_text");
    region_landtype->setText(region->getLocalizedSimProductName());

    LLTextBox* region_maturity = getChild<LLTextBox>("region_maturity_text");
    if (region_maturity)
    {
        insert_maturity_into_textbox(region_maturity, gFloaterView->getParentFloater(this), MATURITY);
    }

    LLTextBox* resellable_clause = getChild<LLTextBox>("resellable_clause");
    if (resellable_clause)
    {
        if (region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL))
        {
            resellable_clause->setText(getString("can_not_resell"));
        }
        else
        {
            resellable_clause->setText(getString("can_resell"));
        }
    }

    LLTextBox* changeable_clause = getChild<LLTextBox>("changeable_clause");
    if (changeable_clause)
    {
        if (region->getRegionFlag(REGION_FLAGS_ALLOW_PARCEL_CHANGES))
        {
            changeable_clause->setText(getString("can_change"));
        }
        else
        {
            changeable_clause->setText(getString("can_not_change"));
        }
    }

    if (mLastRegionID != region->getRegionID()
        || mNextUpdateTime < LLTimer::getElapsedSeconds())
    {
        // Request Covenant Info
        // Note: LLPanelLandCovenant doesn't change Covenant's content and any
        // changes made by Estate floater should be requested by Estate floater
        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());

        mLastRegionID = region->getRegionID();
        mNextUpdateTime = LLTimer::getElapsedSeconds() + COVENANT_REFRESH_TIME_SEC;
    }
}

// static
void LLPanelLandCovenant::updateCovenant(const LLTextBase* source)
{
    if (LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant())
    {
        LLViewerTextEditor* editor = self->getChild<LLViewerTextEditor>("covenant_editor");
        editor->copyContents(source);
    }
}

// static
void LLPanelLandCovenant::updateCovenantText(const std::string &string)
{
    LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
    if (self)
    {
        LLViewerTextEditor* editor = self->getChild<LLViewerTextEditor>("covenant_editor");
        editor->setText(string);
    }
}

// static
void LLPanelLandCovenant::updateEstateName(const std::string& name)
{
    LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
    if (self)
    {
        LLTextBox* editor = self->getChild<LLTextBox>("estate_name_text");
        if (editor) editor->setText(name);
    }
}

// static
void LLPanelLandCovenant::updateLastModified(const std::string& text)
{
    LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
    if (self)
    {
        LLTextBox* editor = self->getChild<LLTextBox>("covenant_timestamp_text");
        if (editor) editor->setText(text);
    }
}

// static
void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name)
{
    LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
    if (self)
    {
        self->mTextEstateOwner->setText(name);
    }
}

// inserts maturity info(icon and text) into target textbox
// names_floater - pointer to floater which contains strings with maturity icons filenames
// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY]
void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse)
{
    LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
    if (!region)
        return;

    LLStyle::Params style;

    U8 sim_access = region->getSimAccess();

    switch(sim_access)
    {
    case SIM_ACCESS_PG:
        style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_general")));
        break;

    case SIM_ACCESS_ADULT:
        style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_adult")));
        break;

    case SIM_ACCESS_MATURE:
        style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_moderate")));
        break;

    default:
        break;
    }

    size_t maturity_pos = str_to_parse.find(MATURITY);

    if (maturity_pos == std::string::npos)
    {
        return;
    }

    std::string text_before_rating = str_to_parse.substr(0, maturity_pos);
    std::string text_after_rating = str_to_parse.substr(maturity_pos + MATURITY.length());

    target_textbox->setText(text_before_rating);

    target_textbox->appendImageSegment(style);

    target_textbox->appendText(LLViewerParcelMgr::getInstance()->getSelectionRegion()->getSimAccessString(), false);
    target_textbox->appendText(text_after_rating, false);
}

LLPanelLandExperiences::LLPanelLandExperiences( LLSafeHandle<LLParcelSelection>& parcelp )
    : mParcel(parcelp)
{

}


bool LLPanelLandExperiences::postBuild()
{
    mAllowed = setupList("panel_allowed", EXPERIENCE_KEY_TYPE_ALLOWED, AL_ALLOW_EXPERIENCE);
    mBlocked = setupList("panel_blocked", EXPERIENCE_KEY_TYPE_BLOCKED, AL_BLOCK_EXPERIENCE);

    // only non-grid-wide experiences
    mAllowed->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_GRID));

    // no privileged ones
    mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithoutProperties, _1, LLExperienceCache::PROPERTY_PRIVILEGED|LLExperienceCache::PROPERTY_GRID));

    getChild<LLLayoutPanel>("trusted_layout_panel")->setVisible(false);
    getChild<LLTextBox>("experiences_help_text")->setVisible(false);
    getChild<LLTextBox>("allowed_text_help")->setText(getString("allowed_parcel_text"));
    getChild<LLTextBox>("blocked_text_help")->setText(getString("blocked_parcel_text"));

    return LLPanel::postBuild();
}

LLPanelExperienceListEditor* LLPanelLandExperiences::setupList( const char* control_name, U32 xp_type, U32 access_type )
{
    LLPanelExperienceListEditor* child = findChild<LLPanelExperienceListEditor>(control_name);
    if(child)
    {
        child->getChild<LLTextBox>("text_name")->setText(child->getString(control_name));
        child->setMaxExperienceIDs(PARCEL_MAX_EXPERIENCE_LIST);
        child->setAddedCallback(boost::bind(&LLPanelLandExperiences::experienceAdded, this, _1, xp_type, access_type));
        child->setRemovedCallback(boost::bind(&LLPanelLandExperiences::experienceRemoved, this, _1, access_type));
    }

    return child;
}

void LLPanelLandExperiences::experienceAdded( const LLUUID& id, U32 xp_type, U32 access_type )
{
    LLParcel* parcel = mParcel->getParcel();
    if (parcel)
    {
        parcel->setExperienceKeyType(id, xp_type);
        LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(access_type);
        refresh();
    }
}

void LLPanelLandExperiences::experienceRemoved( const LLUUID& id, U32 access_type )
{
    LLParcel* parcel = mParcel->getParcel();
    if (parcel)
    {
        parcel->setExperienceKeyType(id, EXPERIENCE_KEY_TYPE_NONE);
        LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(access_type);
        refresh();
    }
}

void LLPanelLandExperiences::refreshPanel(LLPanelExperienceListEditor* panel, U32 xp_type)
{
    LLParcel *parcel = mParcel->getParcel();

    // Display options
    if (panel == NULL)
    {
        return;
    }
    if (!parcel || gDisconnected)
    {
        // disable the panel
        panel->setEnabled(false);
        panel->setExperienceIds(LLSD::emptyArray());
    }
    else
    {
        // enable the panel
        panel->setEnabled(true);
        LLAccessEntry::map entries = parcel->getExperienceKeysByType(xp_type);
        LLAccessEntry::map::iterator it = entries.begin();
        LLSD ids = LLSD::emptyArray();
        for (/**/; it != entries.end(); ++it)
        {
            ids.append(it->second.mID);
        }
        panel->setExperienceIds(ids);
        panel->setReadonly(!LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS));
        panel->refreshExperienceCounter();
    }
}

void LLPanelLandExperiences::refresh()
{
    refreshPanel(mAllowed, EXPERIENCE_KEY_TYPE_ALLOWED);
    refreshPanel(mBlocked, EXPERIENCE_KEY_TYPE_BLOCKED);
}

//=========================================================================

LLPanelLandEnvironment::LLPanelLandEnvironment(LLParcelSelectionHandle& parcel) :
    LLPanelEnvironmentInfo(),
    mParcel(parcel),
    mLastParcelId(INVALID_PARCEL_ID)
{
}

bool LLPanelLandEnvironment::postBuild()
{
    if (!LLPanelEnvironmentInfo::postBuild())
        return false;

    mBtnUseDefault->setLabelArg("[USEDEFAULT]", getString(STR_LABEL_USEREGION));
    mCheckAllowOverride->setVisible(false);
    mPanelEnvRegionMsg->setVisible(false);
    mPanelEnvAltitudes->setVisible(true);

    return true;
}

void LLPanelLandEnvironment::refresh()
{
    if (gDisconnected)
        return;

    commitDayLenOffsetChanges(false); // commit unsaved changes if any

    if (!isSameRegion())
    {
        setCrossRegion(true);
        mCurrentEnvironment.reset();
        mLastParcelId = INVALID_PARCEL_ID;
        mCurEnvVersion = INVALID_PARCEL_ENVIRONMENT_VERSION;
        setControlsEnabled(false);
        return;
    }

    if (mLastParcelId != getParcelId())
    {
        mCurEnvVersion = INVALID_PARCEL_ENVIRONMENT_VERSION;
        mCurrentEnvironment.reset();
    }

    if (!mCurrentEnvironment && mCurEnvVersion <= INVALID_PARCEL_ENVIRONMENT_VERSION)
    {
        refreshFromSource();
        return;
    }

    LLPanelEnvironmentInfo::refresh();
}

void LLPanelLandEnvironment::refreshFromSource()
{
    LLParcel *parcel = getParcel();

    if (!LLEnvironment::instance().isExtendedEnvironmentEnabled())
    {
        setNoEnvironmentSupport(true);
        setControlsEnabled(false);
        mCurEnvVersion = INVALID_PARCEL_ENVIRONMENT_VERSION;
        return;
    }
    setNoEnvironmentSupport(false);

    if (!parcel)
    {
        setNoSelection(true);
        setControlsEnabled(false);
        mCurEnvVersion = INVALID_PARCEL_ENVIRONMENT_VERSION;
        return;
    }

    setNoSelection(false);
    if (isSameRegion())
    {
        LL_DEBUGS("ENVIRONMENT") << "Requesting environment for parcel " << parcel->getLocalID() << ", known version " << mCurEnvVersion << LL_ENDL;
        setCrossRegion(false);

        LLHandle<LLPanel> that_h = getHandle();

        if (mCurEnvVersion < UNSET_PARCEL_ENVIRONMENT_VERSION)
        {
            // to mark as requesting
            mCurEnvVersion = parcel->getParcelEnvironmentVersion();
        }
        mLastParcelId = parcel->getLocalID();

        LLEnvironment::instance().requestParcel(parcel->getLocalID(),
            [that_h](S32 parcel_id, LLEnvironment::EnvironmentInfo::ptr_t envifo)
            {
                LLPanelLandEnvironment *that = (LLPanelLandEnvironment*)that_h.get();
                if (!that) return;
                that->mLastParcelId = parcel_id;
                that->onEnvironmentReceived(parcel_id, envifo);
            });
    }
    else
    {
        setCrossRegion(true);
        mCurrentEnvironment.reset();
        mLastParcelId = INVALID_PARCEL_ID;
        mCurEnvVersion = INVALID_PARCEL_ENVIRONMENT_VERSION;
    }
    setControlsEnabled(false);
}


bool LLPanelLandEnvironment::isSameRegion()
{
    LLViewerRegion* regionp = LLViewerParcelMgr::instance().getSelectionRegion();

    return (!regionp || (regionp->getRegionID() == gAgent.getRegion()->getRegionID()));
}

LLParcel *LLPanelLandEnvironment::getParcel()
{
    return mParcel->getParcel();
}


bool LLPanelLandEnvironment::canEdit()
{
    LLParcel *parcel = getParcel();
    if (!parcel)
        return false;

    return LLEnvironment::instance().canAgentUpdateParcelEnvironment(parcel) && mAllowOverride;
}

S32 LLPanelLandEnvironment::getParcelId()
{
    LLParcel *parcel = getParcel();
    if (!parcel)
        return INVALID_PARCEL_ID;

    return parcel->getLocalID();
}