Files
SingularityViewer/indra/newview/llpanelclassified.cpp
Lirusaito 7303bfcb78 Eliminated most instances of the legacy setCallbackUserData (and modernized up a bunch of callbacks)
Also cleaned up slfloatermediafilter to act as a proper singleton.
Removes childSetUserData entirely.
2013-06-16 20:00:00 -04:00

1013 lines
28 KiB
C++

/**
* @file llpanelclassified.cpp
* @brief LLPanelClassified class implementation
*
* $LicenseInfo:firstyear=2005&license=viewergpl$
*
* Copyright (c) 2005-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
// Display of a classified used both for the global view in the
// Find directory, and also for each individual user's classified in their
// profile.
#include "llviewerprecompiledheaders.h"
#include "llpanelclassified.h"
#include "lldir.h"
#include "lldispatcher.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llparcel.h"
#include "message.h"
#include "llagent.h"
#include "llavataractions.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llclassifiedflags.h"
#include "llclassifiedstatsresponder.h"
#include "llcommandhandler.h" // for classified HTML detail page click tracking
#include "lllineeditor.h"
#include "llfloaterclassified.h"
#include "lltextbox.h"
#include "llcombobox.h"
#include "llviewertexteditor.h"
#include "lltexturectrl.h"
#include "llurldispatcher.h" // for classified HTML detail click teleports
#include "lluictrlfactory.h"
#include "llviewerparcelmgr.h"
#include "llviewerwindow.h"
#include "llworldmap.h"
#include "llfloaterworldmap.h"
#include "llviewergenericmessage.h" // send_generic_message
#include "llviewerregion.h"
#include "llviewerwindow.h" // for window width, height
#include "llappviewer.h" // abortQuit()
#include "hippogridmanager.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$
const S32 MATURE_UNDEFINED = -1;
const S32 MATURE_CONTENT = 1;
const S32 PG_CONTENT = 2;
const S32 DECLINE_TO_STATE = 0;
//static
std::list<LLPanelClassified*> LLPanelClassified::sAllPanels;
// "classifiedclickthrough"
// strings[0] = classified_id
// strings[1] = teleport_clicks
// strings[2] = map_clicks
// strings[3] = profile_clicks
class LLDispatchClassifiedClickThrough : public LLDispatchHandler
{
public:
virtual bool operator()(
const LLDispatcher* dispatcher,
const std::string& key,
const LLUUID& invoice,
const sparam_t& strings)
{
if (strings.size() != 4) return false;
LLUUID classified_id(strings[0]);
S32 teleport_clicks = atoi(strings[1].c_str());
S32 map_clicks = atoi(strings[2].c_str());
S32 profile_clicks = atoi(strings[3].c_str());
LLPanelClassified::setClickThrough(classified_id, teleport_clicks,
map_clicks,
profile_clicks,
false);
return true;
}
};
static LLDispatchClassifiedClickThrough sClassifiedClickThrough;
/* Re-expose this if we need to have classified ad HTML detail
pages. JC
// We need to count classified teleport clicks from the search HTML detail pages,
// so we need have a teleport that also sends a click count message.
class LLClassifiedTeleportHandler : public LLCommandHandler
{
public:
// don't allow from external browsers because it moves you immediately
LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", true) { }
bool handle(const LLSD& tokens, const LLSD& queryMap)
{
// Need at least classified id and region name, so 2 params
if (tokens.size() < 2) return false;
LLUUID classified_id = tokens[0].asUUID();
if (classified_id.isNull()) return false;
// *HACK: construct a SLURL to do the teleport
std::string url("secondlife:///app/teleport/");
// skip the uuid we took off above, rebuild URL
// separated by slashes.
for (S32 i = 1; i < tokens.size(); ++i)
{
url += tokens[i].asString();
url += "/";
}
llinfos << "classified teleport to " << url << llendl;
// *TODO: separately track old search, sidebar, and new search
// Right now detail HTML pages count as new search.
const bool from_search = true;
LLPanelClassified::sendClassifiedClickMessage(classified_id, "teleport", from_search);
// Invoke teleport
LLMediaCtrl* web = NULL;
const bool trusted_browser = true;
return LLURLDispatcher::dispatch(url, web, trusted_browser);
}
};
// Creating the object registers with the dispatcher.
LLClassifiedTeleportHandler gClassifiedTeleportHandler;
*/
LLPanelClassified::LLPanelClassified(bool in_finder, bool from_search)
: LLPanel(std::string("Classified Panel")),
mInFinder(in_finder),
mFromSearch(from_search),
mDirty(false),
mForceClose(false),
mLocationChanged(false),
mClassifiedID(),
mCreatorID(),
mPriceForListing(0),
mDataRequested(FALSE),
mPaidFor(FALSE),
mPosGlobal(),
mSnapshotCtrl(NULL),
mNameEditor(NULL),
mDescEditor(NULL),
mLocationEditor(NULL),
mCategoryCombo(NULL),
mMatureCombo(NULL),
mAutoRenewCheck(NULL),
mUpdateBtn(NULL),
mTeleportBtn(NULL),
mMapBtn(NULL),
mProfileBtn(NULL),
mInfoText(NULL),
mSetBtn(NULL),
mClickThroughText(NULL),
mTeleportClicksOld(0),
mMapClicksOld(0),
mProfileClicksOld(0),
mTeleportClicksNew(0),
mMapClicksNew(0),
mProfileClicksNew(0)
{
sAllPanels.push_back(this);
std::string classified_def_file;
if (mInFinder)
{
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_classified.xml");
}
else
{
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_classified.xml");
}
// Register dispatcher
gGenericDispatcher.addHandler("classifiedclickthrough",
&sClassifiedClickThrough);
}
LLPanelClassified::~LLPanelClassified()
{
if(mCreatorID.notNull())
{
LLAvatarPropertiesProcessor::getInstance()->removeObserver(mCreatorID, this);
}
sAllPanels.remove(this);
}
void LLPanelClassified::reset()
{
if(mCreatorID.notNull())
{
LLAvatarPropertiesProcessor::getInstance()->removeObserver(mCreatorID, this);
}
mClassifiedID.setNull();
mCreatorID.setNull();
mParcelID.setNull();
// Don't request data, this isn't valid
mDataRequested = TRUE;
mDirty = false;
mPaidFor = FALSE;
mPosGlobal.clearVec();
clearCtrls();
resetDirty();
}
BOOL LLPanelClassified::postBuild()
{
mSnapshotCtrl = getChild<LLTextureCtrl>("snapshot_ctrl");
mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mSnapshotSize = mSnapshotCtrl->getRect();
mNameEditor = getChild<LLLineEditor>("given_name_editor");
mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN);
mNameEditor->setCommitOnFocusLost(TRUE);
mNameEditor->setFocusReceivedCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mNameEditor->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII );
mDescEditor = getChild<LLTextEditor>("desc_editor");
mDescEditor->setCommitOnFocusLost(TRUE);
mDescEditor->setFocusReceivedCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mDescEditor->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mDescEditor->setTabsToNextField(TRUE);
mLocationEditor = getChild<LLLineEditor>("location_editor");
mSetBtn = getChild<LLButton>( "set_location_btn");
mSetBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickSet, this));
mTeleportBtn = getChild<LLButton>( "classified_teleport_btn");
mTeleportBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickTeleport, this));
mMapBtn = getChild<LLButton>( "classified_map_btn");
mMapBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickMap, this));
if(mInFinder)
{
mProfileBtn = getChild<LLButton>( "classified_profile_btn");
mProfileBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickProfile, this));
}
mCategoryCombo = getChild<LLComboBox>( "classified_category_combo");
LLClassifiedInfo::cat_map::iterator iter;
for (iter = LLClassifiedInfo::sCategories.begin();
iter != LLClassifiedInfo::sCategories.end();
iter++)
{
mCategoryCombo->add(iter->second, (void *)((intptr_t)iter->first), ADD_BOTTOM);
}
mCategoryCombo->setCurrentByIndex(0);
mCategoryCombo->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
mMatureCombo = getChild<LLComboBox>( "classified_mature_check");
mMatureCombo->setCurrentByIndex(0);
mMatureCombo->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
if (gAgent.wantsPGOnly())
{
// Teens don't get to set mature flag. JC
mMatureCombo->setVisible(FALSE);
mMatureCombo->setCurrentByIndex(PG_CONTENT);
}
if (!mInFinder)
{
mAutoRenewCheck = getChild<LLCheckBoxCtrl>( "auto_renew_check");
mAutoRenewCheck->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this));
}
mUpdateBtn = getChild<LLButton>("classified_update_btn");
mUpdateBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickUpdate, this));
if (!mInFinder)
{
mClickThroughText = getChild<LLTextBox>("click_through_text");
}
resetDirty();
return TRUE;
}
void LLPanelClassified::processProperties(void* data, EAvatarProcessorType type)
{
if(APT_CLASSIFIED_INFO == type)
{
lldebugs << "processClassifiedInfoReply()" << llendl;
LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data);
if(c_info && mClassifiedID == c_info->classified_id)
{
LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID::null, this);
// "Location text" is actually the original
// name that owner gave the parcel, and the location.
std::string location_text = c_info->parcel_name;
if (!location_text.empty())
location_text.append(", ");
S32 region_x = llround((F32)c_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
S32 region_y = llround((F32)c_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
S32 region_z = llround((F32)c_info->pos_global.mdV[VZ]);
std::string buffer = llformat("%s (%d, %d, %d)", c_info->sim_name.c_str(), region_x, region_y, region_z);
location_text.append(buffer);
//BOOL enabled = is_cf_enabled(flags);
time_t tim = c_info->creation_date;
tm *now=localtime(&tim);
// Found the panel, now fill in the information
mClassifiedID = c_info->classified_id;
mCreatorID = c_info->creator_id;
mParcelID = c_info->parcel_id;
mPriceForListing = c_info->price_for_listing;
mSimName = c_info->sim_name;
mPosGlobal = c_info->pos_global;
// Update UI controls
mNameEditor->setText(c_info->name);
mDescEditor->setText(c_info->description);
mSnapshotCtrl->setImageAssetID(c_info->snapshot_id);
mLocationEditor->setText(location_text);
mLocationChanged = false;
mCategoryCombo->setCurrentByIndex(c_info->category - 1);
mMatureCombo->setCurrentByIndex(is_cf_mature(c_info->flags) ? MATURE_CONTENT : PG_CONTENT);
if (mAutoRenewCheck)
{
mAutoRenewCheck->set(is_cf_auto_renew(c_info->flags));
}
std::string datestr;
timeStructToFormattedString(now, gSavedSettings.getString("ShortDateFormat"), datestr);
LLStringUtil::format_map_t string_args;
string_args["[DATE]"] = datestr;
string_args["[CURRENCY]"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol();
string_args["[AMT]"] = llformat("%d", c_info->price_for_listing);
childSetText("classified_info_text", getString("ad_placed_paid", string_args));
// If we got data from the database, we know the listing is paid for.
mPaidFor = TRUE;
mUpdateBtn->setLabel(getString("update_txt"));
resetDirty();
}
}
}
BOOL LLPanelClassified::titleIsValid()
{
// Disallow leading spaces, punctuation, etc. that screw up
// sort order.
const std::string& name = mNameEditor->getText();
if (name.empty())
{
LLNotificationsUtil::add("BlankClassifiedName");
return FALSE;
}
if (!isalnum(name[0]))
{
LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric");
return FALSE;
}
return TRUE;
}
void LLPanelClassified::apply()
{
// Apply is used for automatically saving results, so only
// do that if there is a difference, and this is a save not create.
if (checkDirty() && mPaidFor)
{
sendClassifiedInfoUpdate();
}
}
bool LLPanelClassified::saveCallback(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
switch(option)
{
case 0: // Save
sendClassifiedInfoUpdate();
// fall through to close
case 1: // Don't Save
{
mForceClose = true;
// Close containing floater
LLFloater* parent_floater = gFloaterView->getParentFloater(this);
if (parent_floater)
{
parent_floater->close();
}
}
break;
case 2: // Cancel
default:
LLAppViewer::instance()->abortQuit();
break;
}
return false;
}
BOOL LLPanelClassified::canClose()
{
if (mForceClose || !checkDirty())
return TRUE;
LLSD args;
args["NAME"] = mNameEditor->getText();
LLNotificationsUtil::add("ClassifiedSave", args, LLSD(), boost::bind(&LLPanelClassified::saveCallback, this, _1, _2));
return FALSE;
}
// Fill in some reasonable defaults for a new classified.
void LLPanelClassified::initNewClassified()
{
// TODO: Don't generate this on the client.
mClassifiedID.generate();
mCreatorID = gAgent.getID();
mPosGlobal = gAgent.getPositionGlobal();
mPaidFor = FALSE;
// Try to fill in the current parcel
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if (parcel)
{
mNameEditor->setText(parcel->getName());
//mDescEditor->setText(parcel->getDesc());
mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
//mPriceEditor->setText("0");
mCategoryCombo->setCurrentByIndex(0);
}
mUpdateBtn->setLabel(getString("publish_txt"));
// simulate clicking the "location" button
onClickSet();
}
void LLPanelClassified::setClassifiedID(const LLUUID& id)
{
mClassifiedID = id;
}
//static
void LLPanelClassified::setClickThrough(const LLUUID& classified_id,
S32 teleport,
S32 map,
S32 profile,
bool from_new_table)
{
for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter)
{
LLPanelClassified* self = *iter;
// For top picks, must match pick id
if (self->mClassifiedID != classified_id)
{
continue;
}
// We need to check to see if the data came from the new stat_table
// or the old classified table. We also need to cache the data from
// the two separate sources so as to display the aggregate totals.
if (from_new_table)
{
self->mTeleportClicksNew = teleport;
self->mMapClicksNew = map;
self->mProfileClicksNew = profile;
}
else
{
self->mTeleportClicksOld = teleport;
self->mMapClicksOld = map;
self->mProfileClicksOld = profile;
}
if (self->mClickThroughText)
{
std::string msg = llformat("Clicks: %d teleport, %d map, %d profile",
self->mTeleportClicksNew + self->mTeleportClicksOld,
self->mMapClicksNew + self->mMapClicksOld,
self->mProfileClicksNew + self->mProfileClicksOld);
self->mClickThroughText->setText(msg);
}
}
}
// Schedules the panel to request data
// from the server next time it is drawn.
void LLPanelClassified::markForServerRequest()
{
mDataRequested = FALSE;
}
std::string LLPanelClassified::getClassifiedName()
{
return mNameEditor->getText();
}
void LLPanelClassified::sendClassifiedInfoRequest()
{
if (mClassifiedID != mRequestedID)
{
LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID::null, this);
LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(mClassifiedID);
mDataRequested = TRUE;
mRequestedID = mClassifiedID;
// While we're at it let's get the stats from the new table if that
// capability exists.
std::string url = gAgent.getRegion()->getCapability("SearchStatRequest");
LLSD body;
body["classified_id"] = mClassifiedID;
if (!url.empty())
{
llinfos << "Classified stat request via capability" << llendl;
LLHTTPClient::post(url, body, new LLClassifiedStatsResponder(((LLView*)this)->getHandle(), mClassifiedID));
}
}
}
void LLPanelClassified::sendClassifiedInfoUpdate()
{
LLAvatarClassifiedInfo c_data;
// If we don't have a classified id yet, we'll need to generate one,
// otherwise we'll keep overwriting classified_id 00000 in the database.
if (mClassifiedID.isNull())
{
// TODO: Don't do this on the client.
mClassifiedID.generate();
}
c_data.agent_id = gAgent.getID();
c_data.classified_id = mClassifiedID;
c_data.category = mCategoryCombo->getCurrentIndex() + 1;
c_data.name = mNameEditor->getText();
c_data.description = mDescEditor->getText();
c_data.parcel_id = mParcelID;
c_data.snapshot_id = mSnapshotCtrl->getImageAssetID();
c_data.pos_global = mPosGlobal;
BOOL auto_renew = mAutoRenewCheck && mAutoRenewCheck->get();
c_data.flags = pack_classified_flags_request(auto_renew, false, mMatureCombo->getCurrentIndex() == MATURE_CONTENT, false);
c_data.price_for_listing = mPriceForListing;
c_data.parent_estate = 0; //probably not required.
LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoUpdate(&c_data);
mDirty = false;
}
void LLPanelClassified::draw()
{
refresh();
LLPanel::draw();
}
void LLPanelClassified::refresh()
{
if (!mDataRequested)
{
sendClassifiedInfoRequest();
}
// Check for god mode
BOOL godlike = gAgent.isGodlike();
BOOL is_self = (gAgent.getID() == mCreatorID);
// Set button visibility/enablement appropriately
if (mInFinder)
{
// End user doesn't ned to see price twice, or date posted.
mSnapshotCtrl->setEnabled(godlike);
if(godlike)
{
//make it smaller, so text is more legible
mSnapshotCtrl->setOrigin(20, 175);
mSnapshotCtrl->reshape(300, 200);
}
else
{
mSnapshotCtrl->setOrigin(mSnapshotSize.mLeft, mSnapshotSize.mBottom);
mSnapshotCtrl->reshape(mSnapshotSize.getWidth(), mSnapshotSize.getHeight());
//normal
}
mNameEditor->setEnabled(godlike);
mDescEditor->setEnabled(godlike);
mCategoryCombo->setEnabled(godlike);
mCategoryCombo->setVisible(godlike);
mMatureCombo->setEnabled(godlike);
mMatureCombo->setVisible(godlike);
// Jesse (who is the only one who uses this, as far as we can tell
// Says that he does not want a set location button - he has used it
// accidently in the past.
mSetBtn->setVisible(FALSE);
mSetBtn->setEnabled(FALSE);
mUpdateBtn->setEnabled(godlike);
mUpdateBtn->setVisible(godlike);
}
else
{
mSnapshotCtrl->setEnabled(is_self);
mNameEditor->setEnabled(is_self);
mDescEditor->setEnabled(is_self);
//mPriceEditor->setEnabled(is_self);
mCategoryCombo->setEnabled(is_self);
mMatureCombo->setEnabled(is_self);
if( is_self )
{
if( mMatureCombo->getCurrentIndex() == 0 )
{
// It's a new panel.
// PG regions should have PG classifieds. AO should have mature.
setDefaultAccessCombo();
}
}
if (mAutoRenewCheck)
{
mAutoRenewCheck->setEnabled(is_self);
mAutoRenewCheck->setVisible(is_self);
}
mClickThroughText->setEnabled(is_self);
mClickThroughText->setVisible(is_self);
mSetBtn->setVisible(is_self);
//mSetBtn->setEnabled(is_self);
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a)
mSetBtn->setEnabled(is_self && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) );
// [/RLVa:KB]
mUpdateBtn->setEnabled(is_self && checkDirty());
mUpdateBtn->setVisible(is_self);
}
}
void LLPanelClassified::onClickUpdate()
{
// Disallow leading spaces, punctuation, etc. that screw up
// sort order.
if (!titleIsValid())
{
return;
}
// If user has not set mature, do not allow publish
if (mMatureCombo->getCurrentIndex() == DECLINE_TO_STATE)
{
// Tell user about it
LLNotificationsUtil::add("SetClassifiedMature",
LLSD(),
LLSD(),
boost::bind(&LLPanelClassified::confirmMature, this, _1, _2));
return;
}
// Mature content flag is set, proceed
gotMature();
}
// Callback from a dialog indicating response to mature notification
bool LLPanelClassified::confirmMature(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
// 0 == Yes
// 1 == No
// 2 == Cancel
switch(option)
{
case 0:
mMatureCombo->setCurrentByIndex(MATURE_CONTENT);
break;
case 1:
mMatureCombo->setCurrentByIndex(PG_CONTENT);
break;
default:
return false;
}
// If we got here it means they set a valid value
gotMature();
return false;
}
// Called after we have determined whether this classified has
// mature content or not.
void LLPanelClassified::gotMature()
{
// if already paid for, just do the update
if (mPaidFor)
{
LLNotification::Params params("PublishClassified");
params.functor(boost::bind(&LLPanelClassified::confirmPublish, this, _1, _2));
LLNotifications::instance().forceResponse(params, 0);
}
else
{
// Ask the user how much they want to pay
new LLFloaterPriceForListing(boost::bind(&LLPanelClassified::callbackGotPriceForListing, this, _1));
}
}
void LLPanelClassified::callbackGotPriceForListing(const std::string& text)
{
S32 price_for_listing = strtol(text.c_str(), NULL, 10);
if (price_for_listing < MINIMUM_PRICE_FOR_LISTING)
{
LLSD args;
args["MIN_PRICE"] = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
LLNotificationsUtil::add("MinClassifiedPrice", args);
return;
}
// price is acceptable, put it in the dialog for later read by
// update send
mPriceForListing = price_for_listing;
LLSD args;
args["AMOUNT"] = llformat("%d", price_for_listing);
args["CURRENCY"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol();
LLNotificationsUtil::add("PublishClassified", args, LLSD(),
boost::bind(&LLPanelClassified::confirmPublish, this, _1, _2));
}
void LLPanelClassified::resetDirty()
{
// Tell all the widgets to reset their dirty state since the ad was just saved
if (mSnapshotCtrl)
mSnapshotCtrl->resetDirty();
if (mNameEditor)
mNameEditor->resetDirty();
if (mDescEditor)
mDescEditor->resetDirty();
if (mLocationEditor)
mLocationEditor->resetDirty();
mLocationChanged = false;
if (mCategoryCombo)
mCategoryCombo->resetDirty();
if (mMatureCombo)
mMatureCombo->resetDirty();
if (mAutoRenewCheck)
mAutoRenewCheck->resetDirty();
}
// invoked from callbackConfirmPublish
bool LLPanelClassified::confirmPublish(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
// Option 0 = publish
if (option != 0) return false;
sendClassifiedInfoUpdate();
// Big hack - assume that top picks are always in a browser,
// and non-finder-classifieds are always in a tab container.
if (mInFinder)
{
// TODO: enable this
//LLPanelDirClassifieds* panel = (LLPanelDirClassifieds*)getParent();
//panel->renameClassified(mClassifiedID, mNameEditor->getText());
}
else
{
LLTabContainer* tab = (LLTabContainer*)getParent();
tab->setCurrentTabName(mNameEditor->getText());
}
resetDirty();
return false;
}
void LLPanelClassified::onClickTeleport()
{
if (!mPosGlobal.isExactlyZero())
{
gAgent.teleportViaLocation(mPosGlobal);
gFloaterWorldMap->trackLocation(mPosGlobal);
sendClassifiedClickMessage("teleport");
}
}
void LLPanelClassified::onClickMap()
{
gFloaterWorldMap->trackLocation(mPosGlobal);
LLFloaterWorldMap::show(true);
sendClassifiedClickMessage("map");
}
void LLPanelClassified::onClickProfile()
{
LLAvatarActions::showProfile(mCreatorID);
sendClassifiedClickMessage("profile");
}
/*
void LLPanelClassified::onClickLandmark()
{
create_landmark(mNameEditor->getText(), "", mPosGlobal);
}
*/
void LLPanelClassified::onClickSet()
{
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a)
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
{
return;
}
// [/RLVa:KB]
// Save location for later.
mPosGlobal = gAgent.getPositionGlobal();
std::string location_text;
std::string regionName = "(will update after publish)";
LLViewerRegion* pRegion = gAgent.getRegion();
if (pRegion)
{
regionName = pRegion->getName();
}
location_text.assign(regionName);
location_text.append(", ");
S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
S32 region_z = llround((F32)mPosGlobal.mdV[VZ]);
location_text.append(mSimName);
location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
mLocationEditor->setText(location_text);
mLocationChanged = true;
setDefaultAccessCombo();
// Set this to null so it updates on the next save.
mParcelID.setNull();
checkDirty();
}
BOOL LLPanelClassified::checkDirty()
{
mDirty = FALSE;
if ( mSnapshotCtrl ) mDirty |= mSnapshotCtrl->isDirty();
if ( mNameEditor ) mDirty |= mNameEditor->isDirty();
if ( mDescEditor ) mDirty |= mDescEditor->isDirty();
if ( mLocationEditor ) mDirty |= mLocationEditor->isDirty();
if ( mLocationChanged ) mDirty |= TRUE;
if ( mCategoryCombo ) mDirty |= mCategoryCombo->isDirty();
if ( mMatureCombo ) mDirty |= mMatureCombo->isDirty();
if ( mAutoRenewCheck ) mDirty |= mAutoRenewCheck->isDirty();
return mDirty;
}
void LLPanelClassified::sendClassifiedClickMessage(const std::string& type)
{
// You're allowed to click on your own ads to reassure yourself
// that the system is working.
LLSD body;
body["type"] = type;
body["from_search"] = mFromSearch;
body["classified_id"] = mClassifiedID;
body["parcel_id"] = mParcelID;
body["dest_pos_global"] = mPosGlobal.getValue();
body["region_name"] = mSimName;
std::string url = gAgent.getRegion()->getCapability("SearchStatTracking");
llinfos << "LLPanelClassified::sendClassifiedClickMessage via capability" << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore);
}
////////////////////////////////////////////////////////////////////////////////////////////
LLFloaterPriceForListing::LLFloaterPriceForListing(const signal_t::slot_type& cb)
: LLFloater(std::string("PriceForListing")),
mSignal(new signal_t)
{
// Builds and adds to gFloaterView
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_price_for_listing.xml");
center();
mSignal->connect(cb);
}
//virtual
LLFloaterPriceForListing::~LLFloaterPriceForListing()
{
delete mSignal;
}
//virtual
BOOL LLFloaterPriceForListing::postBuild()
{
LLLineEditor* edit = getChild<LLLineEditor>("price_edit");
if (edit)
{
edit->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
std::string min_price = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
edit->setText(min_price);
edit->selectAll();
edit->setFocus(TRUE);
}
getChild<LLUICtrl>("set_price_btn")->setCommitCallback(boost::bind(&LLFloaterPriceForListing::buttonCore, this));
getChild<LLUICtrl>("cancel_btn")->setCommitCallback(boost::bind(&LLFloater::close, this, false));
setDefaultBtn("set_price_btn");
return TRUE;
}
void LLFloaterPriceForListing::buttonCore()
{
(*mSignal)(childGetText("price_edit"));
close();
}
void LLPanelClassified::setDefaultAccessCombo()
{
// PG regions should have PG classifieds. AO should have mature.
LLViewerRegion *regionp = gAgent.getRegion();
switch( regionp->getSimAccess() )
{
case SIM_ACCESS_PG:
mMatureCombo->setCurrentByIndex(PG_CONTENT);
break;
case SIM_ACCESS_ADULT:
mMatureCombo->setCurrentByIndex(MATURE_CONTENT);
break;
default:
// You are free to move about the cabin.
break;
}
}