hbfloaterteleporthistory.cpp 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /**
  2. * @file hbfloaterteleporthistory.cpp
  3. * @author Henri Beauchamp
  4. * @brief HBFloaterTeleportHistory class implementation
  5. *
  6. * $LicenseInfo:firstyear=2018&license=viewergpl$
  7. *
  8. * Copyright (c) 2018, Henri Beauchamp
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at 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. #include "llviewerprecompiledheaders.h"
  33. #include "hbfloaterteleporthistory.h"
  34. #include "llapp.h"
  35. #include "llbutton.h"
  36. #include "llcombobox.h"
  37. #include "lldir.h"
  38. #include "lllineeditor.h"
  39. #include "llscrolllistctrl.h"
  40. #include "llsdserialize.h"
  41. #include "lltabcontainer.h"
  42. #include "lluictrlfactory.h"
  43. #include "llagent.h"
  44. #include "llfloaterworldmap.h"
  45. #include "llgridmanager.h"
  46. //MK
  47. #include "mkrlinterface.h"
  48. //mk
  49. #include "llslurl.h"
  50. #include "llurldispatcher.h"
  51. #include "llviewercontrol.h"
  52. #include "llviewerparcelmgr.h"
  53. #include "llviewerwindow.h"
  54. #include "llvoavatarself.h"
  55. #include "llweb.h" // For escapeURL()
  56. #define COMMENT_PREFIX "\342\200\243 "
  57. // Globals
  58. // Instance created in LLViewerWindow::initWorldUI()
  59. HBFloaterTeleportHistory* gFloaterTeleportHistoryp = NULL;
  60. // Helper function
  61. static std::string get_timestamp()
  62. {
  63. // Make it easy to sort: use the Year-Month-Day ISO convention
  64. std::string time_format = "%Y-%m-%d " +
  65. gSavedSettings.getString("ShortTimeFormat");
  66. return LLGridManager::getTimeStamp(time_corrected(), time_format);
  67. }
  68. // Local class used to populate the favorite places list
  69. class HBTeleportLocation
  70. {
  71. public:
  72. HBTeleportLocation()
  73. : mVisits(1)
  74. {
  75. }
  76. public:
  77. S32 mVisits;
  78. std::string mParcel;
  79. std::string mRegion;
  80. std::string mPosition;
  81. };
  82. // HBFloaterTeleportHistory class
  83. HBFloaterTeleportHistory::HBFloaterTeleportHistory()
  84. : LLFloater("teleport history"),
  85. mPlacesListComment(NULL),
  86. mCount(0),
  87. mFirstOpen(true),
  88. mCanTeleport(false)
  89. {
  90. LLUICtrlFactory::getInstance()->buildFloater(this,
  91. "floater_teleport_history.xml",
  92. NULL, false);
  93. }
  94. //virtual
  95. HBFloaterTeleportHistory::~HBFloaterTeleportHistory()
  96. {
  97. mTeleportArrivingConnection.disconnect();
  98. mTeleportFinishConnection.disconnect();
  99. mTeleportFailedConnection.disconnect();
  100. gFloaterTeleportHistoryp = NULL;
  101. llinfos << "Teleport history instance destroyed." << llendl;
  102. }
  103. //virtual
  104. void HBFloaterTeleportHistory::onFocusReceived()
  105. {
  106. // Take care to enable or disable buttons depending on the selection in the
  107. // places list
  108. setButtonsStatus();
  109. LLFloater::onFocusReceived();
  110. }
  111. bool HBFloaterTeleportHistory::postBuild()
  112. {
  113. mTabContainer = getChild<LLTabContainer>("lists");
  114. LLPanel* tab = mTabContainer->getChild<LLPanel>("tp_history");
  115. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  116. mTabContainer->setTabUserData(tab, this);
  117. tab = mTabContainer->getChild<LLPanel>("favorite_places");
  118. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  119. mTabContainer->setTabUserData(tab, this);
  120. tab = mTabContainer->getChild<LLPanel>("search_places");
  121. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  122. mTabContainer->setTabUserData(tab, this);
  123. mPlacesList = getChild<LLScrollListCtrl>("places_list");
  124. mPlacesList->setDoubleClickCallback(onTeleport);
  125. mPlacesList->setCommitCallback(onPlacesSelected);
  126. mPlacesList->setCallbackUserData(this);
  127. mFavoritesList = getChild<LLScrollListCtrl>("favorites_list");
  128. mFavoritesList->setDoubleClickCallback(onTeleport);
  129. mFavoritesList->setCommitCallback(onPlacesSelected);
  130. mFavoritesList->setCallbackUserData(this);
  131. mResultsList = getChild<LLScrollListCtrl>("results_list");
  132. mResultsList->setDoubleClickCallback(onTeleport);
  133. mResultsList->setCommitCallback(onPlacesSelected);
  134. mResultsList->setCallbackUserData(this);
  135. mSearchEditor = getChild<LLSearchEditor>("search");
  136. mSearchEditor->setSearchCallback(onSearchEdit, this);
  137. mTeleportBtn = getChild<LLButton>("teleport");
  138. mTeleportBtn->setClickedCallback(onTeleport, this);
  139. mShowOnMapBtn = getChild<LLButton>("show_on_map");
  140. mShowOnMapBtn->setClickedCallback(onShowOnMap, this);
  141. mCopySLURLBtn = getChild<LLButton>("copy_slurl");
  142. mCopySLURLBtn->setClickedCallback(onCopySLURL, this);
  143. mRefreshBtn = getChild<LLButton>("refresh");
  144. mRefreshBtn->setClickedCallback(onRefresh, this);
  145. mRemoveFlyoutBtn = getChild<LLFlyoutButton>("remove");
  146. mRemoveFlyoutBtn->setCommitCallback(onRemove);
  147. mRemoveFlyoutBtn->setCallbackUserData(this);
  148. childSetAction("close", onButtonClose, this);
  149. mNumEntriesStr = COMMENT_PREFIX + getString("number_of_entries");
  150. mNoEntryStr = COMMENT_PREFIX + getString("no_entry");
  151. mTeleportArrivingConnection =
  152. gViewerParcelMgr.setTPArrivingCallback(boost::bind(&HBFloaterTeleportHistory::onTeleportArriving));
  153. mTeleportFinishConnection =
  154. gViewerParcelMgr.setTPFinishedCallback(boost::bind(&HBFloaterTeleportHistory::onTeleportFinished,
  155. _1, _2));
  156. mTeleportFailedConnection =
  157. gViewerParcelMgr.setTPFailedCallback(boost::bind(&HBFloaterTeleportHistory::onTeleportFailed));
  158. return true;
  159. }
  160. //virtual
  161. void HBFloaterTeleportHistory::draw()
  162. {
  163. mTeleportBtn->setEnabled(mCanTeleport && !gAgent.teleportInProgress());
  164. LLFloater::draw();
  165. }
  166. void HBFloaterTeleportHistory::setButtonsStatus()
  167. {
  168. LLScrollListCtrl* list = NULL;
  169. S32 active_tab = mTabContainer->getCurrentPanelIndex();
  170. switch (active_tab)
  171. {
  172. case 0:
  173. mRefreshBtn->setVisible(false);
  174. mRemoveFlyoutBtn->setVisible(true);
  175. mSearchEditor->setVisible(false);
  176. list = mPlacesList;
  177. break;
  178. case 1:
  179. mRefreshBtn->setVisible(true);
  180. mRemoveFlyoutBtn->setVisible(false);
  181. mSearchEditor->setVisible(false);
  182. list = mFavoritesList;
  183. break;
  184. case 2:
  185. mRefreshBtn->setVisible(false);
  186. mRemoveFlyoutBtn->setVisible(false);
  187. mSearchEditor->setVisible(true);
  188. list = mResultsList;
  189. break;
  190. default:
  191. llwarns << "Unknown tab !" << llendl;
  192. llassert(false);
  193. return;
  194. }
  195. mCanTeleport = list && list->getFirstSelected();
  196. mShowOnMapBtn->setEnabled(mCanTeleport);
  197. mCopySLURLBtn->setEnabled(mCanTeleport);
  198. mRemoveFlyoutBtn->setEnabled(mCanTeleport);
  199. }
  200. std::string HBFloaterTeleportHistory::getHistoryFileName(bool fallback) const
  201. {
  202. std::string name = gIsInSecondLifeBetaGrid ? "beta_tp_history.xml"
  203. : "tp_history.xml";
  204. name = gDirUtil.getFullPath(LL_PATH_PER_ACCOUNT, name);
  205. if (fallback && !LLFile::isfile(name))
  206. {
  207. name = gDirUtil.getFullPath(LL_PATH_PER_ACCOUNT,
  208. "teleport_history.xml");
  209. }
  210. return name;
  211. }
  212. void HBFloaterTeleportHistory::addPlacesListComment()
  213. {
  214. removePlacesListComment();
  215. std::string comment;
  216. if (mCount > 0)
  217. {
  218. comment = mNumEntriesStr + llformat(" %d", mCount);
  219. }
  220. else
  221. {
  222. comment = mNoEntryStr;
  223. }
  224. mPlacesListComment = mPlacesList->addCommentText(comment);
  225. }
  226. void HBFloaterTeleportHistory::removePlacesListComment()
  227. {
  228. if (mPlacesListComment)
  229. {
  230. mPlacesList->deleteItem(mPlacesListComment);
  231. mPlacesListComment = NULL;
  232. }
  233. }
  234. void HBFloaterTeleportHistory::populateLists(const LLSD& file_data)
  235. {
  236. // Clear all the data
  237. mPlacesList->clearRows();
  238. mFavoritesList->clearRows();
  239. mTPlist.clear();
  240. mCount = 0;
  241. std::map<std::string, HBTeleportLocation*> favorites;
  242. std::string column, value;
  243. std::string agent_home_parcel =
  244. gSavedPerAccountSettings.getString("AgentHomeParcel");
  245. bool has_type, has_parcel, has_region, has_position, has_timestamp;
  246. bool is_arrival;
  247. for (S32 idx = 0, entries = file_data.size(); idx < entries; ++idx)
  248. {
  249. const LLSD& data = file_data[idx];
  250. if (!data.has("id") || !data.has("columns"))
  251. {
  252. // Silently skip empty maps
  253. continue;
  254. }
  255. LLSD element;
  256. LLSD& columns = element["columns"];
  257. HBTeleportLocation* location = new HBTeleportLocation();
  258. // Let's validate the data and reject badly formatted entries.
  259. has_type = has_parcel = has_region = has_position = has_timestamp =
  260. is_arrival = false;
  261. for (S32 i = 0, count = data["columns"].size(); i < count; ++i)
  262. {
  263. const LLSD& map = data["columns"][i];
  264. if (!map.has("column") || !map.has("value"))
  265. {
  266. // Silently skip empty maps
  267. continue;
  268. }
  269. column = map["column"].asString();
  270. if (column == "type")
  271. {
  272. has_type = true;
  273. value = map["value"].asString();
  274. is_arrival = value == "A";
  275. columns[LIST_TYPE]["column"] = "type";
  276. columns[LIST_TYPE]["value"] = value;
  277. }
  278. else if (column == "parcel")
  279. {
  280. has_parcel = true;
  281. value = map["value"].asString();
  282. LLStringUtil::trim(value);
  283. location->mParcel = value;
  284. columns[LIST_PARCEL]["column"] = "parcel";
  285. columns[LIST_PARCEL]["value"] = value;
  286. }
  287. else if (column == "region")
  288. {
  289. has_region = true;
  290. value = map["value"].asString();
  291. location->mRegion = value;
  292. columns[LIST_REGION]["column"] = "region";
  293. columns[LIST_REGION]["value"] = value;
  294. }
  295. else if (column == "position")
  296. {
  297. has_position = true;
  298. value = map["value"].asString();
  299. location->mPosition = value;
  300. columns[LIST_POSITION]["column"] = "position";
  301. columns[LIST_POSITION]["value"] = value;
  302. }
  303. else if (column == "timestamp")
  304. {
  305. has_timestamp = true;
  306. value = map["value"].asString();
  307. columns[LIST_TIMESTAMP]["column"] = "timestamp";
  308. columns[LIST_TIMESTAMP]["value"] = value;
  309. }
  310. else
  311. {
  312. break;
  313. }
  314. }
  315. if (has_type && has_parcel && has_region && has_position &&
  316. has_timestamp)
  317. {
  318. // We have a valid element, add it to the list
  319. element["id"] = mCount++;
  320. mPlacesList->addElement(element, ADD_TOP);
  321. mTPlist.append(element);
  322. if (is_arrival)
  323. {
  324. value = location->mParcel + "|" + location->mRegion;
  325. if (value != agent_home_parcel)
  326. {
  327. std::map<std::string, HBTeleportLocation*>::iterator it =
  328. favorites.find(value);
  329. if (it != favorites.end())
  330. {
  331. ++it->second->mVisits;
  332. // Update the position to the one of the last visit
  333. it->second->mPosition = location->mPosition;
  334. }
  335. else
  336. {
  337. // Store the new favorite data
  338. favorites[value] = location;
  339. location = NULL; // Prevents data deletion
  340. }
  341. }
  342. }
  343. }
  344. if (location)
  345. {
  346. delete location;
  347. }
  348. }
  349. addPlacesListComment();
  350. std::string comment;
  351. if (!favorites.empty())
  352. {
  353. S32 min_visits = gSavedSettings.getU32("MinVisitsForFavorites");
  354. S32 count = 0;
  355. for (std::map<std::string, HBTeleportLocation*>::iterator
  356. it = favorites.begin(), end = favorites.end();
  357. it != end; ++it)
  358. {
  359. HBTeleportLocation* location = it->second;
  360. S32 visits = location->mVisits;
  361. if (visits >= min_visits)
  362. {
  363. LLSD element;
  364. element["id"] = count++;
  365. LLSD& columns = element["columns"];
  366. columns[FAV_PARCEL]["column"] = "parcel";
  367. columns[FAV_PARCEL]["value"] = location->mParcel;
  368. columns[FAV_REGION]["column"] = "region";
  369. columns[FAV_REGION]["value"] = location->mRegion;
  370. columns[FAV_POSITION]["column"] = "position";
  371. columns[FAV_POSITION]["value"] = location->mPosition;
  372. columns[FAV_VISITS]["column"] = "visits";
  373. columns[FAV_VISITS]["value"] = visits;
  374. mFavoritesList->addElement(element);
  375. }
  376. delete location;
  377. }
  378. comment = mNumEntriesStr + llformat(" %d", count);
  379. }
  380. else
  381. {
  382. comment = mNoEntryStr;
  383. }
  384. // Sort favorites by visits in decreasing order
  385. mFavoritesList->sortByColumnIndex(FAV_VISITS, false);
  386. mFavoritesList->addCommentText(comment);
  387. updateSearchResults();
  388. setButtonsStatus();
  389. }
  390. void HBFloaterTeleportHistory::updateSearchResults()
  391. {
  392. mResultsList->clearRows();
  393. if (mSearchString.length() < 3)
  394. {
  395. return;
  396. }
  397. std::set<std::string> places;
  398. std::string name;
  399. std::vector<LLScrollListItem*> data = mPlacesList->getAllData();
  400. S32 results = 0;
  401. for (S32 i = 0, count = data.size(); i < count; ++i)
  402. {
  403. LLScrollListItem* item = data[i];
  404. // Only take arrival places into account, and illiminate the comment
  405. // line too...
  406. if (item->getColumn(LIST_TYPE)->getValue().asString() != "A")
  407. {
  408. continue;
  409. }
  410. // Concatenate the parcel and region names, lower-cased.
  411. name = item->getColumn(LIST_PARCEL)->getValue().asString() + "|";
  412. name += item->getColumn(LIST_REGION)->getValue().asString();
  413. LLStringUtil::toLower(name);
  414. // If not matching the search pattern or already listed once, skip it
  415. if (name.find(mSearchString) == std::string::npos ||
  416. places.count(name))
  417. {
  418. continue;
  419. }
  420. // Remember we listed this location already.
  421. places.emplace(name);
  422. LLSD element;
  423. // Same Id as in the places list, for easy selection (see
  424. // onPlacesSelected())
  425. element["id"] = item->getValue();
  426. // Copy the data we need
  427. LLSD& columns = element["columns"];
  428. columns[RES_PARCEL]["column"] = "parcel";
  429. columns[RES_PARCEL]["value"] =
  430. item->getColumn(LIST_PARCEL)->getValue();
  431. columns[RES_REGION]["column"] = "region";
  432. columns[RES_REGION]["value"] =
  433. item->getColumn(LIST_REGION)->getValue();
  434. columns[RES_POSITION]["column"] = "position";
  435. columns[RES_POSITION]["value"] =
  436. item->getColumn(LIST_POSITION)->getValue();
  437. mResultsList->addElement(element);
  438. ++results;
  439. }
  440. std::string comment;
  441. if (results > 0)
  442. {
  443. comment = mNumEntriesStr + llformat(" %d", results);
  444. }
  445. else
  446. {
  447. comment = mNoEntryStr;
  448. }
  449. mResultsList->addCommentText(comment);
  450. }
  451. void HBFloaterTeleportHistory::loadEntries()
  452. {
  453. std::string filename = getHistoryFileName(true);
  454. if (filename.empty())
  455. {
  456. llwarns << "Could not access the teleport history file. History not loaded."
  457. << llendl;
  458. return;
  459. }
  460. llifstream file(filename.c_str());
  461. if (file.is_open())
  462. {
  463. LLSD data;
  464. llinfos << "Loading the teleport history from: " << filename << llendl;
  465. LLSDSerialize::fromXML(data, file);
  466. file.close();
  467. populateLists(data);
  468. // Save our validated data
  469. saveList();
  470. }
  471. else
  472. {
  473. llwarns << "Could not open the teleport history file. History not loaded."
  474. << llendl;
  475. }
  476. }
  477. void HBFloaterTeleportHistory::saveList()
  478. {
  479. std::string filename = getHistoryFileName();
  480. if (filename.empty())
  481. {
  482. llwarns_sparse << "Could not access the teleport history file. History not saved."
  483. << llendl;
  484. return;
  485. }
  486. llofstream file(filename.c_str());
  487. if (file.is_open())
  488. {
  489. llinfos << "Saving the teleport history to: " << filename << llendl;
  490. LLSDSerialize::toPrettyXML(mTPlist, file);
  491. file.close();
  492. }
  493. else
  494. {
  495. llwarns << "Could not open file '" << filename << "' for writing."
  496. << llendl;
  497. }
  498. }
  499. void HBFloaterTeleportHistory::addPendingEntry(const std::string& region_name,
  500. LLVector3 pos)
  501. {
  502. //MK
  503. if (gRLenabled && gRLInterface.mContainsShowloc)
  504. {
  505. mPendingRegionName.clear();
  506. return;
  507. }
  508. //mk
  509. // Set pending entry timestamp
  510. mPendingTimeString = get_timestamp();
  511. // Set pending region name
  512. mPendingRegionName = region_name;
  513. // Set pending position
  514. if (isAgentAvatarValid())
  515. {
  516. // The actual Z coordinate of the TP is at the agent's feet
  517. pos.mV[VZ] -= 0.5f * (gAgentAvatarp->mBodySize.mV[VZ] +
  518. gAgentAvatarp->mAvatarOffset.mV[VZ]);
  519. }
  520. mPendingPosition = llformat("%d, %d, %d", (S32)pos.mV[VX], (S32)pos.mV[VY],
  521. (S32)pos.mV[VZ]);
  522. }
  523. void HBFloaterTeleportHistory::addSourceEntry(const std::string& source_slurl,
  524. const std::string& parcel_name)
  525. {
  526. //MK
  527. if (gRLenabled && gRLInterface.mContainsShowloc)
  528. {
  529. mPendingRegionName.clear();
  530. return;
  531. }
  532. //mk
  533. LLSLURL slurl(source_slurl);
  534. if (slurl.getType() != LLSLURL::LOCATION)
  535. {
  536. llwarns << "Could not parse the source SLURL (" << source_slurl
  537. << "): TP history entry not added" << llendl;
  538. return;
  539. }
  540. // Extract the region name
  541. mPendingRegionName = slurl.getRegion();
  542. // Set pending position
  543. LLVector3 pos = slurl.getPosition();
  544. mPendingPosition = llformat("%d, %d, %d", (S32)pos.mV[VX], (S32)pos.mV[VY],
  545. (S32)pos.mV[VZ]);
  546. // Set pending entry timestamp
  547. mPendingTimeString = get_timestamp();
  548. // Add this pending entry immediately, using the passed (departure) parcel
  549. // name.
  550. addEntry(parcel_name, true);
  551. }
  552. void HBFloaterTeleportHistory::addEntry(std::string parcel_name,
  553. bool departure)
  554. {
  555. if (mPendingRegionName.empty())
  556. {
  557. return;
  558. }
  559. // Build the list entry
  560. LLSD element;
  561. element["id"] = mCount++;
  562. LLSD& columns = element["columns"];
  563. columns[LIST_TYPE]["column"] = "type";
  564. columns[LIST_TYPE]["value"] = departure ? "D" : "A";
  565. columns[LIST_PARCEL]["column"] = "parcel";
  566. LLStringUtil::trim(parcel_name);
  567. columns[LIST_PARCEL]["value"] = parcel_name;
  568. columns[LIST_REGION]["column"] = "region";
  569. columns[LIST_REGION]["value"] = mPendingRegionName;
  570. columns[LIST_POSITION]["column"] = "position";
  571. columns[LIST_POSITION]["value"] = mPendingPosition;
  572. columns[LIST_TIMESTAMP]["column"] = "timestamp";
  573. columns[LIST_TIMESTAMP]["value"] = mPendingTimeString;
  574. // Add the new list entry on top of the list, deselect all and disable the
  575. // buttons
  576. mPlacesList->addElement(element, ADD_TOP);
  577. mPlacesList->deselectAllItems(true);
  578. setButtonsStatus();
  579. // Update the number of entries line
  580. addPlacesListComment();
  581. // Save the entry in the history file
  582. mTPlist.append(element);
  583. saveList();
  584. mPendingRegionName.clear();
  585. }
  586. //static
  587. void HBFloaterTeleportHistory::onTeleportArriving()
  588. {
  589. if (gFloaterTeleportHistoryp && !gFloaterTeleportHistoryp->isMinimized() &&
  590. gSavedSettings.getBool("HideFloatersOnTPSuccess"))
  591. {
  592. gFloaterTeleportHistoryp->setVisible(false);
  593. }
  594. }
  595. //static
  596. void HBFloaterTeleportHistory::onTeleportFinished(const LLVector3d& pos,
  597. bool local)
  598. {
  599. if (!gFloaterTeleportHistoryp) return;
  600. if (local)
  601. {
  602. // Do not register local teleports
  603. gFloaterTeleportHistoryp->mPendingRegionName.clear();
  604. return;
  605. }
  606. gFloaterTeleportHistoryp->addEntry(gViewerParcelMgr.getAgentParcelName());
  607. }
  608. //static
  609. void HBFloaterTeleportHistory::onTeleportFailed()
  610. {
  611. if (gFloaterTeleportHistoryp)
  612. {
  613. gFloaterTeleportHistoryp->mPendingRegionName.clear();
  614. }
  615. }
  616. //virtual
  617. void HBFloaterTeleportHistory::onOpen()
  618. {
  619. if (mFirstOpen)
  620. {
  621. mFirstOpen = false;
  622. // Reposition floater from saved settings
  623. LLRect rect = gSavedSettings.getRect("FloaterTeleportHistoryRect");
  624. reshape(rect.getWidth(), rect.getHeight(), false);
  625. setRect(rect);
  626. mTabContainer->selectTab(gSavedSettings.getS32("LastTPHistoryTab"));
  627. }
  628. }
  629. //virtual
  630. void HBFloaterTeleportHistory::onClose(bool app_quitting)
  631. {
  632. LLFloater::setVisible(false);
  633. }
  634. //virtual
  635. bool HBFloaterTeleportHistory::canClose()
  636. {
  637. return !LLApp::isExiting();
  638. }
  639. void HBFloaterTeleportHistory::toggle()
  640. {
  641. if (getVisible())
  642. {
  643. setVisible(false);
  644. }
  645. else
  646. {
  647. open();
  648. }
  649. }
  650. bool HBFloaterTeleportHistory::getSelectedLocation(std::string& region,
  651. LLVector3& pos)
  652. {
  653. LLScrollListCtrl* list = NULL;
  654. S32 col_region = 0;
  655. S32 col_pos = 0;
  656. S32 active_tab = mTabContainer->getCurrentPanelIndex();
  657. switch (active_tab)
  658. {
  659. case 0:
  660. list = mPlacesList;
  661. col_region = LIST_REGION;
  662. col_pos = LIST_POSITION;
  663. break;
  664. case 1:
  665. list = mFavoritesList;
  666. col_region = FAV_REGION;
  667. col_pos = FAV_POSITION;
  668. break;
  669. case 2:
  670. list = mResultsList;
  671. col_region = RES_REGION;
  672. col_pos = RES_POSITION;
  673. break;
  674. default:
  675. llwarns << "Unknown tab !" << llendl;
  676. llassert(false);
  677. return false;
  678. }
  679. LLScrollListItem* itemp = list->getFirstSelected();
  680. if (!itemp)
  681. {
  682. return false;
  683. }
  684. region = itemp->getColumn(col_region)->getValue().asString();
  685. std::string pos_str = itemp->getColumn(col_pos)->getValue().asString();
  686. size_t i = pos_str.find(',');
  687. if (i != std::string::npos)
  688. {
  689. S32 x = atoi(pos_str.substr(0, i++).c_str());
  690. S32 y = atoi(pos_str.substr(i).c_str());
  691. S32 z = 0;
  692. i = pos_str.find(',', i);
  693. if (i != std::string::npos)
  694. {
  695. z = atoi(pos_str.substr(i + 1).c_str());
  696. }
  697. pos = LLVector3(x, y, z);
  698. }
  699. return true;
  700. }
  701. // Callbacks
  702. //static
  703. void HBFloaterTeleportHistory::onTabChanged(void* data, bool)
  704. {
  705. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  706. if (self && self->mTabContainer) // Paranoia
  707. {
  708. gSavedSettings.setS32("LastTPHistoryTab",
  709. self->mTabContainer->getCurrentPanelIndex());
  710. self->setButtonsStatus();
  711. }
  712. }
  713. //static
  714. void HBFloaterTeleportHistory::onPlacesSelected(LLUICtrl* ctrl, void* data)
  715. {
  716. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  717. if (self && ctrl)
  718. {
  719. // On selection change check if we need to enable or disable buttons
  720. self->setButtonsStatus();
  721. // When selecting an item in the search results, select the
  722. // corresponding item in the history list.
  723. LLScrollListCtrl* list = (LLScrollListCtrl*)ctrl;
  724. if (list == self->mResultsList)
  725. {
  726. LLScrollListItem* item = list->getFirstSelected();
  727. if (item)
  728. {
  729. self->mPlacesList->selectByValue(item->getValue());
  730. self->mPlacesList->scrollToShowSelected();
  731. }
  732. }
  733. }
  734. }
  735. //static
  736. void HBFloaterTeleportHistory::onButtonClose(void* data)
  737. {
  738. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  739. if (self)
  740. {
  741. self->close();
  742. }
  743. }
  744. //static
  745. void HBFloaterTeleportHistory::onRefresh(void* data)
  746. {
  747. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  748. if (self)
  749. {
  750. LLSD data = self->mTPlist;
  751. self->populateLists(data);
  752. }
  753. }
  754. //static
  755. void HBFloaterTeleportHistory::onTeleport(void* data)
  756. {
  757. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  758. if (!self || gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
  759. {
  760. return;
  761. }
  762. std::string region;
  763. LLVector3 pos;
  764. if (!self->getSelectedLocation(region, pos))
  765. {
  766. return;
  767. }
  768. // Build the position SLURL for the TP destination
  769. LLSLURL slurl(region, pos);
  770. // Build the app SLURL for instant teleport to destination
  771. std::string app_slurl = LLGridManager::getInstance()->getAppSLURLBase();
  772. app_slurl += "/teleport/" + slurl.getLocationString();
  773. LL_DEBUGS("Teleport") << "Teleport SLURL: " << app_slurl << LL_ENDL;
  774. // Dispatch it
  775. LLMediaCtrl* web = NULL;
  776. LLURLDispatcher::dispatch(app_slurl, "clicked", web, true);
  777. }
  778. //static
  779. void HBFloaterTeleportHistory::onShowOnMap(void* data)
  780. {
  781. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  782. if (!self) return;
  783. std::string region;
  784. LLVector3 pos;
  785. if (!self->getSelectedLocation(region, pos))
  786. {
  787. return;
  788. }
  789. // Point world map at position
  790. gFloaterWorldMapp->trackURL(region, pos.mV[VX], pos.mV[VY], pos.mV[VZ]);
  791. LLFloaterWorldMap::show(NULL, true);
  792. }
  793. // Gets the SLURL of the selected entry and copy it to the clipboard
  794. //static
  795. void HBFloaterTeleportHistory::onCopySLURL(void* data)
  796. {
  797. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  798. if (!self) return;
  799. std::string region;
  800. LLVector3 pos;
  801. if (!self->getSelectedLocation(region, pos))
  802. {
  803. return;
  804. }
  805. LLSLURL slurl(region, pos);
  806. gWindowp->copyTextToClipboard(utf8str_to_wstring(slurl.getSLURLString()));
  807. }
  808. //static
  809. void HBFloaterTeleportHistory::onRemove(LLUICtrl* ctrl, void* data)
  810. {
  811. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  812. if (!self || !ctrl) return;
  813. std::string operation = ctrl->getValue().asString();
  814. if (operation == "remove_all")
  815. {
  816. self->mPlacesList->clearRows();
  817. self->mTPlist.clear();
  818. }
  819. else
  820. {
  821. LLScrollListItem* itemp = self->mPlacesList->getFirstSelected();
  822. if (!itemp) return;
  823. if (operation == "remove_older" || operation == "remove_newer")
  824. {
  825. self->removePlacesListComment();
  826. std::string match =
  827. itemp->getColumn(LIST_TIMESTAMP)->getValue().asString();
  828. std::vector<LLScrollListItem*> items =
  829. self->mPlacesList->getAllData();
  830. std::string date;
  831. bool newer = operation == "remove_newer";
  832. for (S32 i = 0, count = items.size(); i < count; ++i)
  833. {
  834. itemp = items[i];
  835. date = itemp->getColumn(LIST_TIMESTAMP)->getValue().asString();
  836. S32 result = date.compare(match);
  837. if ((newer && result > 0) || (!newer && result < 0))
  838. {
  839. S32 number = itemp->getValue().asInteger();
  840. self->mTPlist[number] = LLSD::emptyArray();
  841. }
  842. }
  843. self->saveList();
  844. self->loadEntries();
  845. }
  846. else if (operation == "remove_parcel")
  847. {
  848. self->removePlacesListComment();
  849. std::string match =
  850. itemp->getColumn(LIST_PARCEL)->getValue().asString();
  851. LLStringUtil::trim(match);
  852. LLStringUtil::toLower(match);
  853. std::vector<LLScrollListItem*> items =
  854. self->mPlacesList->getAllData();
  855. std::string name;
  856. for (S32 i = 0, count = items.size(); i < count; ++i)
  857. {
  858. itemp = items[i];
  859. name = itemp->getColumn(LIST_PARCEL)->getValue().asString();
  860. LLStringUtil::trim(name);
  861. LLStringUtil::toLower(name);
  862. if (name == match)
  863. {
  864. S32 number = itemp->getValue().asInteger();
  865. self->mTPlist[number] = LLSD::emptyArray();
  866. }
  867. }
  868. self->saveList();
  869. self->loadEntries();
  870. }
  871. else if (operation == "remove_region")
  872. {
  873. self->removePlacesListComment();
  874. std::string match =
  875. itemp->getColumn(LIST_REGION)->getValue().asString();
  876. std::vector<LLScrollListItem*> items =
  877. self->mPlacesList->getAllData();
  878. std::string name;
  879. for (S32 i = 0, count = items.size(); i < count; ++i)
  880. {
  881. itemp = items[i];
  882. name = itemp->getColumn(LIST_REGION)->getValue().asString();
  883. if (name == match)
  884. {
  885. S32 number = itemp->getValue().asInteger();
  886. self->mTPlist[number] = LLSD::emptyArray();
  887. }
  888. }
  889. self->saveList();
  890. self->loadEntries();
  891. }
  892. else // "remove_entry" in pull-down list or direct click on the button
  893. {
  894. S32 number = itemp->getValue().asInteger();
  895. self->mPlacesList->deleteItem(itemp);
  896. // NOTE: it is important *not* to use erase() here: the entry must
  897. // be kept in the LLSD so that mCount and append() stay in sync
  898. // (and the TP numbering stays in order). The xml file and LLSD
  899. // will be cleaned up from the empty arrays on next loadEntries()
  900. // call.
  901. self->mTPlist[number] = LLSD::emptyArray();
  902. self->saveList();
  903. }
  904. }
  905. self->setButtonsStatus();
  906. }
  907. //static
  908. void HBFloaterTeleportHistory::onSearchEdit(const std::string& search_string,
  909. void* data)
  910. {
  911. HBFloaterTeleportHistory* self = (HBFloaterTeleportHistory*)data;
  912. if (self)
  913. {
  914. self->mSearchString = search_string;
  915. LLStringUtil::toLower(self->mSearchString);
  916. self->updateSearchResults();
  917. }
  918. }