PageRenderTime 25ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llpanelclassified.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1169 lines | 870 code | 206 blank | 93 comment | 65 complexity | 7de975b060f3568cba8cda7657c0ee2a MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llpanelclassified.cpp
  3. * @brief LLPanelClassified class implementation
  4. *
  5. * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. // Display of a classified used both for the global view in the
  27. // Find directory, and also for each individual user's classified in their
  28. // profile.
  29. #include "llviewerprecompiledheaders.h"
  30. #include "llpanelclassified.h"
  31. #include "lldispatcher.h"
  32. #include "llfloaterreg.h"
  33. #include "llhttpclient.h"
  34. #include "llnotifications.h"
  35. #include "llnotificationsutil.h"
  36. #include "llparcel.h"
  37. #include "llagent.h"
  38. #include "llclassifiedflags.h"
  39. #include "llclassifiedstatsresponder.h"
  40. #include "llcommandhandler.h" // for classified HTML detail page click tracking
  41. #include "lliconctrl.h"
  42. #include "lllineeditor.h"
  43. #include "llcombobox.h"
  44. #include "lltexturectrl.h"
  45. #include "lltexteditor.h"
  46. #include "llviewerparcelmgr.h"
  47. #include "llfloaterworldmap.h"
  48. #include "llviewergenericmessage.h" // send_generic_message
  49. #include "llviewerregion.h"
  50. #include "lltrans.h"
  51. #include "llscrollcontainer.h"
  52. #include "llstatusbar.h"
  53. const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$
  54. //static
  55. LLPanelClassifiedInfo::panel_list_t LLPanelClassifiedInfo::sAllPanels;
  56. // "classifiedclickthrough"
  57. // strings[0] = classified_id
  58. // strings[1] = teleport_clicks
  59. // strings[2] = map_clicks
  60. // strings[3] = profile_clicks
  61. class LLDispatchClassifiedClickThrough : public LLDispatchHandler
  62. {
  63. public:
  64. virtual bool operator()(
  65. const LLDispatcher* dispatcher,
  66. const std::string& key,
  67. const LLUUID& invoice,
  68. const sparam_t& strings)
  69. {
  70. if (strings.size() != 4) return false;
  71. LLUUID classified_id(strings[0]);
  72. S32 teleport_clicks = atoi(strings[1].c_str());
  73. S32 map_clicks = atoi(strings[2].c_str());
  74. S32 profile_clicks = atoi(strings[3].c_str());
  75. LLPanelClassifiedInfo::setClickThrough(
  76. classified_id, teleport_clicks, map_clicks, profile_clicks, false);
  77. return true;
  78. }
  79. };
  80. static LLDispatchClassifiedClickThrough sClassifiedClickThrough;
  81. // Just to debug errors. Can be thrown away later.
  82. class LLClassifiedClickMessageResponder : public LLHTTPClient::Responder
  83. {
  84. LOG_CLASS(LLClassifiedClickMessageResponder);
  85. public:
  86. // If we get back an error (not found, etc...), handle it here
  87. virtual void errorWithContent(
  88. U32 status,
  89. const std::string& reason,
  90. const LLSD& content)
  91. {
  92. llwarns << "Sending click message failed (" << status << "): [" << reason << "]" << llendl;
  93. llwarns << "Content: [" << content << "]" << llendl;
  94. }
  95. };
  96. //////////////////////////////////////////////////////////////////////////
  97. //////////////////////////////////////////////////////////////////////////
  98. //////////////////////////////////////////////////////////////////////////
  99. LLPanelClassifiedInfo::LLPanelClassifiedInfo()
  100. : LLPanel()
  101. , mInfoLoaded(false)
  102. , mScrollingPanel(NULL)
  103. , mScrollContainer(NULL)
  104. , mScrollingPanelMinHeight(0)
  105. , mScrollingPanelWidth(0)
  106. , mSnapshotStreched(false)
  107. , mTeleportClicksOld(0)
  108. , mMapClicksOld(0)
  109. , mProfileClicksOld(0)
  110. , mTeleportClicksNew(0)
  111. , mMapClicksNew(0)
  112. , mProfileClicksNew(0)
  113. , mSnapshotCtrl(NULL)
  114. {
  115. sAllPanels.push_back(this);
  116. }
  117. LLPanelClassifiedInfo::~LLPanelClassifiedInfo()
  118. {
  119. sAllPanels.remove(this);
  120. }
  121. // static
  122. LLPanelClassifiedInfo* LLPanelClassifiedInfo::create()
  123. {
  124. LLPanelClassifiedInfo* panel = new LLPanelClassifiedInfo();
  125. panel->buildFromFile("panel_classified_info.xml");
  126. return panel;
  127. }
  128. BOOL LLPanelClassifiedInfo::postBuild()
  129. {
  130. childSetAction("back_btn", boost::bind(&LLPanelClassifiedInfo::onExit, this));
  131. childSetAction("show_on_map_btn", boost::bind(&LLPanelClassifiedInfo::onMapClick, this));
  132. childSetAction("teleport_btn", boost::bind(&LLPanelClassifiedInfo::onTeleportClick, this));
  133. mScrollingPanel = getChild<LLPanel>("scroll_content_panel");
  134. mScrollContainer = getChild<LLScrollContainer>("profile_scroll");
  135. mScrollingPanelMinHeight = mScrollContainer->getScrolledViewRect().getHeight();
  136. mScrollingPanelWidth = mScrollingPanel->getRect().getWidth();
  137. mSnapshotCtrl = getChild<LLTextureCtrl>("classified_snapshot");
  138. mSnapshotRect = getDefaultSnapshotRect();
  139. return TRUE;
  140. }
  141. void LLPanelClassifiedInfo::setExitCallback(const commit_callback_t& cb)
  142. {
  143. getChild<LLButton>("back_btn")->setClickedCallback(cb);
  144. }
  145. void LLPanelClassifiedInfo::setEditClassifiedCallback(const commit_callback_t& cb)
  146. {
  147. getChild<LLButton>("edit_btn")->setClickedCallback(cb);
  148. }
  149. void LLPanelClassifiedInfo::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
  150. {
  151. LLPanel::reshape(width, height, called_from_parent);
  152. if (!mScrollContainer || !mScrollingPanel)
  153. return;
  154. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  155. S32 scroll_height = mScrollContainer->getRect().getHeight();
  156. if (mScrollingPanelMinHeight >= scroll_height)
  157. {
  158. mScrollingPanel->reshape(mScrollingPanelWidth, mScrollingPanelMinHeight);
  159. }
  160. else
  161. {
  162. mScrollingPanel->reshape(mScrollingPanelWidth + scrollbar_size, scroll_height);
  163. }
  164. mSnapshotRect = getDefaultSnapshotRect();
  165. stretchSnapshot();
  166. }
  167. void LLPanelClassifiedInfo::onOpen(const LLSD& key)
  168. {
  169. LLUUID avatar_id = key["classified_creator_id"];
  170. if(avatar_id.isNull())
  171. {
  172. return;
  173. }
  174. if(getAvatarId().notNull())
  175. {
  176. LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this);
  177. }
  178. setAvatarId(avatar_id);
  179. resetData();
  180. resetControls();
  181. scrollToTop();
  182. setClassifiedId(key["classified_id"]);
  183. setClassifiedName(key["classified_name"]);
  184. setDescription(key["classified_desc"]);
  185. setSnapshotId(key["classified_snapshot_id"]);
  186. setFromSearch(key["from_search"]);
  187. llinfos << "Opening classified [" << getClassifiedName() << "] (" << getClassifiedId() << ")" << llendl;
  188. LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this);
  189. LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getClassifiedId());
  190. gGenericDispatcher.addHandler("classifiedclickthrough", &sClassifiedClickThrough);
  191. // While we're at it let's get the stats from the new table if that
  192. // capability exists.
  193. std::string url = gAgent.getRegion()->getCapability("SearchStatRequest");
  194. if (!url.empty())
  195. {
  196. llinfos << "Classified stat request via capability" << llendl;
  197. LLSD body;
  198. body["classified_id"] = getClassifiedId();
  199. LLHTTPClient::post(url, body, new LLClassifiedStatsResponder(getClassifiedId()));
  200. }
  201. // Update classified click stats.
  202. // *TODO: Should we do this when opening not from search?
  203. sendClickMessage("profile");
  204. setInfoLoaded(false);
  205. }
  206. void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType type)
  207. {
  208. if(APT_CLASSIFIED_INFO == type)
  209. {
  210. LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data);
  211. if(c_info && getClassifiedId() == c_info->classified_id)
  212. {
  213. setClassifiedName(c_info->name);
  214. setDescription(c_info->description);
  215. setSnapshotId(c_info->snapshot_id);
  216. setParcelId(c_info->parcel_id);
  217. setPosGlobal(c_info->pos_global);
  218. setSimName(c_info->sim_name);
  219. setClassifiedLocation(createLocationText(c_info->parcel_name, c_info->sim_name, c_info->pos_global));
  220. getChild<LLUICtrl>("category")->setValue(LLClassifiedInfo::sCategories[c_info->category]);
  221. static std::string mature_str = getString("type_mature");
  222. static std::string pg_str = getString("type_pg");
  223. static LLUIString price_str = getString("l$_price");
  224. static std::string date_fmt = getString("date_fmt");
  225. bool mature = is_cf_mature(c_info->flags);
  226. getChild<LLUICtrl>("content_type")->setValue(mature ? mature_str : pg_str);
  227. getChild<LLIconCtrl>("content_type_moderate")->setVisible(mature);
  228. getChild<LLIconCtrl>("content_type_general")->setVisible(!mature);
  229. std::string auto_renew_str = is_cf_auto_renew(c_info->flags) ?
  230. getString("auto_renew_on") : getString("auto_renew_off");
  231. getChild<LLUICtrl>("auto_renew")->setValue(auto_renew_str);
  232. price_str.setArg("[PRICE]", llformat("%d", c_info->price_for_listing));
  233. getChild<LLUICtrl>("price_for_listing")->setValue(LLSD(price_str));
  234. std::string date_str = date_fmt;
  235. LLStringUtil::format(date_str, LLSD().with("datetime", (S32) c_info->creation_date));
  236. getChild<LLUICtrl>("creation_date")->setValue(date_str);
  237. setInfoLoaded(true);
  238. }
  239. }
  240. }
  241. void LLPanelClassifiedInfo::resetData()
  242. {
  243. setClassifiedName(LLStringUtil::null);
  244. setDescription(LLStringUtil::null);
  245. setClassifiedLocation(LLStringUtil::null);
  246. setClassifiedId(LLUUID::null);
  247. setSnapshotId(LLUUID::null);
  248. setPosGlobal(LLVector3d::zero);
  249. setParcelId(LLUUID::null);
  250. setSimName(LLStringUtil::null);
  251. setFromSearch(false);
  252. // reset click stats
  253. mTeleportClicksOld = 0;
  254. mMapClicksOld = 0;
  255. mProfileClicksOld = 0;
  256. mTeleportClicksNew = 0;
  257. mMapClicksNew = 0;
  258. mProfileClicksNew = 0;
  259. getChild<LLUICtrl>("category")->setValue(LLStringUtil::null);
  260. getChild<LLUICtrl>("content_type")->setValue(LLStringUtil::null);
  261. getChild<LLUICtrl>("click_through_text")->setValue(LLStringUtil::null);
  262. getChild<LLUICtrl>("price_for_listing")->setValue(LLStringUtil::null);
  263. getChild<LLUICtrl>("auto_renew")->setValue(LLStringUtil::null);
  264. getChild<LLUICtrl>("creation_date")->setValue(LLStringUtil::null);
  265. getChild<LLUICtrl>("click_through_text")->setValue(LLStringUtil::null);
  266. getChild<LLIconCtrl>("content_type_moderate")->setVisible(FALSE);
  267. getChild<LLIconCtrl>("content_type_general")->setVisible(FALSE);
  268. }
  269. void LLPanelClassifiedInfo::resetControls()
  270. {
  271. bool is_self = getAvatarId() == gAgent.getID();
  272. getChildView("edit_btn")->setEnabled(is_self);
  273. getChildView("edit_btn")->setVisible( is_self);
  274. getChildView("price_layout_panel")->setVisible( is_self);
  275. getChildView("clickthrough_layout_panel")->setVisible( is_self);
  276. }
  277. void LLPanelClassifiedInfo::setClassifiedName(const std::string& name)
  278. {
  279. getChild<LLUICtrl>("classified_name")->setValue(name);
  280. }
  281. std::string LLPanelClassifiedInfo::getClassifiedName()
  282. {
  283. return getChild<LLUICtrl>("classified_name")->getValue().asString();
  284. }
  285. void LLPanelClassifiedInfo::setDescription(const std::string& desc)
  286. {
  287. getChild<LLUICtrl>("classified_desc")->setValue(desc);
  288. }
  289. std::string LLPanelClassifiedInfo::getDescription()
  290. {
  291. return getChild<LLUICtrl>("classified_desc")->getValue().asString();
  292. }
  293. void LLPanelClassifiedInfo::setClassifiedLocation(const std::string& location)
  294. {
  295. getChild<LLUICtrl>("classified_location")->setValue(location);
  296. }
  297. std::string LLPanelClassifiedInfo::getClassifiedLocation()
  298. {
  299. return getChild<LLUICtrl>("classified_location")->getValue().asString();
  300. }
  301. void LLPanelClassifiedInfo::setSnapshotId(const LLUUID& id)
  302. {
  303. mSnapshotCtrl->setValue(id);
  304. mSnapshotStreched = false;
  305. }
  306. void LLPanelClassifiedInfo::draw()
  307. {
  308. LLPanel::draw();
  309. // Stretch in draw because it takes some time to load a texture,
  310. // going to try to stretch snapshot until texture is loaded
  311. if(!mSnapshotStreched)
  312. {
  313. stretchSnapshot();
  314. }
  315. }
  316. LLUUID LLPanelClassifiedInfo::getSnapshotId()
  317. {
  318. return getChild<LLUICtrl>("classified_snapshot")->getValue().asUUID();
  319. }
  320. // static
  321. void LLPanelClassifiedInfo::setClickThrough(
  322. const LLUUID& classified_id,
  323. S32 teleport,
  324. S32 map,
  325. S32 profile,
  326. bool from_new_table)
  327. {
  328. llinfos << "Click-through data for classified " << classified_id << " arrived: ["
  329. << teleport << ", " << map << ", " << profile << "] ("
  330. << (from_new_table ? "new" : "old") << ")" << llendl;
  331. for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter)
  332. {
  333. LLPanelClassifiedInfo* self = *iter;
  334. if (self->getClassifiedId() != classified_id)
  335. {
  336. continue;
  337. }
  338. // *HACK: Skip LLPanelClassifiedEdit instances: they don't display clicks data.
  339. // Those instances should not be in the list at all.
  340. if (typeid(*self) != typeid(LLPanelClassifiedInfo))
  341. {
  342. continue;
  343. }
  344. llinfos << "Updating classified info panel" << llendl;
  345. // We need to check to see if the data came from the new stat_table
  346. // or the old classified table. We also need to cache the data from
  347. // the two separate sources so as to display the aggregate totals.
  348. if (from_new_table)
  349. {
  350. self->mTeleportClicksNew = teleport;
  351. self->mMapClicksNew = map;
  352. self->mProfileClicksNew = profile;
  353. }
  354. else
  355. {
  356. self->mTeleportClicksOld = teleport;
  357. self->mMapClicksOld = map;
  358. self->mProfileClicksOld = profile;
  359. }
  360. static LLUIString ct_str = self->getString("click_through_text_fmt");
  361. ct_str.setArg("[TELEPORT]", llformat("%d", self->mTeleportClicksNew + self->mTeleportClicksOld));
  362. ct_str.setArg("[MAP]", llformat("%d", self->mMapClicksNew + self->mMapClicksOld));
  363. ct_str.setArg("[PROFILE]", llformat("%d", self->mProfileClicksNew + self->mProfileClicksOld));
  364. self->getChild<LLUICtrl>("click_through_text")->setValue(ct_str.getString());
  365. // *HACK: remove this when there is enough room for click stats in the info panel
  366. self->getChildView("click_through_text")->setToolTip(ct_str.getString());
  367. llinfos << "teleport: " << llformat("%d", self->mTeleportClicksNew + self->mTeleportClicksOld)
  368. << ", map: " << llformat("%d", self->mMapClicksNew + self->mMapClicksOld)
  369. << ", profile: " << llformat("%d", self->mProfileClicksNew + self->mProfileClicksOld)
  370. << llendl;
  371. }
  372. }
  373. // static
  374. std::string LLPanelClassifiedInfo::createLocationText(
  375. const std::string& original_name,
  376. const std::string& sim_name,
  377. const LLVector3d& pos_global)
  378. {
  379. std::string location_text;
  380. location_text.append(original_name);
  381. if (!sim_name.empty())
  382. {
  383. if (!location_text.empty())
  384. location_text.append(", ");
  385. location_text.append(sim_name);
  386. }
  387. if (!location_text.empty())
  388. location_text.append(" ");
  389. if (!pos_global.isNull())
  390. {
  391. S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
  392. S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
  393. S32 region_z = llround((F32)pos_global.mdV[VZ]);
  394. location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
  395. }
  396. return location_text;
  397. }
  398. void LLPanelClassifiedInfo::stretchSnapshot()
  399. {
  400. // *NOTE dzaporozhan
  401. // Could be moved to LLTextureCtrl
  402. LLViewerFetchedTexture* texture = mSnapshotCtrl->getTexture();
  403. if(!texture)
  404. {
  405. return;
  406. }
  407. if(0 == texture->getOriginalWidth() || 0 == texture->getOriginalHeight())
  408. {
  409. // looks like texture is not loaded yet
  410. return;
  411. }
  412. LLRect rc = mSnapshotRect;
  413. // *HACK dzaporozhan
  414. // LLTextureCtrl uses BTN_HEIGHT_SMALL as bottom for texture which causes
  415. // drawn texture to be smaller than expected. (see LLTextureCtrl::draw())
  416. // Lets increase texture height to force texture look as expected.
  417. rc.mBottom -= BTN_HEIGHT_SMALL;
  418. F32 t_width = texture->getFullWidth();
  419. F32 t_height = texture->getFullHeight();
  420. F32 ratio = llmin<F32>( (rc.getWidth() / t_width), (rc.getHeight() / t_height) );
  421. t_width *= ratio;
  422. t_height *= ratio;
  423. rc.setCenterAndSize(rc.getCenterX(), rc.getCenterY(), llfloor(t_width), llfloor(t_height));
  424. mSnapshotCtrl->setShape(rc);
  425. mSnapshotStreched = true;
  426. }
  427. LLRect LLPanelClassifiedInfo::getDefaultSnapshotRect()
  428. {
  429. // Using scroll container makes getting default rect a hard task
  430. // because rect in postBuild() and in first reshape() is not the same.
  431. // Using snapshot_panel makes it easier to reshape snapshot.
  432. return getChild<LLUICtrl>("snapshot_panel")->getLocalRect();
  433. }
  434. void LLPanelClassifiedInfo::scrollToTop()
  435. {
  436. LLScrollContainer* scrollContainer = findChild<LLScrollContainer>("profile_scroll");
  437. if (scrollContainer)
  438. scrollContainer->goToTop();
  439. }
  440. // static
  441. // *TODO: move out of the panel
  442. void LLPanelClassifiedInfo::sendClickMessage(
  443. const std::string& type,
  444. bool from_search,
  445. const LLUUID& classified_id,
  446. const LLUUID& parcel_id,
  447. const LLVector3d& global_pos,
  448. const std::string& sim_name)
  449. {
  450. // You're allowed to click on your own ads to reassure yourself
  451. // that the system is working.
  452. LLSD body;
  453. body["type"] = type;
  454. body["from_search"] = from_search;
  455. body["classified_id"] = classified_id;
  456. body["parcel_id"] = parcel_id;
  457. body["dest_pos_global"] = global_pos.getValue();
  458. body["region_name"] = sim_name;
  459. std::string url = gAgent.getRegion()->getCapability("SearchStatTracking");
  460. llinfos << "Sending click msg via capability (url=" << url << ")" << llendl;
  461. llinfos << "body: [" << body << "]" << llendl;
  462. LLHTTPClient::post(url, body, new LLClassifiedClickMessageResponder());
  463. }
  464. void LLPanelClassifiedInfo::sendClickMessage(const std::string& type)
  465. {
  466. sendClickMessage(
  467. type,
  468. fromSearch(),
  469. getClassifiedId(),
  470. getParcelId(),
  471. getPosGlobal(),
  472. getSimName());
  473. }
  474. void LLPanelClassifiedInfo::onMapClick()
  475. {
  476. sendClickMessage("map");
  477. LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal());
  478. LLFloaterReg::showInstance("world_map", "center");
  479. }
  480. void LLPanelClassifiedInfo::onTeleportClick()
  481. {
  482. if (!getPosGlobal().isExactlyZero())
  483. {
  484. sendClickMessage("teleport");
  485. gAgent.teleportViaLocation(getPosGlobal());
  486. LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal());
  487. }
  488. }
  489. void LLPanelClassifiedInfo::onExit()
  490. {
  491. LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this);
  492. gGenericDispatcher.addHandler("classifiedclickthrough", NULL); // deregister our handler
  493. }
  494. //////////////////////////////////////////////////////////////////////////
  495. //////////////////////////////////////////////////////////////////////////
  496. //////////////////////////////////////////////////////////////////////////
  497. static const S32 CB_ITEM_MATURE = 0;
  498. static const S32 CB_ITEM_PG = 1;
  499. LLPanelClassifiedEdit::LLPanelClassifiedEdit()
  500. : LLPanelClassifiedInfo()
  501. , mIsNew(false)
  502. , mIsNewWithErrors(false)
  503. , mCanClose(false)
  504. , mPublishFloater(NULL)
  505. {
  506. }
  507. LLPanelClassifiedEdit::~LLPanelClassifiedEdit()
  508. {
  509. }
  510. //static
  511. LLPanelClassifiedEdit* LLPanelClassifiedEdit::create()
  512. {
  513. LLPanelClassifiedEdit* panel = new LLPanelClassifiedEdit();
  514. panel->buildFromFile("panel_edit_classified.xml");
  515. return panel;
  516. }
  517. BOOL LLPanelClassifiedEdit::postBuild()
  518. {
  519. LLPanelClassifiedInfo::postBuild();
  520. LLTextureCtrl* snapshot = getChild<LLTextureCtrl>("classified_snapshot");
  521. snapshot->setOnSelectCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this));
  522. LLUICtrl* edit_icon = getChild<LLUICtrl>("edit_icon");
  523. snapshot->setMouseEnterCallback(boost::bind(&LLPanelClassifiedEdit::onTexturePickerMouseEnter, this, edit_icon));
  524. snapshot->setMouseLeaveCallback(boost::bind(&LLPanelClassifiedEdit::onTexturePickerMouseLeave, this, edit_icon));
  525. edit_icon->setVisible(false);
  526. LLLineEditor* line_edit = getChild<LLLineEditor>("classified_name");
  527. line_edit->setKeystrokeCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL);
  528. LLTextEditor* text_edit = getChild<LLTextEditor>("classified_desc");
  529. text_edit->setKeystrokeCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this));
  530. LLComboBox* combobox = getChild<LLComboBox>( "category");
  531. LLClassifiedInfo::cat_map::iterator iter;
  532. for (iter = LLClassifiedInfo::sCategories.begin();
  533. iter != LLClassifiedInfo::sCategories.end();
  534. iter++)
  535. {
  536. combobox->add(LLTrans::getString(iter->second));
  537. }
  538. combobox->setCommitCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this));
  539. childSetCommitCallback("content_type", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL);
  540. childSetCommitCallback("price_for_listing", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL);
  541. childSetCommitCallback("auto_renew", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL);
  542. childSetAction("save_changes_btn", boost::bind(&LLPanelClassifiedEdit::onSaveClick, this));
  543. childSetAction("set_to_curr_location_btn", boost::bind(&LLPanelClassifiedEdit::onSetLocationClick, this));
  544. mSnapshotCtrl->setOnSelectCallback(boost::bind(&LLPanelClassifiedEdit::onTextureSelected, this));
  545. return TRUE;
  546. }
  547. void LLPanelClassifiedEdit::fillIn(const LLSD& key)
  548. {
  549. setAvatarId(gAgent.getID());
  550. if(key.isUndefined())
  551. {
  552. setPosGlobal(gAgent.getPositionGlobal());
  553. LLUUID snapshot_id = LLUUID::null;
  554. std::string desc;
  555. LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
  556. if(parcel)
  557. {
  558. desc = parcel->getDesc();
  559. snapshot_id = parcel->getSnapshotID();
  560. }
  561. std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish");
  562. LLViewerRegion* region = gAgent.getRegion();
  563. if (region)
  564. {
  565. region_name = region->getName();
  566. }
  567. getChild<LLUICtrl>("classified_name")->setValue(makeClassifiedName());
  568. getChild<LLUICtrl>("classified_desc")->setValue(desc);
  569. setSnapshotId(snapshot_id);
  570. setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal()));
  571. // server will set valid parcel id
  572. setParcelId(LLUUID::null);
  573. }
  574. else
  575. {
  576. setClassifiedId(key["classified_id"]);
  577. setClassifiedName(key["name"]);
  578. setDescription(key["desc"]);
  579. setSnapshotId(key["snapshot_id"]);
  580. setCategory((U32)key["category"].asInteger());
  581. setContentType((U32)key["content_type"].asInteger());
  582. setClassifiedLocation(key["location_text"]);
  583. getChild<LLUICtrl>("auto_renew")->setValue(key["auto_renew"]);
  584. getChild<LLUICtrl>("price_for_listing")->setValue(key["price_for_listing"].asInteger());
  585. }
  586. }
  587. void LLPanelClassifiedEdit::onOpen(const LLSD& key)
  588. {
  589. mIsNew = key.isUndefined();
  590. scrollToTop();
  591. // classified is not created yet
  592. bool is_new = isNew() || isNewWithErrors();
  593. if(is_new)
  594. {
  595. resetData();
  596. resetControls();
  597. fillIn(key);
  598. if(isNew())
  599. {
  600. LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this);
  601. }
  602. }
  603. else
  604. {
  605. LLPanelClassifiedInfo::onOpen(key);
  606. }
  607. std::string save_btn_label = is_new ? getString("publish_label") : getString("save_label");
  608. getChild<LLUICtrl>("save_changes_btn")->setLabelArg("[LABEL]", save_btn_label);
  609. enableVerbs(is_new);
  610. enableEditing(is_new);
  611. showEditing(!is_new);
  612. resetDirty();
  613. setInfoLoaded(false);
  614. }
  615. void LLPanelClassifiedEdit::processProperties(void* data, EAvatarProcessorType type)
  616. {
  617. if(APT_CLASSIFIED_INFO == type)
  618. {
  619. LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data);
  620. if(c_info && getClassifiedId() == c_info->classified_id)
  621. {
  622. // see LLPanelClassifiedEdit::sendUpdate() for notes
  623. mIsNewWithErrors = false;
  624. // for just created classified - panel will probably be closed when we get here.
  625. if(!getVisible())
  626. {
  627. return;
  628. }
  629. enableEditing(true);
  630. setClassifiedName(c_info->name);
  631. setDescription(c_info->description);
  632. setSnapshotId(c_info->snapshot_id);
  633. setPosGlobal(c_info->pos_global);
  634. setClassifiedLocation(createLocationText(c_info->parcel_name, c_info->sim_name, c_info->pos_global));
  635. // *HACK see LLPanelClassifiedEdit::sendUpdate()
  636. setCategory(c_info->category - 1);
  637. bool mature = is_cf_mature(c_info->flags);
  638. bool auto_renew = is_cf_auto_renew(c_info->flags);
  639. setContentType(mature ? CB_ITEM_MATURE : CB_ITEM_PG);
  640. getChild<LLUICtrl>("auto_renew")->setValue(auto_renew);
  641. getChild<LLUICtrl>("price_for_listing")->setValue(c_info->price_for_listing);
  642. getChildView("price_for_listing")->setEnabled(isNew());
  643. resetDirty();
  644. setInfoLoaded(true);
  645. enableVerbs(false);
  646. // for just created classified - in case user opened edit panel before processProperties() callback
  647. getChild<LLUICtrl>("save_changes_btn")->setLabelArg("[LABEL]", getString("save_label"));
  648. }
  649. }
  650. }
  651. BOOL LLPanelClassifiedEdit::isDirty() const
  652. {
  653. if(mIsNew)
  654. {
  655. return TRUE;
  656. }
  657. BOOL dirty = false;
  658. dirty |= LLPanelClassifiedInfo::isDirty();
  659. dirty |= getChild<LLUICtrl>("classified_snapshot")->isDirty();
  660. dirty |= getChild<LLUICtrl>("classified_name")->isDirty();
  661. dirty |= getChild<LLUICtrl>("classified_desc")->isDirty();
  662. dirty |= getChild<LLUICtrl>("category")->isDirty();
  663. dirty |= getChild<LLUICtrl>("content_type")->isDirty();
  664. dirty |= getChild<LLUICtrl>("auto_renew")->isDirty();
  665. dirty |= getChild<LLUICtrl>("price_for_listing")->isDirty();
  666. return dirty;
  667. }
  668. void LLPanelClassifiedEdit::resetDirty()
  669. {
  670. LLPanelClassifiedInfo::resetDirty();
  671. getChild<LLUICtrl>("classified_snapshot")->resetDirty();
  672. getChild<LLUICtrl>("classified_name")->resetDirty();
  673. LLTextEditor* desc = getChild<LLTextEditor>("classified_desc");
  674. // call blockUndo() to really reset dirty(and make isDirty work as intended)
  675. desc->blockUndo();
  676. desc->resetDirty();
  677. getChild<LLUICtrl>("category")->resetDirty();
  678. getChild<LLUICtrl>("content_type")->resetDirty();
  679. getChild<LLUICtrl>("auto_renew")->resetDirty();
  680. getChild<LLUICtrl>("price_for_listing")->resetDirty();
  681. }
  682. void LLPanelClassifiedEdit::setSaveCallback(const commit_signal_t::slot_type& cb)
  683. {
  684. mSaveButtonClickedSignal.connect(cb);
  685. }
  686. void LLPanelClassifiedEdit::setCancelCallback(const commit_signal_t::slot_type& cb)
  687. {
  688. getChild<LLButton>("cancel_btn")->setClickedCallback(cb);
  689. }
  690. void LLPanelClassifiedEdit::resetControls()
  691. {
  692. LLPanelClassifiedInfo::resetControls();
  693. getChild<LLComboBox>("category")->setCurrentByIndex(0);
  694. getChild<LLComboBox>("content_type")->setCurrentByIndex(0);
  695. getChild<LLUICtrl>("auto_renew")->setValue(false);
  696. getChild<LLUICtrl>("price_for_listing")->setValue(MINIMUM_PRICE_FOR_LISTING);
  697. getChildView("price_for_listing")->setEnabled(TRUE);
  698. }
  699. bool LLPanelClassifiedEdit::canClose()
  700. {
  701. return mCanClose;
  702. }
  703. void LLPanelClassifiedEdit::draw()
  704. {
  705. LLPanel::draw();
  706. // Need to re-stretch on every draw because LLTextureCtrl::onSelectCallback
  707. // does not trigger callbacks when user navigates through images.
  708. stretchSnapshot();
  709. }
  710. void LLPanelClassifiedEdit::stretchSnapshot()
  711. {
  712. LLPanelClassifiedInfo::stretchSnapshot();
  713. getChild<LLUICtrl>("edit_icon")->setShape(mSnapshotCtrl->getRect());
  714. }
  715. U32 LLPanelClassifiedEdit::getContentType()
  716. {
  717. LLComboBox* ct_cb = getChild<LLComboBox>("content_type");
  718. return ct_cb->getCurrentIndex();
  719. }
  720. void LLPanelClassifiedEdit::setContentType(U32 content_type)
  721. {
  722. LLComboBox* ct_cb = getChild<LLComboBox>("content_type");
  723. ct_cb->setCurrentByIndex(content_type);
  724. ct_cb->resetDirty();
  725. }
  726. bool LLPanelClassifiedEdit::getAutoRenew()
  727. {
  728. return getChild<LLUICtrl>("auto_renew")->getValue().asBoolean();
  729. }
  730. void LLPanelClassifiedEdit::sendUpdate()
  731. {
  732. LLAvatarClassifiedInfo c_data;
  733. if(getClassifiedId().isNull())
  734. {
  735. setClassifiedId(LLUUID::generateNewID());
  736. }
  737. c_data.agent_id = gAgent.getID();
  738. c_data.classified_id = getClassifiedId();
  739. // *HACK
  740. // Categories on server start with 1 while combo-box index starts with 0
  741. c_data.category = getCategory() + 1;
  742. c_data.name = getClassifiedName();
  743. c_data.description = getDescription();
  744. c_data.parcel_id = getParcelId();
  745. c_data.snapshot_id = getSnapshotId();
  746. c_data.pos_global = getPosGlobal();
  747. c_data.flags = getFlags();
  748. c_data.price_for_listing = getPriceForListing();
  749. LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoUpdate(&c_data);
  750. if(isNew())
  751. {
  752. // Lets assume there will be some error.
  753. // Successful sendClassifiedInfoUpdate will trigger processProperties and
  754. // let us know there was no error.
  755. mIsNewWithErrors = true;
  756. }
  757. }
  758. U32 LLPanelClassifiedEdit::getCategory()
  759. {
  760. LLComboBox* cat_cb = getChild<LLComboBox>("category");
  761. return cat_cb->getCurrentIndex();
  762. }
  763. void LLPanelClassifiedEdit::setCategory(U32 category)
  764. {
  765. LLComboBox* cat_cb = getChild<LLComboBox>("category");
  766. cat_cb->setCurrentByIndex(category);
  767. cat_cb->resetDirty();
  768. }
  769. U8 LLPanelClassifiedEdit::getFlags()
  770. {
  771. bool auto_renew = getChild<LLUICtrl>("auto_renew")->getValue().asBoolean();
  772. LLComboBox* content_cb = getChild<LLComboBox>("content_type");
  773. bool mature = content_cb->getCurrentIndex() == CB_ITEM_MATURE;
  774. return pack_classified_flags_request(auto_renew, false, mature, false);
  775. }
  776. void LLPanelClassifiedEdit::enableVerbs(bool enable)
  777. {
  778. getChildView("save_changes_btn")->setEnabled(enable);
  779. }
  780. void LLPanelClassifiedEdit::enableEditing(bool enable)
  781. {
  782. getChildView("classified_snapshot")->setEnabled(enable);
  783. getChildView("classified_name")->setEnabled(enable);
  784. getChildView("classified_desc")->setEnabled(enable);
  785. getChildView("set_to_curr_location_btn")->setEnabled(enable);
  786. getChildView("category")->setEnabled(enable);
  787. getChildView("content_type")->setEnabled(enable);
  788. getChildView("price_for_listing")->setEnabled(enable);
  789. getChildView("auto_renew")->setEnabled(enable);
  790. }
  791. void LLPanelClassifiedEdit::showEditing(bool show)
  792. {
  793. getChildView("price_for_listing_label")->setVisible( show);
  794. getChildView("price_for_listing")->setVisible( show);
  795. }
  796. std::string LLPanelClassifiedEdit::makeClassifiedName()
  797. {
  798. std::string name;
  799. LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
  800. if(parcel)
  801. {
  802. name = parcel->getName();
  803. }
  804. if(!name.empty())
  805. {
  806. return name;
  807. }
  808. LLViewerRegion* region = gAgent.getRegion();
  809. if(region)
  810. {
  811. name = region->getName();
  812. }
  813. return name;
  814. }
  815. S32 LLPanelClassifiedEdit::getPriceForListing()
  816. {
  817. return getChild<LLUICtrl>("price_for_listing")->getValue().asInteger();
  818. }
  819. void LLPanelClassifiedEdit::setPriceForListing(S32 price)
  820. {
  821. getChild<LLUICtrl>("price_for_listing")->setValue(price);
  822. }
  823. void LLPanelClassifiedEdit::onSetLocationClick()
  824. {
  825. setPosGlobal(gAgent.getPositionGlobal());
  826. setParcelId(LLUUID::null);
  827. std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish");
  828. LLViewerRegion* region = gAgent.getRegion();
  829. if (region)
  830. {
  831. region_name = region->getName();
  832. }
  833. setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal()));
  834. // mark classified as dirty
  835. setValue(LLSD());
  836. onChange();
  837. }
  838. void LLPanelClassifiedEdit::onChange()
  839. {
  840. enableVerbs(isDirty());
  841. }
  842. void LLPanelClassifiedEdit::onSaveClick()
  843. {
  844. mCanClose = false;
  845. if(!isValidName())
  846. {
  847. notifyInvalidName();
  848. return;
  849. }
  850. if(isNew() || isNewWithErrors())
  851. {
  852. if(gStatusBar->getBalance() < getPriceForListing())
  853. {
  854. LLNotificationsUtil::add("ClassifiedInsufficientFunds");
  855. return;
  856. }
  857. mPublishFloater = LLFloaterReg::findTypedInstance<LLPublishClassifiedFloater>(
  858. "publish_classified", LLSD());
  859. if(!mPublishFloater)
  860. {
  861. mPublishFloater = LLFloaterReg::getTypedInstance<LLPublishClassifiedFloater>(
  862. "publish_classified", LLSD());
  863. mPublishFloater->setPublishClickedCallback(boost::bind
  864. (&LLPanelClassifiedEdit::onPublishFloaterPublishClicked, this));
  865. }
  866. // set spinner value before it has focus or value wont be set
  867. mPublishFloater->setPrice(getPriceForListing());
  868. mPublishFloater->openFloater(mPublishFloater->getKey());
  869. mPublishFloater->center();
  870. }
  871. else
  872. {
  873. doSave();
  874. }
  875. }
  876. void LLPanelClassifiedEdit::doSave()
  877. {
  878. mCanClose = true;
  879. sendUpdate();
  880. resetDirty();
  881. mSaveButtonClickedSignal(this, LLSD());
  882. }
  883. void LLPanelClassifiedEdit::onPublishFloaterPublishClicked()
  884. {
  885. setPriceForListing(mPublishFloater->getPrice());
  886. doSave();
  887. }
  888. std::string LLPanelClassifiedEdit::getLocationNotice()
  889. {
  890. static std::string location_notice = getString("location_notice");
  891. return location_notice;
  892. }
  893. bool LLPanelClassifiedEdit::isValidName()
  894. {
  895. std::string name = getClassifiedName();
  896. if (name.empty())
  897. {
  898. return false;
  899. }
  900. if (!isalnum(name[0]))
  901. {
  902. return false;
  903. }
  904. return true;
  905. }
  906. void LLPanelClassifiedEdit::notifyInvalidName()
  907. {
  908. std::string name = getClassifiedName();
  909. if (name.empty())
  910. {
  911. LLNotificationsUtil::add("BlankClassifiedName");
  912. }
  913. else if (!isalnum(name[0]))
  914. {
  915. LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric");
  916. }
  917. }
  918. void LLPanelClassifiedEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl)
  919. {
  920. ctrl->setVisible(TRUE);
  921. }
  922. void LLPanelClassifiedEdit::onTexturePickerMouseLeave(LLUICtrl* ctrl)
  923. {
  924. ctrl->setVisible(FALSE);
  925. }
  926. void LLPanelClassifiedEdit::onTextureSelected()
  927. {
  928. setSnapshotId(mSnapshotCtrl->getValue().asUUID());
  929. }
  930. //////////////////////////////////////////////////////////////////////////
  931. //////////////////////////////////////////////////////////////////////////
  932. //////////////////////////////////////////////////////////////////////////
  933. LLPublishClassifiedFloater::LLPublishClassifiedFloater(const LLSD& key)
  934. : LLFloater(key)
  935. {
  936. }
  937. LLPublishClassifiedFloater::~LLPublishClassifiedFloater()
  938. {
  939. }
  940. BOOL LLPublishClassifiedFloater::postBuild()
  941. {
  942. LLFloater::postBuild();
  943. childSetAction("publish_btn", boost::bind(&LLFloater::closeFloater, this, false));
  944. childSetAction("cancel_btn", boost::bind(&LLFloater::closeFloater, this, false));
  945. return TRUE;
  946. }
  947. void LLPublishClassifiedFloater::setPrice(S32 price)
  948. {
  949. getChild<LLUICtrl>("price_for_listing")->setValue(price);
  950. }
  951. S32 LLPublishClassifiedFloater::getPrice()
  952. {
  953. return getChild<LLUICtrl>("price_for_listing")->getValue().asInteger();
  954. }
  955. void LLPublishClassifiedFloater::setPublishClickedCallback(const commit_signal_t::slot_type& cb)
  956. {
  957. getChild<LLButton>("publish_btn")->setClickedCallback(cb);
  958. }
  959. void LLPublishClassifiedFloater::setCancelClickedCallback(const commit_signal_t::slot_type& cb)
  960. {
  961. getChild<LLButton>("cancel_btn")->setClickedCallback(cb);
  962. }
  963. //EOF