llpanelclassified.cpp

Go to the documentation of this file.
00001 
00032 // Display of a classified used both for the global view in the
00033 // Find directory, and also for each individual user's classified in their
00034 // profile.
00035 
00036 #include "llviewerprecompiledheaders.h"
00037 
00038 #include "llpanelclassified.h"
00039 
00040 #include "lldir.h"
00041 #include "lldispatcher.h"
00042 #include "llparcel.h"
00043 #include "message.h"
00044 
00045 #include "llagent.h"
00046 #include "llalertdialog.h"
00047 #include "llbutton.h"
00048 #include "llcheckboxctrl.h"
00049 #include "llclassifiedflags.h"
00050 #include "llclassifiedstatsresponder.h"
00051 #include "llcommandhandler.h" // for classified HTML detail page click tracking
00052 #include "llviewercontrol.h"
00053 #include "lllineeditor.h"
00054 #include "llfloateravatarinfo.h"
00055 #include "llfloaterclassified.h"
00056 #include "lltabcontainervertical.h"
00057 #include "lltextbox.h"
00058 #include "llcombobox.h"
00059 #include "llviewertexteditor.h"
00060 #include "lltexturectrl.h"
00061 #include "lluiconstants.h"
00062 #include "llurldispatcher.h"    // for classified HTML detail click teleports
00063 #include "lluictrlfactory.h"
00064 #include "llviewerparcelmgr.h"
00065 #include "llviewerwindow.h"
00066 #include "llworldmap.h"
00067 #include "llfloaterworldmap.h"
00068 #include "llviewergenericmessage.h"     // send_generic_message
00069 #include "llviewerregion.h"
00070 #include "llviewerwindow.h"     // for window width, height
00071 #include "llappviewer.h"        // abortQuit()
00072 
00073 const S32 MINIMUM_PRICE_FOR_LISTING = 50;       // L$
00074 const S32 MATURE_CONTENT = 1;
00075 const S32 NON_MATURE_CONTENT = 2;
00076 const S32 DECLINE_TO_STATE = 0;
00077 
00078 //static
00079 std::list<LLPanelClassified*> LLPanelClassified::sAllPanels;
00080 
00081 // "classifiedclickthrough"
00082 // strings[0] = classified_id
00083 // strings[1] = teleport_clicks
00084 // strings[2] = map_clicks
00085 // strings[3] = profile_clicks
00086 class LLDispatchClassifiedClickThrough : public LLDispatchHandler
00087 {
00088 public:
00089         virtual bool operator()(
00090                 const LLDispatcher* dispatcher,
00091                 const std::string& key,
00092                 const LLUUID& invoice,
00093                 const sparam_t& strings)
00094         {
00095                 if (strings.size() != 4) return false;
00096                 LLUUID classified_id(strings[0]);
00097                 S32 teleport_clicks = atoi(strings[1].c_str());
00098                 S32 map_clicks = atoi(strings[2].c_str());
00099                 S32 profile_clicks = atoi(strings[3].c_str());
00100                 LLPanelClassified::setClickThrough(classified_id, teleport_clicks,
00101                                                                                    map_clicks,
00102                                                                                    profile_clicks,
00103                                                                                    false);
00104                 return true;
00105         }
00106 };
00107 static LLDispatchClassifiedClickThrough sClassifiedClickThrough;
00108 
00109 
00110 /* Re-expose this if we need to have classified ad HTML detail
00111    pages.  JC
00112 
00113 // We need to count classified teleport clicks from the search HTML detail pages,
00114 // so we need have a teleport that also sends a click count message.
00115 class LLClassifiedTeleportHandler : public LLCommandHandler
00116 {
00117 public:
00118         // don't allow from external browsers because it moves you immediately
00119         LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", false) { }
00120 
00121         bool handle(const LLSD& tokens, const LLSD& queryMap)
00122         {
00123                 // Need at least classified id and region name, so 2 params
00124                 if (tokens.size() < 2) return false;
00125                 LLUUID classified_id = tokens[0].asUUID();
00126                 if (classified_id.isNull()) return false;
00127                 // *HACK: construct a SLURL to do the teleport
00128                 std::string url("secondlife:///app/teleport/");
00129                 // skip the uuid we took off above, rebuild URL
00130                 // separated by slashes.
00131                 for (S32 i = 1; i < tokens.size(); ++i)
00132                 {
00133                         url += tokens[i].asString();
00134                         url += "/";
00135                 }
00136                 llinfos << "classified teleport to " << url << llendl;
00137                 // *TODO: separately track old search, sidebar, and new search
00138                 // Right now detail HTML pages count as new search.
00139                 const bool from_search = true;
00140                 LLPanelClassified::sendClassifiedClickMessage(classified_id, "teleport", from_search);
00141                 // Invoke teleport
00142                 const bool from_external_browser = false;
00143                 return LLURLDispatcher::dispatch(url, from_external_browser);
00144         }
00145 };
00146 // Creating the object registers with the dispatcher.
00147 LLClassifiedTeleportHandler gClassifiedTeleportHandler;
00148 */
00149 
00150 LLPanelClassified::LLPanelClassified(bool in_finder, bool from_search)
00151 :       LLPanel("Classified Panel"),
00152         mInFinder(in_finder),
00153         mFromSearch(from_search),
00154         mDirty(false),
00155         mForceClose(false),
00156         mLocationChanged(false),
00157         mClassifiedID(),
00158         mCreatorID(),
00159         mPriceForListing(0),
00160         mDataRequested(FALSE),
00161         mPaidFor(FALSE),
00162         mPosGlobal(),
00163         mSnapshotCtrl(NULL),
00164         mNameEditor(NULL),
00165         mDescEditor(NULL),
00166         mLocationEditor(NULL),
00167         mCategoryCombo(NULL),
00168         mMatureCombo(NULL),
00169         mAutoRenewCheck(NULL),
00170         mUpdateBtn(NULL),
00171         mTeleportBtn(NULL),
00172         mMapBtn(NULL),
00173         mProfileBtn(NULL),
00174         mInfoText(NULL),
00175         mSetBtn(NULL),
00176         mClickThroughText(NULL),
00177         mTeleportClicksOld(0),
00178         mMapClicksOld(0),
00179         mProfileClicksOld(0),
00180         mTeleportClicksNew(0),
00181         mMapClicksNew(0),
00182         mProfileClicksNew(0)
00183 
00184 {
00185     sAllPanels.push_back(this);
00186 
00187         std::string classified_def_file;
00188         if (mInFinder)
00189         {
00190                 LLUICtrlFactory::getInstance()->buildPanel(this, "panel_classified.xml");
00191         }
00192         else
00193         {
00194                 LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_classified.xml");
00195         }
00196 
00197         // Register dispatcher
00198         gGenericDispatcher.addHandler("classifiedclickthrough", 
00199                                                                   &sClassifiedClickThrough);
00200 }
00201 
00202 
00203 LLPanelClassified::~LLPanelClassified()
00204 {
00205     sAllPanels.remove(this);
00206 }
00207 
00208 
00209 void LLPanelClassified::reset()
00210 {
00211         mClassifiedID.setNull();
00212         mCreatorID.setNull();
00213         mParcelID.setNull();
00214 
00215         // Don't request data, this isn't valid
00216         mDataRequested = TRUE;
00217 
00218         mDirty = false;
00219         mPaidFor = FALSE;
00220 
00221         mPosGlobal.clearVec();
00222 
00223         clearCtrls();
00224 }
00225 
00226 
00227 BOOL LLPanelClassified::postBuild()
00228 {
00229     mSnapshotCtrl = getChild<LLTextureCtrl>("snapshot_ctrl");
00230         mSnapshotCtrl->setCommitCallback(onCommitAny);
00231         mSnapshotCtrl->setCallbackUserData(this);
00232         mSnapshotSize = mSnapshotCtrl->getRect();
00233 
00234     mNameEditor = getChild<LLLineEditor>("given_name_editor");
00235         mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN);
00236         mNameEditor->setCommitOnFocusLost(TRUE);
00237         mNameEditor->setFocusReceivedCallback(focusReceived, this);
00238         mNameEditor->setCommitCallback(onCommitAny);
00239         mNameEditor->setCallbackUserData(this);
00240         mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII );
00241 
00242     mDescEditor = getChild<LLTextEditor>("desc_editor");
00243         mDescEditor->setCommitOnFocusLost(TRUE);
00244         mDescEditor->setFocusReceivedCallback(focusReceived, this);
00245         mDescEditor->setCommitCallback(onCommitAny);
00246         mDescEditor->setCallbackUserData(this);
00247         mDescEditor->setTabsToNextField(TRUE);
00248 
00249     mLocationEditor = getChild<LLLineEditor>("location_editor");
00250 
00251     mSetBtn = getChild<LLButton>( "set_location_btn");
00252     mSetBtn->setClickedCallback(onClickSet);
00253     mSetBtn->setCallbackUserData(this);
00254 
00255     mTeleportBtn = getChild<LLButton>( "classified_teleport_btn");
00256     mTeleportBtn->setClickedCallback(onClickTeleport);
00257     mTeleportBtn->setCallbackUserData(this);
00258 
00259     mMapBtn = getChild<LLButton>( "classified_map_btn");
00260     mMapBtn->setClickedCallback(onClickMap);
00261     mMapBtn->setCallbackUserData(this);
00262 
00263         if(mInFinder)
00264         {
00265                 mProfileBtn  = getChild<LLButton>( "classified_profile_btn");
00266                 mProfileBtn->setClickedCallback(onClickProfile);
00267                 mProfileBtn->setCallbackUserData(this);
00268         }
00269 
00270         mCategoryCombo = getChild<LLComboBox>( "classified_category_combo");
00271         LLClassifiedInfo::cat_map::iterator iter;
00272         for (iter = LLClassifiedInfo::sCategories.begin();
00273                 iter != LLClassifiedInfo::sCategories.end();
00274                 iter++)
00275         {
00276                 mCategoryCombo->add(iter->second, (void *)((intptr_t)iter->first), ADD_BOTTOM);
00277         }
00278         mCategoryCombo->setCurrentByIndex(0);
00279         mCategoryCombo->setCommitCallback(onCommitAny);
00280         mCategoryCombo->setCallbackUserData(this);
00281 
00282         mMatureCombo = getChild<LLComboBox>( "classified_mature_check");
00283         mMatureCombo->setCurrentByIndex(0);
00284         mMatureCombo->setCommitCallback(onCommitAny);
00285         mMatureCombo->setCallbackUserData(this);
00286         if (gAgent.isTeen())
00287         {
00288                 // Teens don't get to set mature flag. JC
00289                 mMatureCombo->setVisible(FALSE);
00290                 mMatureCombo->setCurrentByIndex(NON_MATURE_CONTENT);
00291         }
00292 
00293         if (!mInFinder)
00294         {
00295                 mAutoRenewCheck = getChild<LLCheckBoxCtrl>( "auto_renew_check");
00296                 mAutoRenewCheck->setCommitCallback(onCommitAny);
00297                 mAutoRenewCheck->setCallbackUserData(this);
00298         }
00299 
00300         mUpdateBtn = getChild<LLButton>("classified_update_btn");
00301     mUpdateBtn->setClickedCallback(onClickUpdate);
00302     mUpdateBtn->setCallbackUserData(this);
00303 
00304         if (!mInFinder)
00305         {
00306                 mClickThroughText = getChild<LLTextBox>("click_through_text");
00307         }
00308         
00309     return TRUE;
00310 }
00311 
00312 BOOL LLPanelClassified::titleIsValid()
00313 {
00314         // Disallow leading spaces, punctuation, etc. that screw up
00315         // sort order.
00316         const LLString& name = mNameEditor->getText();
00317         if (name.empty())
00318         {
00319                 gViewerWindow->alertXml("BlankClassifiedName");
00320                 return FALSE;
00321         }
00322         if (!isalnum(name[0]))
00323         {
00324                 gViewerWindow->alertXml("ClassifiedMustBeAlphanumeric");
00325                 return FALSE;
00326         }
00327 
00328         return TRUE;
00329 }
00330 
00331 void LLPanelClassified::apply()
00332 {
00333         // Apply is used for automatically saving results, so only
00334         // do that if there is a difference, and this is a save not create.
00335         if (checkDirty() && mPaidFor)
00336         {
00337                 sendClassifiedInfoUpdate();
00338         }
00339 }
00340 
00341 
00342 // static
00343 void LLPanelClassified::saveCallback(S32 option, void* data)
00344 {
00345         LLPanelClassified* self = (LLPanelClassified*)data;
00346         switch(option)
00347         {
00348                 case 0: // Save
00349                         self->sendClassifiedInfoUpdate();
00350                         // fall through to close
00351 
00352                 case 1: // Don't Save
00353                         {
00354                                 self->mForceClose = true;
00355                                 // Close containing floater
00356                                 LLView* view = self;
00357                                 while (view)
00358                                 {
00359                                         LLFloater* floaterp = dynamic_cast<LLFloater*>(view);
00360                                         if (floaterp)
00361                                         {
00362                                                 floaterp->close();
00363                                                 break;
00364                                         }
00365                                         view = view->getParent();
00366                                 }
00367                         }
00368                         break;
00369 
00370                 case 2: // Cancel
00371                 default:
00372             LLAppViewer::instance()->abortQuit();
00373                         break;
00374         }
00375 }
00376 
00377 BOOL LLPanelClassified::canClose()
00378 {
00379         if (mForceClose || !checkDirty()) 
00380                 return TRUE;
00381 
00382         LLString::format_map_t args;
00383         args["[NAME]"] = mNameEditor->getText();
00384         LLAlertDialog::showXml("ClassifiedSave", args, saveCallback, this);
00385         return FALSE;
00386 }
00387 
00388 // Fill in some reasonable defaults for a new classified.
00389 void LLPanelClassified::initNewClassified()
00390 {
00391         // TODO:  Don't generate this on the client.
00392         mClassifiedID.generate();
00393 
00394         mCreatorID = gAgent.getID();
00395 
00396         mPosGlobal = gAgent.getPositionGlobal();
00397 
00398         mPaidFor = FALSE;
00399 
00400         // Try to fill in the current parcel
00401         LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
00402         if (parcel)
00403         {
00404                 mNameEditor->setText(parcel->getName());
00405                 //mDescEditor->setText(parcel->getDesc());
00406                 mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
00407                 //mPriceEditor->setText("0");
00408                 mCategoryCombo->setCurrentByIndex(0);
00409         }
00410 
00411         mUpdateBtn->setLabel(getString("publish_txt"));
00412 }
00413 
00414 
00415 void LLPanelClassified::setClassifiedID(const LLUUID& id)
00416 {
00417         mClassifiedID = id;
00418 }
00419 
00420 //static
00421 void LLPanelClassified::setClickThrough(const LLUUID& classified_id,
00422                                                                                 S32 teleport,
00423                                                                                 S32 map,
00424                                                                                 S32 profile,
00425                                                                                 bool from_new_table)
00426 {
00427         for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter)
00428         {
00429                 LLPanelClassified* self = *iter;
00430                 // For top picks, must match pick id
00431                 if (self->mClassifiedID != classified_id)
00432                 {
00433                         continue;
00434                 }
00435 
00436                 // We need to check to see if the data came from the new stat_table 
00437                 // or the old classified table. We also need to cache the data from 
00438                 // the two separate sources so as to display the aggregate totals.
00439 
00440                 if (from_new_table)
00441                 {
00442                         self->mTeleportClicksNew = teleport;
00443                         self->mMapClicksNew = map;
00444                         self->mProfileClicksNew = profile;
00445                 }
00446                 else
00447                 {
00448                         self->mTeleportClicksOld = teleport;
00449                         self->mMapClicksOld = map;
00450                         self->mProfileClicksOld = profile;
00451                 }
00452 
00453                 if (self->mClickThroughText)
00454                 {
00455                         std::string msg = llformat("Clicks: %d teleport, %d map, %d profile",
00456                                                                         self->mTeleportClicksNew + self->mTeleportClicksOld,
00457                                                                         self->mMapClicksNew + self->mMapClicksOld,
00458                                                                         self->mProfileClicksNew + self->mProfileClicksOld);
00459                         self->mClickThroughText->setText(msg);
00460                 }
00461         }
00462 }
00463 
00464 // Schedules the panel to request data
00465 // from the server next time it is drawn.
00466 void LLPanelClassified::markForServerRequest()
00467 {
00468         mDataRequested = FALSE;
00469 }
00470 
00471 
00472 std::string LLPanelClassified::getClassifiedName()
00473 {
00474         return mNameEditor->getText();
00475 }
00476 
00477 
00478 void LLPanelClassified::sendClassifiedInfoRequest()
00479 {
00480     LLMessageSystem *msg = gMessageSystem;
00481 
00482         if (mClassifiedID != mRequestedID)
00483         {
00484                 msg->newMessageFast(_PREHASH_ClassifiedInfoRequest);
00485                 msg->nextBlockFast(_PREHASH_AgentData);
00486                 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
00487                 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
00488                 msg->nextBlockFast(_PREHASH_Data);
00489                 msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
00490                 gAgent.sendReliableMessage();
00491 
00492                 mDataRequested = TRUE;
00493                 mRequestedID = mClassifiedID;
00494 
00495                 // While we're at it let's get the stats from the new table if that
00496                 // capability exists.
00497                 std::string url = gAgent.getRegion()->getCapability("SearchStatRequest");
00498                 LLSD body;
00499                 body["classified_id"] = mClassifiedID;
00500 
00501                 if (!url.empty())
00502                 {
00503                         llinfos << "Classified stat request via capability" << llendl;
00504                         LLHTTPClient::post(url, body, new LLClassifiedStatsResponder(((LLView*)this)->getHandle(), mClassifiedID));
00505                 }
00506         }
00507 }
00508 
00509 
00510 void LLPanelClassified::sendClassifiedInfoUpdate()
00511 {
00512         // If we don't have a classified id yet, we'll need to generate one,
00513         // otherwise we'll keep overwriting classified_id 00000 in the database.
00514         if (mClassifiedID.isNull())
00515         {
00516                 // TODO:  Don't do this on the client.
00517                 mClassifiedID.generate();
00518         }
00519 
00520         LLMessageSystem* msg = gMessageSystem;
00521 
00522         msg->newMessageFast(_PREHASH_ClassifiedInfoUpdate);
00523         msg->nextBlockFast(_PREHASH_AgentData);
00524         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00525         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00526         msg->nextBlockFast(_PREHASH_Data);
00527         msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
00528         // TODO: fix this
00529         U32 category = mCategoryCombo->getCurrentIndex() + 1;
00530         msg->addU32Fast(_PREHASH_Category, category);
00531         msg->addStringFast(_PREHASH_Name, mNameEditor->getText());
00532         msg->addStringFast(_PREHASH_Desc, mDescEditor->getText());
00533 
00534         // fills in on simulator if null
00535         msg->addUUIDFast(_PREHASH_ParcelID, mParcelID);
00536         // fills in on simulator if null
00537         msg->addU32Fast(_PREHASH_ParentEstate, 0);
00538         msg->addUUIDFast(_PREHASH_SnapshotID, mSnapshotCtrl->getImageAssetID());
00539         msg->addVector3dFast(_PREHASH_PosGlobal, mPosGlobal);
00540         BOOL mature = mMatureCombo->getCurrentIndex() == MATURE_CONTENT;
00541         BOOL auto_renew = FALSE;
00542         if (mAutoRenewCheck) 
00543         {
00544                 auto_renew = mAutoRenewCheck->get();
00545         }
00546         U8 flags = pack_classified_flags(mature, auto_renew);
00547         msg->addU8Fast(_PREHASH_ClassifiedFlags, flags);
00548         msg->addS32("PriceForListing", mPriceForListing);
00549         gAgent.sendReliableMessage();
00550 
00551         mDirty = false;
00552 }
00553 
00554 
00555 //static
00556 void LLPanelClassified::processClassifiedInfoReply(LLMessageSystem *msg, void **)
00557 {
00558         lldebugs << "processClassifiedInfoReply()" << llendl;
00559     // Extract the agent id and verify the message is for this
00560     // client.
00561     LLUUID agent_id;
00562     msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
00563     if (agent_id != gAgent.getID())
00564     {
00565         llwarns << "Agent ID mismatch in processClassifiedInfoReply"
00566             << llendl;
00567                 return;
00568     }
00569 
00570     LLUUID classified_id;
00571     msg->getUUIDFast(_PREHASH_Data, _PREHASH_ClassifiedID, classified_id);
00572 
00573     LLUUID creator_id;
00574     msg->getUUIDFast(_PREHASH_Data, _PREHASH_CreatorID, creator_id);
00575 
00576     LLUUID parcel_id;
00577     msg->getUUIDFast(_PREHASH_Data, _PREHASH_ParcelID, parcel_id);
00578 
00579         char name[DB_PARCEL_NAME_SIZE];         /*Flawfinder: ignore*/
00580         msg->getStringFast(_PREHASH_Data, _PREHASH_Name, DB_PARCEL_NAME_SIZE, name);
00581 
00582         char desc[DB_PICK_DESC_SIZE];           /*Flawfinder: ignore*/
00583         msg->getStringFast(_PREHASH_Data, _PREHASH_Desc, DB_PICK_DESC_SIZE, desc);
00584 
00585         LLUUID snapshot_id;
00586         msg->getUUIDFast(_PREHASH_Data, _PREHASH_SnapshotID, snapshot_id);
00587 
00588     // "Location text" is actually the original
00589     // name that owner gave the parcel, and the location.
00590         char buffer[256];               /*Flawfinder: ignore*/
00591     LLString location_text;
00592 
00593     msg->getStringFast(_PREHASH_Data, _PREHASH_ParcelName, 256, buffer);
00594         if (buffer[0] != '\0')
00595         {
00596                 location_text.assign(buffer);
00597                 location_text.append(", ");
00598         }
00599         else
00600         {
00601                 location_text.assign("");
00602         }
00603 
00604         char sim_name[256];             /*Flawfinder: ignore*/
00605         msg->getStringFast(_PREHASH_Data, _PREHASH_SimName, 256, sim_name);
00606 
00607         LLVector3d pos_global;
00608         msg->getVector3dFast(_PREHASH_Data, _PREHASH_PosGlobal, pos_global);
00609 
00610     S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
00611     S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
00612         S32 region_z = llround((F32)pos_global.mdV[VZ]);
00613    
00614     snprintf(buffer, sizeof(buffer), "%s (%d, %d, %d)", sim_name, region_x, region_y, region_z);                        /* Flawfinder: ignore */
00615     location_text.append(buffer);
00616 
00617         U8 flags;
00618         msg->getU8Fast(_PREHASH_Data, _PREHASH_ClassifiedFlags, flags);
00619         //BOOL enabled = is_cf_enabled(flags);
00620         bool mature = is_cf_mature(flags);
00621         bool auto_renew = is_cf_auto_renew(flags);
00622 
00623         U32 date = 0;
00624         msg->getU32Fast(_PREHASH_Data, _PREHASH_CreationDate, date);
00625         time_t tim = date;
00626         tm *now=localtime(&tim);
00627 
00628         // future use
00629         U32 expiration_date = 0;
00630         msg->getU32("Data", "ExpirationDate", expiration_date);
00631 
00632         U32 category = 0;
00633         msg->getU32Fast(_PREHASH_Data, _PREHASH_Category, category);
00634 
00635         S32 price_for_listing = 0;
00636         msg->getS32("Data", "PriceForListing", price_for_listing);
00637 
00638     // Look up the panel to fill in
00639         for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter)
00640         {
00641                 LLPanelClassified* self = *iter;
00642                 // For top picks, must match pick id
00643                 if (self->mClassifiedID != classified_id)
00644                 {
00645                         continue;
00646                 }
00647 
00648         // Found the panel, now fill in the information
00649                 self->mClassifiedID = classified_id;
00650                 self->mCreatorID = creator_id;
00651                 self->mParcelID = parcel_id;
00652                 self->mPriceForListing = price_for_listing;
00653                 self->mSimName.assign(sim_name);
00654                 self->mPosGlobal = pos_global;
00655 
00656                 // Update UI controls
00657         self->mNameEditor->setText(LLString(name));
00658         self->mDescEditor->setText(LLString(desc));
00659         self->mSnapshotCtrl->setImageAssetID(snapshot_id);
00660         self->mLocationEditor->setText(location_text);
00661                 self->mLocationChanged = false;
00662 
00663                 self->mCategoryCombo->setCurrentByIndex(category - 1);
00664                 if(mature)
00665                 {
00666                         self->mMatureCombo->setCurrentByIndex(MATURE_CONTENT);
00667                 }
00668                 else
00669                 {
00670                         self->mMatureCombo->setCurrentByIndex(NON_MATURE_CONTENT);
00671                 }
00672                 if (self->mAutoRenewCheck)
00673                 {
00674                         self->mAutoRenewCheck->set(auto_renew);
00675                 }
00676 
00677                 LLString datestr = llformat("%02d/%02d/%d", now->tm_mon+1, now->tm_mday, now->tm_year+1900);
00678                 LLString::format_map_t string_args;
00679                 string_args["[DATE]"] = datestr;
00680                 string_args["[AMT]"] = llformat("%d", price_for_listing);
00681                 self->childSetText("classified_info_text", self->getString("ad_placed_paid", string_args));
00682 
00683                 // If we got data from the database, we know the listing is paid for.
00684                 self->mPaidFor = TRUE;
00685 
00686                 self->mUpdateBtn->setLabel(self->getString("update_txt"));
00687 
00688                 self->resetDirty();
00689 
00690                 self->resetDirty();
00691     }
00692 }
00693 
00694 void LLPanelClassified::draw()
00695 {
00696         refresh();
00697 
00698         LLPanel::draw();
00699 }
00700 
00701 
00702 void LLPanelClassified::refresh()
00703 {
00704         if (!mDataRequested)
00705         {
00706                 sendClassifiedInfoRequest();
00707         }
00708 
00709     // Check for god mode
00710     BOOL godlike = gAgent.isGodlike();
00711         BOOL is_self = (gAgent.getID() == mCreatorID);
00712 
00713     // Set button visibility/enablement appropriately
00714         if (mInFinder)
00715         {
00716 
00717                 // End user doesn't ned to see price twice, or date posted.
00718 
00719                 mSnapshotCtrl->setEnabled(godlike);
00720                 if(godlike)
00721                 {
00722                         //make it smaller, so text is more legible
00723                         mSnapshotCtrl->setOrigin(20, 175);
00724                         mSnapshotCtrl->reshape(300, 200);
00725                 }
00726                 else
00727                 {
00728                         mSnapshotCtrl->setOrigin(mSnapshotSize.mLeft, mSnapshotSize.mBottom);
00729                         mSnapshotCtrl->reshape(mSnapshotSize.getWidth(), mSnapshotSize.getHeight());
00730                         //normal
00731                 }
00732                 mNameEditor->setEnabled(godlike);
00733                 mDescEditor->setEnabled(godlike);
00734                 mCategoryCombo->setEnabled(godlike);
00735                 mCategoryCombo->setVisible(godlike);
00736 
00737                 mMatureCombo->setEnabled(godlike);
00738                 mMatureCombo->setVisible(godlike);
00739 
00740                 // Jesse (who is the only one who uses this, as far as we can tell
00741                 // Says that he does not want a set location button - he has used it
00742                 // accidently in the past.
00743                 mSetBtn->setVisible(FALSE);
00744                 mSetBtn->setEnabled(FALSE);
00745 
00746                 mUpdateBtn->setEnabled(godlike);
00747                 mUpdateBtn->setVisible(godlike);
00748         }
00749         else
00750         {
00751                 mSnapshotCtrl->setEnabled(is_self);
00752                 mNameEditor->setEnabled(is_self);
00753                 mDescEditor->setEnabled(is_self);
00754                 //mPriceEditor->setEnabled(is_self);
00755                 mCategoryCombo->setEnabled(is_self);
00756 
00757                 mMatureCombo->setEnabled(is_self);
00758 
00759                 if (mAutoRenewCheck)
00760                 {
00761                         mAutoRenewCheck->setEnabled(is_self);
00762                         mAutoRenewCheck->setVisible(is_self);
00763                 }
00764                 
00765                 mClickThroughText->setEnabled(is_self);
00766                 mClickThroughText->setVisible(is_self);
00767 
00768                 mSetBtn->setVisible(is_self);
00769                 mSetBtn->setEnabled(is_self);
00770 
00771                 mUpdateBtn->setEnabled(is_self && checkDirty());
00772                 mUpdateBtn->setVisible(is_self);
00773         }
00774 }
00775 
00776 // static
00777 void LLPanelClassified::onClickUpdate(void* data)
00778 {
00779         LLPanelClassified* self = (LLPanelClassified*)data;
00780 
00781         if(self == NULL) return;
00782 
00783         // Disallow leading spaces, punctuation, etc. that screw up
00784         // sort order.
00785         if ( ! self->titleIsValid() )
00786         {
00787                 return;
00788         };
00789 
00790         // If user has not set mature, do not allow publish
00791         if(self->mMatureCombo->getCurrentIndex() == DECLINE_TO_STATE)
00792         {
00793                 LLString::format_map_t args;
00794                 gViewerWindow->alertXml("SetClassifiedMature", &callbackConfirmMature, self);
00795                 return;
00796         }
00797 
00798         // Mature content flag is set, proceed
00799         self->gotMature();
00800 }
00801 
00802 // static
00803 void LLPanelClassified::callbackConfirmMature(S32 option, void* data)
00804 {
00805         LLPanelClassified* self = (LLPanelClassified*)data;
00806         self->confirmMature(option);
00807 }
00808 
00809 // invoked from callbackConfirmMature
00810 void LLPanelClassified::confirmMature(S32 option)
00811 {
00812         // 0 == Yes
00813         // 1 == No
00814         // 2 == Cancel
00815         switch(option)
00816         {
00817         case 0:
00818                 mMatureCombo->setCurrentByIndex(MATURE_CONTENT);
00819                 break;
00820         case 1:
00821                 mMatureCombo->setCurrentByIndex(NON_MATURE_CONTENT);
00822                 break;
00823         default:
00824                 return;
00825         }
00826         
00827         // If we got here it means they set a valid value
00828         gotMature();
00829 }
00830 
00831 // Called after we have determined whether this classified has
00832 // mature content or not.
00833 void LLPanelClassified::gotMature()
00834 {
00835         // if already paid for, just do the update
00836         if (mPaidFor)
00837         {
00838                 callbackConfirmPublish(0, this);
00839         }
00840         else
00841         {
00842                 // Ask the user how much they want to pay
00843                 LLFloaterPriceForListing::show( callbackGotPriceForListing, this );
00844         }
00845 }
00846 
00847 // static
00848 void LLPanelClassified::callbackGotPriceForListing(S32 option, LLString text, void* data)
00849 {
00850         LLPanelClassified* self = (LLPanelClassified*)data;
00851 
00852         // Only do something if user hits publish
00853         if (option != 0) return;
00854 
00855         S32 price_for_listing = strtol(text.c_str(), NULL, 10);
00856         if (price_for_listing < MINIMUM_PRICE_FOR_LISTING)
00857         {
00858                 LLString::format_map_t args;
00859                 LLString price_text = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
00860                 args["[MIN_PRICE]"] = price_text;
00861                         
00862                 gViewerWindow->alertXml("MinClassifiedPrice", args);
00863                 return;
00864         }
00865 
00866         // price is acceptable, put it in the dialog for later read by 
00867         // update send
00868         self->mPriceForListing = price_for_listing;
00869 
00870         LLString::format_map_t args;
00871         args["[AMOUNT]"] = llformat("%d", price_for_listing);
00872         gViewerWindow->alertXml("PublishClassified", args, &callbackConfirmPublish, self);
00873 
00874 }
00875 
00876 void LLPanelClassified::resetDirty()
00877 {
00878         // Tell all the widgets to reset their dirty state since the ad was just saved
00879         if (mSnapshotCtrl)
00880                 mSnapshotCtrl->resetDirty();
00881         if (mNameEditor)
00882                 mNameEditor->resetDirty();
00883         if (mDescEditor)
00884                 mDescEditor->resetDirty();
00885         if (mLocationEditor)
00886                 mLocationEditor->resetDirty();
00887         mLocationChanged = false;
00888         if (mCategoryCombo)
00889                 mCategoryCombo->resetDirty();
00890         if (mMatureCombo)
00891                 mMatureCombo->resetDirty();
00892         if (mAutoRenewCheck)
00893                 mAutoRenewCheck->resetDirty();
00894 }
00895 
00896 // invoked from callbackConfirmPublish
00897 void LLPanelClassified::confirmPublish(S32 option)
00898 {
00899         // Option 0 = publish
00900         if (option != 0) return;
00901 
00902         sendClassifiedInfoUpdate();
00903 
00904         // Big hack - assume that top picks are always in a browser,
00905         // and non-finder-classifieds are always in a tab container.
00906         if (mInFinder)
00907         {
00908                 // TODO: enable this
00909                 //LLPanelDirClassifieds* panel = (LLPanelDirClassifieds*)getParent();
00910                 //panel->renameClassified(mClassifiedID, mNameEditor->getText().c_str());
00911         }
00912         else
00913         {
00914                 LLTabContainer* tab = (LLTabContainer*)getParent();
00915                 tab->setCurrentTabName(mNameEditor->getText());
00916         }
00917 
00918         resetDirty();
00919 }
00920 
00921 // static
00922 void LLPanelClassified::callbackConfirmPublish(S32 option, void* data)
00923 {
00924         LLPanelClassified* self = (LLPanelClassified*)data;
00925         self->confirmPublish(option);
00926 }
00927 
00928 // static
00929 void LLPanelClassified::onClickTeleport(void* data)
00930 {
00931     LLPanelClassified* self = (LLPanelClassified*)data;
00932 
00933     if (!self->mPosGlobal.isExactlyZero())
00934     {
00935         gAgent.teleportViaLocation(self->mPosGlobal);
00936         gFloaterWorldMap->trackLocation(self->mPosGlobal);
00937 
00938                 self->sendClassifiedClickMessage("teleport");
00939     }
00940 }
00941 
00942 
00943 // static
00944 void LLPanelClassified::onClickMap(void* data)
00945 {
00946         LLPanelClassified* self = (LLPanelClassified*)data;
00947         gFloaterWorldMap->trackLocation(self->mPosGlobal);
00948         LLFloaterWorldMap::show(NULL, TRUE);
00949 
00950         self->sendClassifiedClickMessage("map");
00951 }
00952 
00953 // static
00954 void LLPanelClassified::onClickProfile(void* data)
00955 {
00956         LLPanelClassified* self = (LLPanelClassified*)data;
00957         LLFloaterAvatarInfo::showFromDirectory(self->mCreatorID);
00958         self->sendClassifiedClickMessage("profile");
00959 }
00960 
00961 // static
00962 /*
00963 void LLPanelClassified::onClickLandmark(void* data)
00964 {
00965     LLPanelClassified* self = (LLPanelClassified*)data;
00966         create_landmark(self->mNameEditor->getText().c_str(), "", self->mPosGlobal);
00967 }
00968 */
00969 
00970 // static
00971 void LLPanelClassified::onClickSet(void* data)
00972 {
00973     LLPanelClassified* self = (LLPanelClassified*)data;
00974 
00975         // Save location for later.
00976         self->mPosGlobal = gAgent.getPositionGlobal();
00977 
00978         LLString location_text;
00979         location_text.assign("(will update after publish)");
00980         location_text.append(", ");
00981 
00982     S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
00983     S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
00984         S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]);
00985    
00986         location_text.append(self->mSimName);
00987     location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
00988 
00989         self->mLocationEditor->setText(location_text);
00990         self->mLocationChanged = true;
00991 
00992         // Set this to null so it updates on the next save.
00993         self->mParcelID.setNull();
00994 
00995         onCommitAny(NULL, data);
00996 }
00997 
00998 
00999 BOOL LLPanelClassified::checkDirty()
01000 {
01001         mDirty = FALSE;
01002         if      ( mSnapshotCtrl )                       mDirty |= mSnapshotCtrl->isDirty();
01003         if      ( mNameEditor )                         mDirty |= mNameEditor->isDirty();
01004         if      ( mDescEditor )                         mDirty |= mDescEditor->isDirty();
01005         if      ( mLocationEditor )                     mDirty |= mLocationEditor->isDirty();
01006         if  ( mLocationChanged )                mDirty |= TRUE;
01007         if      ( mCategoryCombo )                      mDirty |= mCategoryCombo->isDirty();
01008         if      ( mMatureCombo )                        mDirty |= mMatureCombo->isDirty();
01009         if      ( mAutoRenewCheck )                     mDirty |= mAutoRenewCheck->isDirty();
01010 
01011         return mDirty;
01012 }
01013 
01014 // static
01015 void LLPanelClassified::onCommitAny(LLUICtrl* ctrl, void* data)
01016 {
01017         LLPanelClassified* self = (LLPanelClassified*)data;
01018         if (self)
01019         {
01020                 self->checkDirty();
01021         }
01022 }
01023 
01024 // static
01025 void LLPanelClassified::focusReceived(LLFocusableElement* ctrl, void* data)
01026 {
01027         // allow the data to be saved
01028         onCommitAny((LLUICtrl*)ctrl, data);
01029 }
01030 
01031 
01032 void LLPanelClassified::sendClassifiedClickMessage(const char* type)
01033 {
01034         // You're allowed to click on your own ads to reassure yourself
01035         // that the system is working.
01036         LLSD body;
01037         body["type"] = type;
01038         body["from_search"] = mFromSearch;
01039         body["classified_id"] = mClassifiedID;
01040         body["parcel_id"] = mParcelID;
01041         body["dest_pos_global"] = mPosGlobal.getValue();
01042         body["region_name"] = mSimName;
01043 
01044         std::string url = gAgent.getRegion()->getCapability("SearchStatTracking");
01045         llinfos << "LLPanelClassified::sendClassifiedClickMessage via capability" << llendl;
01046         LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
01047 }
01048 
01050 
01051 LLFloaterPriceForListing::LLFloaterPriceForListing()
01052 :       LLFloater("PriceForListing"),
01053         mCallback(NULL),
01054         mUserData(NULL)
01055 { }
01056 
01057 //virtual
01058 LLFloaterPriceForListing::~LLFloaterPriceForListing()
01059 { }
01060 
01061 //virtual
01062 BOOL LLFloaterPriceForListing::postBuild()
01063 {
01064         LLLineEditor* edit = getChild<LLLineEditor>("price_edit");
01065         if (edit)
01066         {
01067                 edit->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
01068                 LLString min_price = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
01069                 edit->setText(min_price);
01070                 edit->selectAll();
01071                 edit->setFocus(TRUE);
01072         }
01073 
01074         childSetAction("set_price_btn", onClickSetPrice, this);
01075 
01076         childSetAction("cancel_btn", onClickCancel, this);
01077 
01078         setDefaultBtn("set_price_btn");
01079         return TRUE;
01080 }
01081 
01082 //static
01083 void LLFloaterPriceForListing::show( void (*callback)(S32, LLString, void*), void* userdata)
01084 {
01085         LLFloaterPriceForListing *self = new LLFloaterPriceForListing();
01086 
01087         // Builds and adds to gFloaterView
01088         LLUICtrlFactory::getInstance()->buildFloater(self, "floater_price_for_listing.xml");
01089         self->center();
01090 
01091         self->mCallback = callback;
01092         self->mUserData = userdata;
01093 }
01094 
01095 //static
01096 void LLFloaterPriceForListing::onClickSetPrice(void* data)
01097 {
01098         buttonCore(0, data);
01099 }
01100 
01101 //static
01102 void LLFloaterPriceForListing::onClickCancel(void* data)
01103 {
01104         buttonCore(1, data);
01105 }
01106 
01107 //static
01108 void LLFloaterPriceForListing::buttonCore(S32 button, void* data)
01109 {
01110         LLFloaterPriceForListing* self = (LLFloaterPriceForListing*)data;
01111 
01112         if (self->mCallback)
01113         {
01114                 LLString text = self->childGetText("price_edit");
01115                 self->mCallback(button, text, self->mUserData);
01116                 self->close();
01117         }
01118 }

Generated on Fri May 16 08:33:49 2008 for SecondLife by  doxygen 1.5.5