llpaneldirbrowser.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. /**
  2. * @file llpaneldirbrowser.cpp
  3. * @brief LLPanelDirBrowser class implementation
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. // Base class for the various search panels/results browsers in the Search
  33. // floater. For example, Search > Places is derived from this.
  34. #include "llviewerprecompiledheaders.h"
  35. #include <sstream>
  36. #include "llpaneldirbrowser.h"
  37. #include "llbutton.h"
  38. #include "llcheckboxctrl.h"
  39. #include "lllineeditor.h"
  40. #include "llqueryflags.h"
  41. #include "llscrolllistctrl.h"
  42. #include "lltrans.h"
  43. #include "lluictrlfactory.h"
  44. #include "llmessage.h"
  45. #include "llagent.h"
  46. #include "llfloateravatarinfo.h"
  47. #include "hbfloatersearch.h"
  48. #include "llgridmanager.h"
  49. #include "llpanelavatar.h"
  50. #include "llpanelclassified.h"
  51. #include "llpaneldirland.h"
  52. #include "llpanelevent.h"
  53. #include "llpanelgroup.h"
  54. #include "llpanelpick.h"
  55. #include "llpanelplace.h"
  56. #include "llproductinforequest.h"
  57. #include "llviewercontrol.h"
  58. #include "llviewertexturelist.h"
  59. //static
  60. LLPanelDirBrowser::instances_map_t LLPanelDirBrowser::sInstances;
  61. LLPanelDirBrowser::LLPanelDirBrowser(const std::string& name,
  62. HBFloaterSearch* floater)
  63. : LLPanel(name),
  64. mCurrentSortColumn("name"),
  65. mCurrentSortAscending(true),
  66. mSearchStart(0),
  67. mResultsPerPage(100),
  68. mResultsReceived(0),
  69. mMinSearchChars(1),
  70. mHaveSearchResults(false),
  71. mDidAutoSelect(true),
  72. mFloaterSearch(floater),
  73. mLastWantPGOnly(true),
  74. mLastCanAccessMature(true),
  75. mLastCanAccessAdult(true),
  76. mIncAdultCheck(NULL),
  77. mIncMatureCheck(NULL),
  78. mIncPGCheck(NULL),
  79. mPrevButton(NULL),
  80. mNextButton(NULL),
  81. mResultsList(NULL)
  82. {
  83. }
  84. //virtual
  85. bool LLPanelDirBrowser::postBuild()
  86. {
  87. mIncAdultCheck = getChild<LLCheckBoxCtrl>("incadult", true, false);
  88. if (mIncAdultCheck)
  89. {
  90. mIncMatureCheck = getChild<LLCheckBoxCtrl>("incmature");
  91. mIncPGCheck = getChild<LLCheckBoxCtrl>("incpg");
  92. // Note: each check box is associated with a control name. Changing the
  93. // control automatically changes the check box but the other way
  94. // around (i.e. doing a setValue() on the checkbox) is not true (only
  95. // a click in the checkbox does change the control accordingly). This
  96. // is why we must use gSavedSettings.setBool("control_name") to set
  97. // the checkboxes in updateMaturityCheckbox(), thus the necessity to
  98. // get the control names (we cache them for speed).
  99. mControlNameAdult = mIncAdultCheck->getControlName();
  100. mControlNameMature = mIncMatureCheck->getControlName();
  101. mControlNamePG = mIncPGCheck->getControlName();
  102. updateMaturityCheckbox(true); // true to force an update
  103. }
  104. mPrevButton = getChild<LLButton>("< Prev", true, false);
  105. if (mPrevButton)
  106. {
  107. mPrevButton->setClickedCallback(onClickPrev, this);
  108. mPrevButton->setVisible(false);
  109. mNextButton = getChild<LLButton>("Next >", true, false);
  110. mNextButton->setClickedCallback(onClickNext, this);
  111. mNextButton->setVisible(false);
  112. mResultsList = getChild<LLScrollListCtrl>("results");
  113. mResultsList->setCommitCallback(onCommitList);
  114. mResultsList->setCallbackUserData(this);
  115. }
  116. return true;
  117. }
  118. //virtual
  119. LLPanelDirBrowser::~LLPanelDirBrowser()
  120. {
  121. sInstances.erase(mSearchID);
  122. }
  123. //virtual
  124. void LLPanelDirBrowser::draw()
  125. {
  126. // *HACK: if the results panel has data, we want to select the first item.
  127. // Unfortunately, we do not know when the find is actually done, so only do
  128. // this if it has been some time since the last packet of results was
  129. // received.
  130. if (mLastResultTimer.getElapsedTimeF32() > 1.f)
  131. {
  132. if (!mDidAutoSelect && mResultsList && !mResultsList->hasFocus())
  133. {
  134. if (mResultsList->getCanSelect())
  135. {
  136. // Select first item by default
  137. mResultsList->selectFirstItem();
  138. mResultsList->setFocus(true);
  139. }
  140. // Request specific data from the server
  141. onCommitList(NULL, this);
  142. }
  143. mDidAutoSelect = true;
  144. }
  145. LLPanel::draw();
  146. }
  147. //virtual
  148. void LLPanelDirBrowser::nextPage()
  149. {
  150. mSearchStart += mResultsPerPage;
  151. if (mPrevButton)
  152. {
  153. mPrevButton->setVisible(true);
  154. }
  155. performQuery();
  156. }
  157. //virtual
  158. void LLPanelDirBrowser::prevPage()
  159. {
  160. mSearchStart -= mResultsPerPage;
  161. if (mPrevButton)
  162. {
  163. mPrevButton->setVisible(mSearchStart > 0);
  164. }
  165. performQuery();
  166. }
  167. void LLPanelDirBrowser::resetSearchStart()
  168. {
  169. mSearchStart = 0;
  170. if (mPrevButton)
  171. {
  172. mPrevButton->setVisible(false);
  173. mNextButton->setVisible(false);
  174. }
  175. }
  176. void LLPanelDirBrowser::updateResultCount()
  177. {
  178. if (!mResultsList) return;
  179. S32 result_count = mHaveSearchResults ? mResultsList->getItemCount() : 0;
  180. std::string result_text;
  181. if (mNextButton && mNextButton->getVisible())
  182. {
  183. // Item count be off by a few if bogus items sent from database
  184. // Just use the number of results per page. JC
  185. result_text = llformat(">%d found", mResultsPerPage);
  186. }
  187. else
  188. {
  189. result_text = llformat("%d found", result_count);
  190. }
  191. childSetValue("result_text", result_text);
  192. if (result_count == 0)
  193. {
  194. // Add none found response
  195. if (mResultsList->getItemCount() == 0)
  196. {
  197. // *TODO: Translate
  198. mResultsList->addCommentText("None found.");
  199. mResultsList->operateOnAll(LLScrollListCtrl::OP_DESELECT);
  200. }
  201. }
  202. else
  203. {
  204. mResultsList->setEnabled(true);
  205. }
  206. }
  207. //static
  208. void LLPanelDirBrowser::onClickPrev(void* data)
  209. {
  210. LLPanelDirBrowser* self = (LLPanelDirBrowser*)data;
  211. if (self)
  212. {
  213. self->prevPage();
  214. }
  215. }
  216. //static
  217. void LLPanelDirBrowser::onClickNext(void* data)
  218. {
  219. LLPanelDirBrowser* self = (LLPanelDirBrowser*)data;
  220. if (self)
  221. {
  222. self->nextPage();
  223. }
  224. }
  225. //static
  226. std::string LLPanelDirBrowser::filterShortWords(const std::string source_str,
  227. S32 shortest_word_length,
  228. bool& was_filtered)
  229. {
  230. // Degenerate case
  231. if (!source_str.length())
  232. {
  233. return "";
  234. }
  235. std::stringstream codec(source_str);
  236. std::string each_word;
  237. std::vector<std::string> all_words;
  238. while (codec >> each_word)
  239. {
  240. all_words.push_back(each_word);
  241. }
  242. std::ostringstream dest_string("");
  243. was_filtered = false;
  244. std::vector<std::string>::iterator iter = all_words.begin();
  245. while (iter != all_words.end())
  246. {
  247. if ((S32)iter->length() >= shortest_word_length)
  248. {
  249. dest_string << *iter;
  250. dest_string << " ";
  251. }
  252. else
  253. {
  254. was_filtered = true;
  255. }
  256. ++iter;
  257. }
  258. return dest_string.str();
  259. }
  260. void LLPanelDirBrowser::updateMaturityCheckbox(bool force)
  261. {
  262. if (!mIncAdultCheck)
  263. {
  264. return;
  265. }
  266. // You only have a choice if your maturity is 'mature' or higher. Logic: if
  267. // you are not at least mature, hide the mature and adult options. After
  268. // that, enable only the options you can legitimately choose. If you're PG
  269. // only, show you the checkbox but don't let you change it.
  270. bool pg_only_access = gAgent.wantsPGOnly();
  271. bool mature_access = gAgent.canAccessMature();
  272. bool adult_access = gAgent.canAccessAdult();
  273. if (!force && pg_only_access == mLastWantPGOnly &&
  274. mature_access == mLastCanAccessMature &&
  275. adult_access == mLastCanAccessAdult)
  276. {
  277. // Nothing to update
  278. return;
  279. }
  280. mLastWantPGOnly = pg_only_access;
  281. mLastCanAccessMature = mature_access;
  282. mLastCanAccessAdult = adult_access;
  283. if (pg_only_access)
  284. {
  285. // Teens do not get mature/adult choices
  286. gSavedSettings.setBool(mControlNamePG.c_str(), true);
  287. gSavedSettings.setBool(mControlNameMature.c_str(), false);
  288. gSavedSettings.setBool(mControlNameAdult.c_str(), false);
  289. mIncPGCheck->setEnabled(false);
  290. mIncMatureCheck->setVisible(false);
  291. mIncAdultCheck->setVisible(false);
  292. }
  293. else
  294. {
  295. mIncPGCheck->setEnabled(true);
  296. mIncMatureCheck->setVisible(true);
  297. mIncAdultCheck->setVisible(true);
  298. if (mature_access)
  299. {
  300. mIncMatureCheck->setEnabled(true);
  301. }
  302. else
  303. {
  304. gSavedSettings.setBool(mControlNameMature.c_str(), false);
  305. mIncMatureCheck->setEnabled(false);
  306. }
  307. if (adult_access)
  308. {
  309. mIncAdultCheck->setEnabled(true);
  310. }
  311. else
  312. {
  313. gSavedSettings.setBool(mControlNameAdult.c_str(), false);
  314. mIncAdultCheck->setEnabled(false);
  315. }
  316. }
  317. }
  318. void LLPanelDirBrowser::selectByUUID(const LLUUID& id)
  319. {
  320. if (!mResultsList) return;
  321. if (mResultsList->setCurrentByID(id))
  322. {
  323. // We got it, do not wait for network. Do not bother looking for this
  324. // in the draw loop.
  325. mWantSelectID.setNull();
  326. // Make sure UI updates.
  327. onCommitList(NULL, this);
  328. }
  329. else
  330. {
  331. // Waiting for this item from the network
  332. mWantSelectID = id;
  333. }
  334. }
  335. void LLPanelDirBrowser::selectEventByID(S32 event_id)
  336. {
  337. if (mFloaterSearch)
  338. {
  339. LLPanelEvent* panelp = mFloaterSearch->mPanelEventp;
  340. if (panelp)
  341. {
  342. panelp->setVisible(true);
  343. panelp->setEventID(event_id);
  344. }
  345. }
  346. }
  347. U32 LLPanelDirBrowser::getSelectedEventID() const
  348. {
  349. if (mFloaterSearch)
  350. {
  351. LLPanelEvent* panelp = mFloaterSearch->mPanelEventp;
  352. if (panelp)
  353. {
  354. return panelp->getEventID();
  355. }
  356. }
  357. return 0;
  358. }
  359. void LLPanelDirBrowser::getSelectedInfo(LLUUID* id, S32* type)
  360. {
  361. if (!mResultsList) return;
  362. LLSD id_sd = mResultsList->getValue();
  363. *id = id_sd.asUUID();
  364. std::string id_str = id_sd.asString();
  365. *type = mResultsContents[id_str]["type"];
  366. }
  367. //static
  368. void LLPanelDirBrowser::onCommitList(LLUICtrl* ctrl, void* data)
  369. {
  370. LLPanelDirBrowser* self = (LLPanelDirBrowser*)data;
  371. if (!self || !self->mResultsList) return;
  372. // Start with everyone invisible
  373. if (self->mFloaterSearch)
  374. {
  375. self->mFloaterSearch->hideAllDetailPanels();
  376. }
  377. if (!self->mResultsList->getCanSelect())
  378. {
  379. return;
  380. }
  381. std::string id_str = self->mResultsList->getValue().asString();
  382. if (id_str.empty())
  383. {
  384. return;
  385. }
  386. LLSD item_id = self->mResultsList->getCurrentID();
  387. S32 type = self->mResultsContents[id_str]["type"];
  388. if (type == EVENT_CODE)
  389. {
  390. // All but events use the UUID above
  391. item_id = self->mResultsContents[id_str]["event_id"];
  392. }
  393. self->showDetailPanel(type, item_id);
  394. if (type == FOR_SALE_CODE && self->mFloaterSearch)
  395. {
  396. LLPanelPlace* panelp = self->mFloaterSearch->mPanelPlaceSmallp;
  397. if (panelp)
  398. {
  399. std::string land_type =
  400. self->mResultsContents[id_str]["landtype"].asString();
  401. panelp->setLandTypeString(land_type);
  402. }
  403. }
  404. }
  405. void LLPanelDirBrowser::showDetailPanel(S32 type, LLSD id)
  406. {
  407. if (!mFloaterSearch) return;
  408. switch (type)
  409. {
  410. case AVATAR_CODE:
  411. {
  412. LLPanelAvatar* panelp = mFloaterSearch->mPanelAvatarp;
  413. if (panelp)
  414. {
  415. panelp->setVisible(true);
  416. panelp->setAvatarID(id.asUUID(), LLStringUtil::null,
  417. ONLINE_STATUS_NO);
  418. }
  419. break;
  420. }
  421. case EVENT_CODE:
  422. {
  423. mFloaterSearch->hideAllDetailPanels();
  424. LLPanelEvent* panelp = mFloaterSearch->mPanelEventp;
  425. if (panelp)
  426. {
  427. U32 event_id = (U32)id.asInteger();
  428. panelp->setVisible(true);
  429. panelp->setEventID(event_id);
  430. }
  431. break;
  432. }
  433. case GROUP_CODE:
  434. {
  435. LLPanel* holderp = mFloaterSearch->mPanelGroupHolderp;
  436. if (holderp)
  437. {
  438. holderp->setVisible(true);
  439. }
  440. LLPanelGroup* panelp = mFloaterSearch->mPanelGroupp;
  441. if (panelp)
  442. {
  443. panelp->setVisible(true);
  444. panelp->setGroupID(id.asUUID());
  445. }
  446. break;
  447. }
  448. case CLASSIFIED_CODE:
  449. {
  450. LLPanelClassified* panelp = mFloaterSearch->mPanelClassifiedp;
  451. if (panelp)
  452. {
  453. panelp->setVisible(true);
  454. panelp->setClassifiedID(id.asUUID());
  455. panelp->sendClassifiedInfoRequest();
  456. }
  457. break;
  458. }
  459. case FOR_SALE_CODE:
  460. case AUCTION_CODE:
  461. {
  462. LLPanelPlace* panelp = mFloaterSearch->mPanelPlaceSmallp;
  463. if (panelp)
  464. {
  465. panelp->setVisible(true);
  466. panelp->resetLocation();
  467. panelp->setParcelID(id.asUUID());
  468. }
  469. break;
  470. }
  471. case PLACE_CODE:
  472. case POPULAR_CODE:
  473. {
  474. LLPanelPlace* panelp = mFloaterSearch->mPanelPlacep;
  475. if (panelp)
  476. {
  477. panelp->setVisible(true);
  478. panelp->resetLocation();
  479. panelp->setParcelID(id.asUUID());
  480. }
  481. break;
  482. }
  483. default:
  484. llwarns << "Unknown event type: " << type << llendl;
  485. }
  486. }
  487. void LLPanelDirBrowser::processDirPeopleReply(LLMessageSystem* msg, void**)
  488. {
  489. LLUUID query_id;
  490. msg->getUUIDFast(_PREHASH_QueryData,_PREHASH_QueryID, query_id);
  491. LLPanelDirBrowser* self = get_ptr_in_map(sInstances, query_id);
  492. if (!self || !self->mResultsList)
  493. {
  494. // Data from an old query
  495. return;
  496. }
  497. self->mHaveSearchResults = true;
  498. if (!self->mResultsList->getCanSelect())
  499. {
  500. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  501. self->mResultsContents = LLSD();
  502. }
  503. S32 rows = msg->getNumberOfBlocksFast(_PREHASH_QueryReplies);
  504. self->mResultsReceived += rows;
  505. LLUUID agent_id;
  506. std::string first_name, last_name;
  507. rows = self->showNextButton(rows);
  508. for (S32 i = 0; i < rows; ++i)
  509. {
  510. msg->getStringFast(_PREHASH_QueryReplies, _PREHASH_FirstName,
  511. first_name, i);
  512. msg->getStringFast(_PREHASH_QueryReplies, _PREHASH_LastName,
  513. last_name, i);
  514. msg->getUUIDFast(_PREHASH_QueryReplies, _PREHASH_AgentID,
  515. agent_id, i);
  516. #if 0 // Legacy data, no more used.
  517. msg->getU8Fast(_PREHASH_QueryReplies, _PREHASH_Online, online, i);
  518. msg->getStringFast(_PREHASH_QueryReplies, _PREHASH_Group, group, i);
  519. msg->getS32Fast(_PREHASH_QueryReplies, _PREHASH_Reputation,
  520. reputation, i);
  521. #endif
  522. if (agent_id.isNull())
  523. {
  524. continue;
  525. }
  526. LLSD content;
  527. LLSD row;
  528. row["id"] = agent_id;
  529. // We do not show online status in the finder anymore, so just use the
  530. // 'offline' icon as the generic 'person' icon
  531. LLSD& columns = row["columns"];
  532. columns[0]["column"] = "icon";
  533. columns[0]["type"] = "icon";
  534. columns[0]["value"] = "icon_avatar_offline.tga";
  535. content["type"] = AVATAR_CODE;
  536. std::string fullname = first_name + " " + last_name;
  537. columns[1]["column"] = "name";
  538. columns[1]["value"] = fullname;
  539. columns[1]["font"] = "SANSSERIF";
  540. content["name"] = fullname;
  541. self->mResultsList->addElement(row);
  542. self->mResultsContents[agent_id.asString()] = content;
  543. }
  544. self->mResultsList->sortByColumn(self->mCurrentSortColumn,
  545. self->mCurrentSortAscending);
  546. self->updateResultCount();
  547. // Poke the result received timer
  548. self->mLastResultTimer.reset();
  549. self->mDidAutoSelect = false;
  550. }
  551. void LLPanelDirBrowser::processDirPlacesReply(LLMessageSystem* msg, void**)
  552. {
  553. LLUUID agent_id, query_id;
  554. msg->getUUID("AgentData", "AgentID", agent_id);
  555. msg->getUUID("QueryData", "QueryID", query_id);
  556. if (msg->getNumberOfBlocks("StatusData"))
  557. {
  558. U32 status;
  559. msg->getU32("StatusData", "Status", status);
  560. if (status & STATUS_SEARCH_PLACES_BANNEDWORD)
  561. {
  562. gNotifications.add("SearchWordBanned");
  563. }
  564. }
  565. LLPanelDirBrowser* self = get_ptr_in_map(sInstances, query_id);
  566. if (!self)
  567. {
  568. // Data from an old query
  569. return;
  570. }
  571. self->mHaveSearchResults = true;
  572. if (!self->mResultsList) return;
  573. if (!self->mResultsList->getCanSelect())
  574. {
  575. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  576. self->mResultsContents = LLSD();
  577. }
  578. S32 count = msg->getNumberOfBlocks("QueryReplies");
  579. self->mResultsReceived += count;
  580. LLUUID parcel_id;
  581. std::string name;
  582. F32 dwell;
  583. bool is_for_sale, is_auction;
  584. count = self->showNextButton(count);
  585. for (S32 i = 0; i < count ; ++i)
  586. {
  587. msg->getUUID("QueryReplies", "ParcelID", parcel_id, i);
  588. msg->getString("QueryReplies", "Name", name, i);
  589. msg->getBool("QueryReplies", "ForSale", is_for_sale, i);
  590. msg->getBool("QueryReplies", "Auction", is_auction, i);
  591. msg->getF32("QueryReplies", "Dwell", dwell, i);
  592. if (parcel_id.isNull())
  593. {
  594. continue;
  595. }
  596. S32 type;
  597. LLSD row = self->createLandSale(parcel_id, is_auction, is_for_sale,
  598. name, &type);
  599. LLSD content;
  600. content["type"] = type;
  601. content["name"] = name;
  602. LLSD& columns = row["columns"];
  603. columns[3]["column"] = "dwell";
  604. columns[3]["value"] = llformat("%.0f", (F64)dwell);
  605. columns[3]["font"] = "SANSSERIF_SMALL";
  606. self->mResultsList->addElement(row);
  607. self->mResultsContents[parcel_id.asString()] = content;
  608. }
  609. self->mResultsList->sortByColumn(self->mCurrentSortColumn,
  610. self->mCurrentSortAscending);
  611. self->updateResultCount();
  612. // Poke the result received timer
  613. self->mLastResultTimer.reset();
  614. self->mDidAutoSelect = false;
  615. }
  616. void LLPanelDirBrowser::processDirEventsReply(LLMessageSystem* msg, void**)
  617. {
  618. LLUUID agent_id, query_id;
  619. msg->getUUID("AgentData", "AgentID", agent_id);
  620. msg->getUUID("QueryData", "QueryID", query_id);
  621. LLPanelDirBrowser* self = get_ptr_in_map(sInstances, query_id);
  622. if (!self)
  623. {
  624. return;
  625. }
  626. if (msg->getNumberOfBlocks("StatusData"))
  627. {
  628. U32 status;
  629. msg->getU32("StatusData", "Status", status);
  630. if (status & STATUS_SEARCH_EVENTS_BANNEDWORD)
  631. {
  632. gNotifications.add("SearchWordBanned");
  633. }
  634. }
  635. self->mHaveSearchResults = true;
  636. if (!self->mResultsList) return;
  637. if (!self->mResultsList->getCanSelect())
  638. {
  639. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  640. self->mResultsContents = LLSD();
  641. }
  642. S32 rows = msg->getNumberOfBlocks("QueryReplies");
  643. self->mResultsReceived += rows;
  644. bool show_pg = gSavedSettings.getBool("ShowPGEvents");
  645. bool show_mature = gSavedSettings.getBool("ShowMatureEvents");
  646. bool show_adult = gSavedSettings.getBool("ShowAdultEvents");
  647. std::string time_format = "%m-%d " +
  648. gSavedSettings.getString("ShortTimeFormat");
  649. LLUUID owner_id;
  650. std::string name;
  651. rows = self->showNextButton(rows);
  652. for (S32 i = 0; i < rows; ++i)
  653. {
  654. U32 event_id;
  655. U32 unix_time;
  656. U32 event_flags;
  657. msg->getUUID("QueryReplies", "OwnerID", owner_id, i);
  658. msg->getString("QueryReplies", "Name", name, i);
  659. msg->getU32("QueryReplies", "EventID", event_id, i);
  660. #if 0
  661. msg->getString("QueryReplies", "Date", date, i);
  662. #endif
  663. msg->getU32("QueryReplies", "UnixTime", unix_time, i);
  664. msg->getU32("QueryReplies", "EventFlags", event_flags, i);
  665. // Skip empty events
  666. if (owner_id.isNull())
  667. {
  668. // RN: should this check event_id instead ?
  669. llwarns << "skipped event due to owner_id null, event_id "
  670. << event_id << llendl;
  671. continue;
  672. }
  673. // Skip events that do not match the flags; there's no PG flag, so we
  674. // make sure neither adult nor mature is set
  675. if (!show_pg &&
  676. ((event_flags & (EVENT_FLAG_ADULT | EVENT_FLAG_MATURE)) ==
  677. EVENT_FLAG_NONE))
  678. {
  679. continue;
  680. }
  681. if (!show_mature && (event_flags & EVENT_FLAG_MATURE))
  682. {
  683. continue;
  684. }
  685. if (!show_adult && (event_flags & EVENT_FLAG_ADULT))
  686. {
  687. continue;
  688. }
  689. LLSD content;
  690. content["type"] = EVENT_CODE;
  691. content["name"] = name;
  692. content["event_id"] = (S32)event_id;
  693. LLSD row;
  694. row["id"] = llformat("%u", event_id);
  695. LLSD& columns = row["columns"];
  696. // Column 0 - event icon
  697. if (event_flags == EVENT_FLAG_ADULT)
  698. {
  699. columns[0]["column"] = "icon";
  700. columns[0]["type"] = "icon";
  701. columns[0]["value"] = "icon_event_adult.tga";
  702. }
  703. else if (event_flags == EVENT_FLAG_MATURE)
  704. {
  705. columns[0]["column"] = "icon";
  706. columns[0]["type"] = "icon";
  707. columns[0]["value"] = "icon_event_mature.tga";
  708. }
  709. else
  710. {
  711. columns[0]["column"] = "icon";
  712. columns[0]["type"] = "icon";
  713. columns[0]["value"] = "icon_event.tga";
  714. }
  715. columns[1]["column"] = "name";
  716. columns[1]["value"] = name;
  717. columns[1]["font"] = "SANSSERIF";
  718. columns[2]["column"] = "date";
  719. columns[2]["value"] = LLGridManager::getTimeStamp(unix_time,
  720. time_format);
  721. columns[2]["font"] = "SANSSERIF_SMALL";
  722. columns[3]["column"] = "time";
  723. columns[3]["value"] = llformat("%u", unix_time);
  724. columns[3]["font"] = "SANSSERIF_SMALL";
  725. self->mResultsList->addElement(row, ADD_SORTED);
  726. std::string id_str = llformat("%u", event_id);
  727. self->mResultsContents[id_str] = content;
  728. }
  729. self->mResultsList->sortByColumn(self->mCurrentSortColumn,
  730. self->mCurrentSortAscending);
  731. self->updateResultCount();
  732. // Poke the result received timer
  733. self->mLastResultTimer.reset();
  734. self->mDidAutoSelect = false;
  735. }
  736. //static
  737. void LLPanelDirBrowser::processDirGroupsReply(LLMessageSystem* msg, void**)
  738. {
  739. LLUUID query_id;
  740. msg->getUUIDFast(_PREHASH_QueryData,_PREHASH_QueryID, query_id);
  741. LLPanelDirBrowser* self = get_ptr_in_map(sInstances, query_id);
  742. if (!self)
  743. {
  744. return;
  745. }
  746. self->mHaveSearchResults = true;
  747. if (!self->mResultsList) return;
  748. if (!self->mResultsList->getCanSelect())
  749. {
  750. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  751. self->mResultsContents = LLSD();
  752. }
  753. S32 rows = msg->getNumberOfBlocksFast(_PREHASH_QueryReplies);
  754. self->mResultsReceived += rows;
  755. S32 members;
  756. F32 search_order;
  757. LLUUID group_id;
  758. std::string group_name;
  759. rows = self->showNextButton(rows);
  760. for (S32 i = 0; i < rows; ++i)
  761. {
  762. msg->getUUIDFast(_PREHASH_QueryReplies, _PREHASH_GroupID, group_id, i);
  763. msg->getStringFast(_PREHASH_QueryReplies, _PREHASH_GroupName,
  764. group_name, i);
  765. msg->getS32Fast(_PREHASH_QueryReplies, _PREHASH_Members, members, i);
  766. msg->getF32Fast(_PREHASH_QueryReplies, _PREHASH_SearchOrder,
  767. search_order, i);
  768. if (group_id.isNull())
  769. {
  770. continue;
  771. }
  772. LLSD content;
  773. content["type"] = GROUP_CODE;
  774. content["name"] = group_name;
  775. LLSD row;
  776. row["id"] = group_id;
  777. LLSD& columns = row["columns"];
  778. columns[0]["column"] = "icon";
  779. columns[0]["type"] = "icon";
  780. columns[0]["value"] = "icon_group.tga";
  781. columns[1]["column"] = "name";
  782. columns[1]["value"] = group_name;
  783. columns[1]["font"] = "SANSSERIF";
  784. columns[2]["column"] = "members";
  785. columns[2]["value"] = members;
  786. columns[2]["font"] = "SANSSERIF_SMALL";
  787. columns[3]["column"] = "score";
  788. columns[3]["value"] = search_order;
  789. self->mResultsList->addElement(row);
  790. self->mResultsContents[group_id.asString()] = content;
  791. }
  792. self->mResultsList->sortByColumn(self->mCurrentSortColumn,
  793. self->mCurrentSortAscending);
  794. self->updateResultCount();
  795. // Poke the result received timer
  796. self->mLastResultTimer.reset();
  797. self->mDidAutoSelect = false;
  798. }
  799. //static
  800. void LLPanelDirBrowser::processDirClassifiedReply(LLMessageSystem* msg, void**)
  801. {
  802. LLUUID agent_id;
  803. msg->getUUID("AgentData", "AgentID", agent_id);
  804. if (agent_id != gAgentID)
  805. {
  806. llwarns << "Message for wrong agent " << agent_id
  807. << " in processDirClassifiedReply" << llendl;
  808. return;
  809. }
  810. LLUUID query_id;
  811. msg->getUUID("QueryData", "QueryID", query_id);
  812. LLPanelDirBrowser* self = get_ptr_in_map(sInstances, query_id);
  813. if (!self)
  814. {
  815. return;
  816. }
  817. if (msg->getNumberOfBlocks("StatusData"))
  818. {
  819. U32 status;
  820. msg->getU32("StatusData", "Status", status);
  821. if (status & STATUS_SEARCH_CLASSIFIEDS_BANNEDWORD)
  822. {
  823. gNotifications.add("SearchWordBanned");
  824. }
  825. }
  826. self->mHaveSearchResults = true;
  827. if (!self->mResultsList) return;
  828. if (!self->mResultsList->getCanSelect())
  829. {
  830. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  831. self->mResultsContents = LLSD();
  832. }
  833. S32 num_new_rows = msg->getNumberOfBlocksFast(_PREHASH_QueryReplies);
  834. self->mResultsReceived += num_new_rows;
  835. LLUUID classified_id;
  836. std::string name;
  837. num_new_rows = self->showNextButton(num_new_rows);
  838. for (S32 i = 0; i < num_new_rows; ++i)
  839. {
  840. msg->getUUID("QueryReplies", "ClassifiedID", classified_id, i);
  841. msg->getString("QueryReplies", "Name", name, i);
  842. U32 creation_date = 0; // unix timestamp
  843. msg->getU32("QueryReplies","CreationDate", creation_date, i);
  844. U32 expiration_date = 0; // future use
  845. msg->getU32("QueryReplies","ExpirationDate", expiration_date, i);
  846. S32 price_for_listing = 0;
  847. msg->getS32("QueryReplies","PriceForListing", price_for_listing, i);
  848. if (classified_id.notNull())
  849. {
  850. self->addClassified(self->mResultsList, classified_id, name,
  851. creation_date, price_for_listing);
  852. LLSD content;
  853. content["type"] = CLASSIFIED_CODE;
  854. content["name"] = name;
  855. self->mResultsContents[classified_id.asString()] = content;
  856. }
  857. }
  858. // The server does the initial sort, by price paid per listing and date. JC
  859. self->updateResultCount();
  860. // Poke the result received timer
  861. self->mLastResultTimer.reset();
  862. self->mDidAutoSelect = false;
  863. }
  864. void LLPanelDirBrowser::processDirLandReply(LLMessageSystem* msg, void**)
  865. {
  866. LLUUID agent_id, query_id;
  867. msg->getUUID("AgentData", "AgentID", agent_id);
  868. msg->getUUID("QueryData", "QueryID", query_id);
  869. LLPanelDirBrowser* browser = get_ptr_in_map(sInstances, query_id);
  870. if (!browser)
  871. {
  872. // Data from an old query
  873. return;
  874. }
  875. // Only handled by LLPanelDirLand
  876. LLPanelDirLand* self = (LLPanelDirLand*)browser;
  877. self->mHaveSearchResults = true;
  878. if (!self->mResultsList) return;
  879. if (!self->mResultsList->getCanSelect())
  880. {
  881. self->mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  882. self->mResultsContents = LLSD();
  883. }
  884. bool use_price = gSavedSettings.getBool("FindLandPrice");
  885. S32 limit_price = self->childGetValue("priceedit").asInteger();
  886. bool use_area = gSavedSettings.getBool("FindLandArea");
  887. S32 limit_area = self->childGetValue("areaedit").asInteger();
  888. S32 count = msg->getNumberOfBlocks("QueryReplies");
  889. self->mResultsReceived += count;
  890. S32 non_auction_count = 0;
  891. S32 sale_price, actual_area;
  892. LLUUID parcel_id;
  893. std::string name, land_sku, land_type;
  894. bool auction, for_sale;
  895. LLProductInfoRequestManager* mgrp =
  896. LLProductInfoRequestManager::getInstance();
  897. for (S32 i = 0; i < count; ++i)
  898. {
  899. msg->getUUID("QueryReplies", "ParcelID", parcel_id, i);
  900. msg->getString("QueryReplies", "Name", name, i);
  901. msg->getBool("QueryReplies", "Auction", auction, i);
  902. msg->getBool("QueryReplies", "ForSale", for_sale, i);
  903. msg->getS32("QueryReplies", "SalePrice", sale_price, i);
  904. msg->getS32("QueryReplies", "ActualArea", actual_area, i);
  905. if (msg->getSizeFast(_PREHASH_QueryReplies, i,
  906. _PREHASH_ProductSKU) > 0)
  907. {
  908. msg->getStringFast(_PREHASH_QueryReplies, _PREHASH_ProductSKU,
  909. land_sku, i);
  910. LL_DEBUGS("Land SKU") << "Land sku: " << land_sku << LL_ENDL;
  911. land_type = mgrp->getDescriptionForSku(land_sku);
  912. }
  913. else
  914. {
  915. land_sku.clear();
  916. land_type = LLTrans::getString("unknown");
  917. }
  918. if (parcel_id.isNull() || (use_price && sale_price > limit_price) ||
  919. (use_area && actual_area < limit_area))
  920. {
  921. continue;
  922. }
  923. S32 type;
  924. LLSD row = self->createLandSale(parcel_id, auction, for_sale, name,
  925. &type);
  926. LLSD content;
  927. content["type"] = type;
  928. content["name"] = name;
  929. content["landtype"] = land_type;
  930. std::string buffer = "Auction";
  931. if (!auction)
  932. {
  933. buffer = llformat("%d", sale_price);
  934. non_auction_count++;
  935. }
  936. LLSD& columns = row["columns"];
  937. columns[3]["column"] = "price";
  938. columns[3]["value"] = buffer;
  939. columns[3]["font"] = "SANSSERIF_SMALL";
  940. buffer = llformat("%d", actual_area);
  941. columns[4]["column"] = "area";
  942. columns[4]["value"] = buffer;
  943. columns[4]["font"] = "SANSSERIF_SMALL";
  944. if (!auction)
  945. {
  946. F32 price_per_meter;
  947. if (actual_area > 0)
  948. {
  949. price_per_meter = (F32)sale_price / (F32)actual_area;
  950. }
  951. else
  952. {
  953. price_per_meter = 0.f;
  954. }
  955. // Prices are usually L$1 - L$10 / meter
  956. buffer = llformat("%.1f", price_per_meter);
  957. columns[5]["column"] = "per_meter";
  958. columns[5]["value"] = buffer;
  959. columns[5]["font"] = "SANSSERIF_SMALL";
  960. }
  961. else
  962. {
  963. // Auctions start at L$1 per meter
  964. columns[5]["column"] = "per_meter";
  965. columns[5]["value"] = "1.0";
  966. columns[5]["font"] = "SANSSERIF_SMALL";
  967. }
  968. columns[6]["column"] = "landtype";
  969. columns[6]["value"] = land_type;
  970. columns[6]["font"] = "SANSSERIF_SMALL";
  971. self->mResultsList->addElement(row);
  972. self->mResultsContents[parcel_id.asString()] = content;
  973. }
  974. // All auction results are shown on the first page. But they do not count
  975. // towards the 100 / page limit. So figure out the next button here, when
  976. // we know how many aren't auctions
  977. count = self->showNextButton(non_auction_count);
  978. self->updateResultCount();
  979. // Poke the result received timer
  980. self->mLastResultTimer.reset();
  981. self->mDidAutoSelect = false;
  982. }
  983. void LLPanelDirBrowser::addClassified(LLScrollListCtrl* listp,
  984. const LLUUID& pick_id,
  985. const std::string& name,
  986. U32 creation_date, S32 price_for_listing)
  987. {
  988. std::string type = llformat("%d", CLASSIFIED_CODE);
  989. LLSD row;
  990. row["id"] = pick_id;
  991. LLSD& columns = row["columns"];
  992. columns[0]["column"] = "icon";
  993. columns[0]["type"] = "icon";
  994. columns[0]["value"] = "icon_top_pick.tga";
  995. columns[1]["column"] = "name";
  996. columns[1]["value"] = name;
  997. columns[1]["font"] = "SANSSERIF";
  998. columns[2]["column"] = "price";
  999. columns[2]["value"] = price_for_listing;
  1000. columns[2]["font"] = "SANSSERIF_SMALL";
  1001. listp->addElement(row);
  1002. }
  1003. LLSD LLPanelDirBrowser::createLandSale(const LLUUID& parcel_id,
  1004. bool is_auction, bool is_for_sale,
  1005. const std::string& name, S32* type)
  1006. {
  1007. LLSD row;
  1008. row["id"] = parcel_id;
  1009. LLSD& columns = row["columns"];
  1010. // Icon and type
  1011. if (is_auction)
  1012. {
  1013. columns[0]["column"] = "icon";
  1014. columns[0]["type"] = "icon";
  1015. columns[0]["value"] = "icon_auction.tga";
  1016. *type = AUCTION_CODE;
  1017. }
  1018. else if (is_for_sale)
  1019. {
  1020. columns[0]["column"] = "icon";
  1021. columns[0]["type"] = "icon";
  1022. columns[0]["value"] = "icon_for_sale.tga";
  1023. *type = FOR_SALE_CODE;
  1024. }
  1025. else
  1026. {
  1027. columns[0]["column"] = "icon";
  1028. columns[0]["type"] = "icon";
  1029. columns[0]["value"] = "icon_place.tga";
  1030. *type = PLACE_CODE;
  1031. }
  1032. columns[2]["column"] = "name";
  1033. columns[2]["value"] = name;
  1034. columns[2]["font"] = "SANSSERIF";
  1035. return row;
  1036. }
  1037. void LLPanelDirBrowser::newClassified()
  1038. {
  1039. if (!mResultsList) return;
  1040. LLPanelClassified* panelp = mFloaterSearch->mPanelClassifiedp;
  1041. if (panelp)
  1042. {
  1043. // Clear the panel on the right
  1044. panelp->reset();
  1045. // Set up the classified with the info we have created and a sane
  1046. // default position.
  1047. panelp->initNewClassified();
  1048. // We need the ID to select in the list.
  1049. LLUUID classified_id = panelp->getClassifiedID();
  1050. // Put it in the list on the left
  1051. addClassified(mResultsList, classified_id, panelp->getClassifiedName(),
  1052. 0, 0);
  1053. // Select it.
  1054. mResultsList->setCurrentByID(classified_id);
  1055. // Make the right panel visible (should already be)
  1056. panelp->setVisible(true);
  1057. }
  1058. }
  1059. void LLPanelDirBrowser::setupNewSearch()
  1060. {
  1061. sInstances.erase(mSearchID);
  1062. mSearchID.generate(); // Make a new query ID
  1063. sInstances[mSearchID] = this;
  1064. if (mResultsList)
  1065. {
  1066. // Ready the list for results
  1067. mResultsList->operateOnAll(LLScrollListCtrl::OP_DELETE);
  1068. // *TODO: translate
  1069. mResultsList->addCommentText("Searching...");
  1070. mResultsList->setEnabled(false);
  1071. }
  1072. mResultsReceived = 0;
  1073. mHaveSearchResults = false;
  1074. // Set all panels to be invisible
  1075. mFloaterSearch->hideAllDetailPanels();
  1076. updateResultCount();
  1077. }
  1078. //static
  1079. // Called from classifieds, events, groups, land, people, and places
  1080. void LLPanelDirBrowser::onClickSearchCore(void* userdata)
  1081. {
  1082. LLPanelDirBrowser* self = (LLPanelDirBrowser*)userdata;
  1083. if (self)
  1084. {
  1085. self->resetSearchStart();
  1086. self->performQuery();
  1087. }
  1088. }
  1089. //static
  1090. void LLPanelDirBrowser::sendDirFindQuery(LLMessageSystem* msg,
  1091. const LLUUID& query_id,
  1092. const std::string& text,
  1093. U32 flags, S32 query_start)
  1094. {
  1095. msg->newMessage("DirFindQuery");
  1096. msg->nextBlock("AgentData");
  1097. msg->addUUID("AgentID", gAgentID);
  1098. msg->addUUID("SessionID", gAgentSessionID);
  1099. msg->nextBlock("QueryData");
  1100. msg->addUUID("QueryID", query_id);
  1101. msg->addString("QueryText", text);
  1102. msg->addU32("QueryFlags", flags);
  1103. msg->addS32("QueryStart", query_start);
  1104. gAgent.sendReliableMessage();
  1105. }
  1106. //static
  1107. void LLPanelDirBrowser::onSearchEdit(const std::string& text, void* data)
  1108. {
  1109. LLPanelDirBrowser* self = (LLPanelDirBrowser*)data;
  1110. if (!self) return;
  1111. if ((U32)text.length() >= self->mMinSearchChars)
  1112. {
  1113. self->setDefaultBtn("search_btn");
  1114. self->childEnable("search_btn");
  1115. }
  1116. else
  1117. {
  1118. self->setDefaultBtn();
  1119. self->childDisable("search_btn");
  1120. }
  1121. }
  1122. // Setups results when shown
  1123. void LLPanelDirBrowser::onVisibilityChange(bool new_visibility)
  1124. {
  1125. if (new_visibility)
  1126. {
  1127. onCommitList(NULL, this);
  1128. }
  1129. LLPanel::onVisibilityChange(new_visibility);
  1130. }
  1131. S32 LLPanelDirBrowser::showNextButton(S32 rows)
  1132. {
  1133. if (!mPrevButton) return rows;
  1134. // *HACK: this hack does not work for LLPanelDirFind because some other
  1135. // data is being returned as well.
  1136. if (getName() != "find_all_panel")
  1137. {
  1138. // *HACK: The mResultsPerPage+1th entry indicates there are 'more'
  1139. bool show_next = mResultsReceived > mResultsPerPage;
  1140. mNextButton->setVisible(show_next);
  1141. if (show_next)
  1142. {
  1143. rows -= mResultsReceived - mResultsPerPage;
  1144. }
  1145. }
  1146. else if (mPrevButton)
  1147. {
  1148. // Hide page buttons
  1149. mPrevButton->setVisible(false);
  1150. mNextButton->setVisible(false);
  1151. }
  1152. return rows;
  1153. }