hbfloaterrlv.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  1. /**
  2. * @file hbfloaterrlv.cpp
  3. * @brief The HBFloaterRLV and HBFloaterBlacklistRLV classes definitions
  4. *
  5. * $LicenseInfo:firstyear=2011&license=viewergpl$
  6. *
  7. * Copyright (c) 2011-2020, Henri Beauchamp
  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. #include "llviewerprecompiledheaders.h"
  33. #include "hbfloaterrlv.h"
  34. #include "llbutton.h"
  35. #include "llcachename.h"
  36. #include "llcheckboxctrl.h"
  37. #include "hbfastmap.h"
  38. #include "llinventory.h"
  39. #include "llnotifications.h"
  40. #include "llscrolllistctrl.h"
  41. #include "lltabcontainer.h"
  42. #include "lluictrlfactory.h"
  43. #include "llagent.h"
  44. #include "mkrlinterface.h"
  45. #include "hbviewerautomation.h"
  46. #include "llviewercontrol.h"
  47. #include "llviewerobjectlist.h"
  48. #include "llviewerwindow.h" // For gViewerWindowp
  49. #include "llvoavatar.h"
  50. // This is used as a cache for the names of objects emitting RestrainedLove
  51. // commands. The name is captured from the log or from the inventory for
  52. // attachments. We keep this cache for the duration of the viewer session since
  53. // it will stay quite small.
  54. typedef fast_hmap<LLUUID, std::string> cached_names_map_t;
  55. static cached_names_map_t sCachedNamesMap;
  56. ///////////////////////////////////////////////////////////////////////////////
  57. // Helper functions
  58. ///////////////////////////////////////////////////////////////////////////////
  59. static std::string get_object_name(const LLUUID& id, bool& is_lua, bool& is_gone,
  60. bool& is_root)
  61. {
  62. is_lua = is_gone = is_root = false;
  63. #if LL_LINUX
  64. // If it bears a the fake UUID used for Lua D-Bus, then the restrictions
  65. // were set via Lua D-Bus scripting. Note: we check for id.notNull() since
  66. // it may happen that no D-Bus command was sent just yet, in which case
  67. // sLuaDBusFakeObjectId would still be a null UUID.
  68. if (id.notNull() && id == HBViewerAutomation::sLuaDBusFakeObjectId)
  69. {
  70. is_lua = true;
  71. return "Lua D-Bus";
  72. }
  73. #endif
  74. if (id == gAgentID)
  75. {
  76. // If it bears our avatar UUID, then the restrictions were set via Lua
  77. // scripting.
  78. is_lua = true;
  79. return "Lua script";
  80. }
  81. LLViewerObject* objectp = gObjectList.findObject(id);
  82. if (!objectp)
  83. {
  84. // Object is gone (detached/derezzed) !
  85. is_gone = true;
  86. // Let's see if we have its name cached...
  87. cached_names_map_t::iterator it = sCachedNamesMap.find(id);
  88. if (it != sCachedNamesMap.end())
  89. {
  90. return it->second;
  91. }
  92. return id.asString();
  93. }
  94. if (objectp == objectp->getRootEdit())
  95. {
  96. // Root primitive
  97. is_root = true;
  98. }
  99. std::string name;
  100. LLInventoryItem* itemp = gRLInterface.getItem(id);
  101. if (itemp)
  102. {
  103. name = itemp->getName();
  104. }
  105. // If the name is empty, either this is an attachement that renamed itself
  106. // with an empty name, or this is not an attachament but an in-world object
  107. // (owned by us)... which should have used a relay...
  108. if (name.empty())
  109. {
  110. // Let's see if we have its name cached...
  111. cached_names_map_t::iterator it = sCachedNamesMap.find(id);
  112. if (it != sCachedNamesMap.end())
  113. {
  114. name = it->second;
  115. }
  116. else
  117. {
  118. name = id.asString();
  119. }
  120. }
  121. else
  122. {
  123. // Cache the name for this object, in case it gets derezzed (or renamed
  124. // to an empty name) later...
  125. sCachedNamesMap.emplace(id, name);
  126. }
  127. return name;
  128. }
  129. // This helper function is responsible for decorating object names with the
  130. // proper font face and color.
  131. static std::string get_name_decoration(bool is_lua, bool is_gone,
  132. bool is_root, LLColor4& color)
  133. {
  134. if (is_lua)
  135. {
  136. color = LLColor4::green3.getValue();
  137. return "NORMAL";
  138. }
  139. if (is_gone)
  140. {
  141. color = LLColor4::red2.getValue();
  142. }
  143. if (is_root)
  144. {
  145. return "BOLD";
  146. }
  147. return "NORMAL";
  148. }
  149. // Helper to setup the LLSD for an object status list element.
  150. static void set_status(LLSD& element, const LLUUID& id,
  151. const std::string& text)
  152. {
  153. LLSD& columns = element["columns"];
  154. columns[0]["column"] = "object_name";
  155. columns[0]["font"] = "SANSSERIF_SMALL";
  156. bool is_lua, is_gone, is_root;
  157. columns[0]["value"] = get_object_name(id, is_lua, is_gone, is_root);
  158. LLColor4 color; // Set to black by default on construction
  159. columns[0]["font-style"] = get_name_decoration(is_lua, is_gone, is_root,
  160. color);
  161. if (color != LLColor4::black) // If black, use the default skin color
  162. {
  163. columns[0]["color"] = color.getValue();
  164. }
  165. columns[1]["column"] = "commands";
  166. columns[1]["font"] = "SANSSERIF_SMALL";
  167. columns[1]["value"] = text;
  168. }
  169. // Helper to resolve potential UUIDs into attachment/avatar/group names in RLV
  170. // exceptions target.
  171. static void resolve_name(std::string& text)
  172. {
  173. if (text.size() != 36)
  174. {
  175. // Not an Id, do not bother
  176. return;
  177. }
  178. LLUUID id(text, false);
  179. if (id.isNull())
  180. {
  181. // Not a valid Id either.
  182. return;
  183. }
  184. // Perhaps an attachment...
  185. LLInventoryItem* itemp = gRLInterface.getItem(id);
  186. if (itemp)
  187. {
  188. text = itemp->getName();
  189. return;
  190. }
  191. // Note: we do not bother with gCacheNamep callbacks and asynchronous UUID
  192. // to name (time consuming) replacements in the scroll lists, because the
  193. // queries for missing names will be sent to the server anyway, and the
  194. // result will have arrived next time we rebuild the list (on next RLV
  195. // command processing or by using the "Refresh" button)...
  196. // Perhaps a group... Note: it is important NOT to query for a group name
  197. // when the Id is not one of a group, because then the following avatar
  198. // name request with the same Id would not get properly queued ! That is
  199. // why we restrict the search to groups to which the agent does belong
  200. // (they would not be able to use other groups anyway).
  201. std::string name;
  202. if (gAgent.isInGroup(id))
  203. {
  204. if (gCacheNamep && gCacheNamep->getGroupName(id, name))
  205. {
  206. text = name;
  207. }
  208. return;
  209. }
  210. if (gRLenabled && gRLInterface.mContainsShownames)
  211. {
  212. return;
  213. }
  214. // Perhaps an avatar currently around...
  215. LLVOAvatar* avatarp = gObjectList.findAvatar(id);
  216. if (avatarp)
  217. {
  218. text = avatarp->getFullname(true);
  219. return;
  220. }
  221. // Perhaps an offline or far avatar...
  222. if (gCacheNamep && gCacheNamep->getFullName(id, name))
  223. {
  224. text = name;
  225. }
  226. }
  227. // Helper function to maintain and update a set of 'restrictions' and a map of
  228. // 'exceptions'. NOTE: 'cmd' (which is the input parameter) may also get
  229. // modified by this function.
  230. typedef std::set<std::string> cmd_list_t;
  231. typedef std::map<std::string, std::string> except_map_t;
  232. static void parse_command(std::string& cmd, cmd_list_t& restrictions,
  233. except_map_t& exceptions)
  234. {
  235. // Note: this list can be found by grep'ing the sources for
  236. // containsWithoutException
  237. static const cmd_list_t exception_types =
  238. {
  239. "edit",
  240. "recvchat",
  241. "recvemote",
  242. "recvim",
  243. "sendchannel",
  244. "sendim",
  245. "share",
  246. "shownames",
  247. "startim",
  248. "touchhud",
  249. "touchworld",
  250. "tplure",
  251. "tprequest"
  252. };
  253. if (cmd.compare(0, 6, "notify") == 0)
  254. {
  255. return; // Ignore notification commands
  256. }
  257. // Special exception/relaxation, applying to the restricted agent.
  258. if (cmd == "emote")
  259. {
  260. if (!restrictions.count(cmd))
  261. {
  262. restrictions.emplace(cmd);
  263. exceptions[cmd] = HBFloaterRLV::sUnrestrictedEmotes;
  264. }
  265. return;
  266. }
  267. // Check to see if the command is another type of exception...
  268. size_t i = cmd.find(':');
  269. if (i != std::string::npos && i < cmd.size() - 1)
  270. {
  271. std::string restriction = cmd.substr(0, i);
  272. // Account *_sec variants exceptions in the same category as their
  273. // non-*_sec variant; this is correct (even if not docummented)
  274. // and corresponds exactly with what containsWithoutException() is
  275. // doing with exceptions...
  276. size_t j = restriction.rfind("_sec");
  277. if (j != std::string::npos && j == restriction.size() - 4)
  278. {
  279. restriction.erase(j);
  280. }
  281. cmd_list_t::const_iterator it = exception_types.find(restriction);
  282. if (it != exception_types.end())
  283. {
  284. // We have an exception !
  285. std::string exception = cmd.substr(i + 1);
  286. resolve_name(exception); // Turn UUID into name if applicable
  287. except_map_t::iterator iter = exceptions.find(restriction);
  288. if (iter == exceptions.end())
  289. {
  290. // New exception for this type
  291. exceptions[restriction] = exception;
  292. }
  293. else
  294. {
  295. // Add exception to existing exception(s) for this type, if not
  296. // already there.
  297. exception = "," + exception;
  298. std::string existing = "," + iter->second + ",";
  299. if (existing.find(exception + ",") == std::string::npos)
  300. {
  301. iter->second += exception;
  302. }
  303. }
  304. return;
  305. }
  306. }
  307. // Account *_sec variants restrictions in the same category as their
  308. // non-*_sec variant. This is a simplification of how RestrainedLove
  309. // deals with *_sec restrictions (since those only accept exceptions set
  310. // from the same object). *TODO: fix that, perhaps ?...
  311. i = cmd.rfind("_sec");
  312. if (i != std::string::npos && i == cmd.size() - 4)
  313. {
  314. cmd.erase(i);
  315. }
  316. // It is not a notification or an exception, so it must be a restriction
  317. if (!restrictions.count(cmd))
  318. {
  319. restrictions.emplace(cmd);
  320. }
  321. }
  322. // Helper to setup the LLSD for a log list element.
  323. static void set_log_line(LLSD& element,
  324. const HBFloaterRLV::LoggedCommand& log_line)
  325. {
  326. LLSD& columns = element["columns"];
  327. columns[0]["column"] = "time_stamp";
  328. columns[0]["font"] = "SANSSERIF_SMALL";
  329. columns[0]["value"] = log_line.mTimeStamp;
  330. columns[1]["column"] = "object_name";
  331. columns[1]["font"] = "SANSSERIF_SMALL";
  332. columns[1]["value"] = log_line.mName;
  333. LLColor4 color; // Set to black by default on construction
  334. columns[1]["font-style"] = get_name_decoration(log_line.mIsLua,
  335. log_line.mIsGone,
  336. log_line.mIsRoot, color);
  337. if (color != LLColor4::black) // If black, use the default skin color
  338. {
  339. columns[1]["color"] = color.getValue();
  340. }
  341. columns[2]["column"] = "status";
  342. columns[2]["font"] = "SANSSERIF_SMALL";
  343. switch (log_line.mStatus)
  344. {
  345. case HBFloaterRLV::QUEUED:
  346. columns[2]["value"] = HBFloaterRLV::sQueued;
  347. columns[2]["color"] = LLColor4::blue.getValue();
  348. break;
  349. case HBFloaterRLV::FAILED:
  350. columns[2]["value"] = HBFloaterRLV::sFailed;
  351. columns[2]["color"] = LLColor4::red2.getValue();
  352. break;
  353. case HBFloaterRLV::BLACKLISTED:
  354. columns[2]["value"] = HBFloaterRLV::sBlacklisted;
  355. columns[2]["font-style"] = "BOLD";
  356. break;
  357. case HBFloaterRLV::IMPLICIT:
  358. columns[2]["value"] = HBFloaterRLV::sImplicit;
  359. columns[2]["color"] = LLColor4::green3.getValue();
  360. break;
  361. default:
  362. columns[2]["value"] = HBFloaterRLV::sExecuted;
  363. columns[2]["color"] = LLColor4::green3.getValue();
  364. }
  365. columns[3]["column"] = "command";
  366. columns[3]["font"] = "SANSSERIF_SMALL";
  367. columns[3]["value"] = log_line.mCommand;
  368. }
  369. ///////////////////////////////////////////////////////////////////////////////
  370. // HBFloaterRLV class
  371. ///////////////////////////////////////////////////////////////////////////////
  372. //static
  373. std::vector<HBFloaterRLV::LoggedCommand> HBFloaterRLV::sLoggedCommands;
  374. std::string HBFloaterRLV::sQueued;
  375. std::string HBFloaterRLV::sFailed;
  376. std::string HBFloaterRLV::sExecuted;
  377. std::string HBFloaterRLV::sBlacklisted;
  378. std::string HBFloaterRLV::sImplicit;
  379. std::string HBFloaterRLV::sUnrestrictedEmotes;
  380. // Sub-class used to register commands in the log
  381. HBFloaterRLV::LoggedCommand::LoggedCommand(const LLUUID& id,
  382. const std::string& name,
  383. const std::string& cmd, S32 status)
  384. : mName(name),
  385. mCommand(cmd),
  386. mStatus(status)
  387. {
  388. // Note: we register the data at the moment the command is logged because
  389. // the object could disappear or be renamed later on. We do not store the
  390. // Id either (excepted as a name for missing objects or objects with empty
  391. // names), since in the case of an attachment, it could get modified via
  392. // auto-reattaching when kicked off; see the RLInterface::replace() method
  393. // which is used by the LLViewerJointAttachment::addObject() method.
  394. std::string cur_name = get_object_name(id, mIsLua, mIsGone, mIsRoot);
  395. // Give priority to the name transmitted via the llOwnerSay() chat message,
  396. // but if empty, use the name we found with get_object_name() which is, at
  397. // worst, the object UUID...
  398. if (mName.empty())
  399. {
  400. mName = cur_name;
  401. }
  402. else if (!mIsLua)
  403. {
  404. // Cache the object name when we have one.
  405. sCachedNamesMap.emplace(id, mName);
  406. }
  407. time_t utc_time = time_corrected();
  408. struct tm* timep = utc_time_to_tm(utc_time);
  409. // Make it easy to sort: use the Year-Month-Day HH:MM:SS ISO convention
  410. timeStructToFormattedString(timep, "%Y-%m-%d %H:%M:%S", mTimeStamp);
  411. // Special, internal command meaning: end of @relayed commands block.
  412. if (cmd == " ")
  413. {
  414. mCommand = "end-relayed";
  415. if (status == EXECUTED)
  416. {
  417. mStatus = IMPLICIT;
  418. }
  419. }
  420. }
  421. HBFloaterRLV::HBFloaterRLV(const LLSD&)
  422. : mIsDirty(false),
  423. mFirstOpen(true),
  424. mLastCommandsLogSize(0)
  425. {
  426. LLUICtrlFactory::getInstance()->buildFloater(this,
  427. "floater_rlv_status.xml");
  428. if (sQueued.empty())
  429. {
  430. sQueued = getString("queued");
  431. sFailed = getString("failed");
  432. sExecuted = getString("executed");
  433. sBlacklisted = getString("blacklisted");
  434. sImplicit = getString("implicit");
  435. sUnrestrictedEmotes = getString("unrestricted_emote");
  436. }
  437. }
  438. //virtual
  439. bool HBFloaterRLV::postBuild()
  440. {
  441. mTabContainer = getChild<LLTabContainer>("tabs");
  442. LLPanel* tab = mTabContainer->getChild<LLPanel>("status");
  443. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  444. mTabContainer->setTabUserData(tab, this);
  445. tab = mTabContainer->getChild<LLPanel>("restrictions");
  446. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  447. mTabContainer->setTabUserData(tab, this);
  448. tab = mTabContainer->getChild<LLPanel>("commands_log");
  449. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  450. mTabContainer->setTabUserData(tab, this);
  451. mStatusByObject = getChild<LLScrollListCtrl>("status_list");
  452. mStatusByObject->setDoubleClickCallback(onDoubleClick);
  453. mStatusByObject->setCallbackUserData(this);
  454. mRestrictions = getChild<LLScrollListCtrl>("restrictions_list");
  455. mCommandsLog = getChild<LLScrollListCtrl>("commands_list");
  456. childSetAction("help", onButtonHelp, this);
  457. mRefreshButton = getChild<LLButton>("refresh_btn");
  458. mRefreshButton->setClickedCallback(onButtonRefresh, this);
  459. mClearButton = getChild<LLButton>("clear_btn");
  460. mClearButton->setClickedCallback(onButtonClear, this);
  461. childSetAction("close_btn", onButtonClose, this);
  462. setButtonsStatus();
  463. mIsDirty = true;
  464. return true;
  465. }
  466. //virtual
  467. void HBFloaterRLV::onOpen()
  468. {
  469. if (mFirstOpen)
  470. {
  471. mFirstOpen = false;
  472. mTabContainer->selectTab(gSavedSettings.getS32("LastRLVFloaterTab"));
  473. }
  474. }
  475. //virtual
  476. void HBFloaterRLV::draw()
  477. {
  478. if (gRLenabled && gRLInterface.mContainsViewscript)
  479. {
  480. close();
  481. return;
  482. }
  483. if (mIsDirty)
  484. {
  485. S32 scrollpos1 = mStatusByObject->getScrollPos();
  486. S32 scrollpos2 = mRestrictions->getScrollPos();
  487. // It is faster to rebuild fully these lists than trying to figure out
  488. // what changed in them...
  489. mStatusByObject->deleteAllItems();
  490. mRestrictions->deleteAllItems();
  491. if (!gRLInterface.mSpecialObjectBehaviours.empty())
  492. {
  493. cmd_list_t restrictions;
  494. except_map_t exceptions;
  495. LLUUID id, old_id;
  496. std::string commands, cmd;
  497. bool first = true;
  498. rl_map_it_t it = gRLInterface.mSpecialObjectBehaviours.begin();
  499. while (it != gRLInterface.mSpecialObjectBehaviours.end())
  500. {
  501. // We concatenate all commands pertaining to the same object
  502. old_id = id;
  503. id = LLUUID(it->first);
  504. if (!first && id != old_id)
  505. {
  506. LLSD element;
  507. set_status(element, old_id, commands);
  508. mStatusByObject->addElement(element);
  509. commands = "";
  510. }
  511. if (!commands.empty())
  512. {
  513. commands += ",";
  514. }
  515. cmd = it++->second;
  516. commands += cmd;
  517. first = false;
  518. // Now, parse the command and classify it as restriction or
  519. // exception, as appropriate.
  520. parse_command(cmd, restrictions, exceptions);
  521. }
  522. // Add the last object in the status list
  523. LLSD element;
  524. set_status(element, id, commands);
  525. mStatusByObject->addElement(element);
  526. // Finally, build the restrictions/exceptions list from our map
  527. except_map_t::const_iterator iter;
  528. except_map_t::const_iterator except_end = exceptions.end();
  529. for (cmd_list_t::const_iterator it = restrictions.begin(),
  530. end = restrictions.end();
  531. it != end; ++it)
  532. {
  533. LLSD element;
  534. LLSD& columns = element["columns"];
  535. columns[0]["column"] = "restriction";
  536. columns[0]["font"] = "SANSSERIF_SMALL";
  537. columns[0]["value"] = *it;
  538. columns[1]["column"] = "exception";
  539. columns[1]["font"] = "SANSSERIF_SMALL";
  540. iter = exceptions.find(*it);
  541. columns[1]["value"] = iter != except_end ? iter->second : "";
  542. mRestrictions->addElement(element);
  543. }
  544. }
  545. mStatusByObject->setScrollPos(scrollpos1);
  546. mRestrictions->setScrollPos(scrollpos2);
  547. // If a log line is selected in the list, remember the scroll position
  548. // to restore it later.
  549. S32 scrollpos = -1;
  550. if (mCommandsLog->getFirstSelected())
  551. {
  552. scrollpos = mCommandsLog->getScrollPos();
  553. }
  554. // Here we append new log lines to the existing list, for speed
  555. U32 count = sLoggedCommands.size();
  556. if (!mLastCommandsLogSize || count < mLastCommandsLogSize)
  557. {
  558. mCommandsLog->deleteAllItems();
  559. mLastCommandsLogSize = 0;
  560. }
  561. if (count > mLastCommandsLogSize)
  562. {
  563. if (mCommandsLog->hasSortOrder())
  564. {
  565. mCommandsLog->clearSortOrder();
  566. }
  567. for (U32 i = mLastCommandsLogSize; i < count; ++i)
  568. {
  569. LLSD element;
  570. set_log_line(element, sLoggedCommands[i]);
  571. mCommandsLog->addElement(element);
  572. }
  573. mLastCommandsLogSize = sLoggedCommands.size();
  574. // Automatically clamped to last line
  575. mCommandsLog->setScrollPos(S32_MAX);
  576. }
  577. // Restore the scroll position, if a log line was selected, so that
  578. // the user can choose whether or not to let the list scroll on new
  579. // events.
  580. if (scrollpos >= 0)
  581. {
  582. mCommandsLog->setScrollPos(scrollpos);
  583. }
  584. mIsDirty = false;
  585. }
  586. LLFloater::draw();
  587. }
  588. void HBFloaterRLV::setButtonsStatus()
  589. {
  590. bool can_refresh = mTabContainer->getCurrentPanelIndex() < 2;
  591. mRefreshButton->setVisible(can_refresh);
  592. mClearButton->setVisible(!can_refresh);
  593. }
  594. //static
  595. void HBFloaterRLV::setDirty()
  596. {
  597. HBFloaterRLV* self = findInstance();
  598. if (self)
  599. {
  600. self->mIsDirty = true;
  601. }
  602. }
  603. //static
  604. void HBFloaterRLV::logCommand(const LLUUID& obj_id, const std::string& obj_name,
  605. const std::string& command, S32 status)
  606. {
  607. sLoggedCommands.emplace_back(obj_id, obj_name, command, status);
  608. // Note: the constructor of LoggedCommand may change the status and name of
  609. // the logged command (this is currently the case for the "end-relay"
  610. // implicit command) as well as the name of the object name. So we must
  611. // recover the actually stored status and names.
  612. const LoggedCommand& logged_cmd = sLoggedCommands.back();
  613. const std::string& name = logged_cmd.mName;
  614. const std::string& cmd = logged_cmd.mCommand;
  615. status = logged_cmd.mStatus;
  616. switch (status)
  617. {
  618. case QUEUED:
  619. LL_DEBUGS("RestrainedLove") << "Queued command for '"
  620. << name << "' (" << obj_id << "): "
  621. << cmd << LL_ENDL;
  622. break;
  623. case FAILED:
  624. llwarns << "Failed command for '" << name << "' (" << obj_id
  625. << "): " << cmd << llendl;
  626. break;
  627. case EXECUTED:
  628. LL_DEBUGS("RestrainedLove") << "Success executing command for '"
  629. << name << "' (" << obj_id << "): "
  630. << cmd << LL_ENDL;
  631. break;
  632. case IMPLICIT:
  633. LL_DEBUGS("RestrainedLove") << "Executed implicit command for '"
  634. << name << "' (" << obj_id << "): "
  635. << cmd << LL_ENDL;
  636. break;
  637. case BLACKLISTED:
  638. LL_DEBUGS("RestrainedLove") << "Blacklisted command for '"
  639. << name << "' (" << obj_id << "): "
  640. << cmd << LL_ENDL;
  641. break;
  642. default:
  643. llwarns << "Unknown status code " << status << " for command '"
  644. << cmd << "' executed on behalf of: '" << name << "' ("
  645. << obj_id << ")" << llendl;
  646. llassert(false);
  647. }
  648. setDirty();
  649. }
  650. //static
  651. void HBFloaterRLV::onTabChanged(void* data, bool)
  652. {
  653. HBFloaterRLV* self = (HBFloaterRLV*)data;
  654. if (self && self->mTabContainer) // Paranoia
  655. {
  656. gSavedSettings.setS32("LastRLVFloaterTab",
  657. self->mTabContainer->getCurrentPanelIndex());
  658. self->setButtonsStatus();
  659. }
  660. }
  661. //static
  662. void HBFloaterRLV::onButtonHelp(void*)
  663. {
  664. gNotifications.add("RLVFLoaterHelp");
  665. }
  666. //static
  667. void HBFloaterRLV::onButtonRefresh(void* data)
  668. {
  669. HBFloaterRLV* self = (HBFloaterRLV*)data;
  670. if (self)
  671. {
  672. gRLInterface.garbageCollector(false);
  673. self->setDirty();
  674. }
  675. }
  676. //static
  677. void HBFloaterRLV::onButtonClear(void* data)
  678. {
  679. HBFloaterRLV* self = (HBFloaterRLV*)data;
  680. if (self)
  681. {
  682. sLoggedCommands.clear();
  683. self->mLastCommandsLogSize = 0;
  684. self->mIsDirty = true;
  685. }
  686. }
  687. //static
  688. void HBFloaterRLV::onButtonClose(void* data)
  689. {
  690. HBFloaterRLV* self = (HBFloaterRLV*)data;
  691. if (self)
  692. {
  693. self->close();
  694. }
  695. }
  696. //static
  697. void HBFloaterRLV::onDoubleClick(void* data)
  698. {
  699. HBFloaterRLV* self = (HBFloaterRLV*)data;
  700. if (!self) return;
  701. LLScrollListItem* item = self->mStatusByObject->getFirstSelected();
  702. if (!item) return;
  703. // Get the commands in force
  704. const std::string& commands = item->getColumn(1)->getValue().asString();
  705. // Copy it to clipboard
  706. gWindowp->copyTextToClipboard(utf8str_to_wstring(commands));
  707. // Notify
  708. LL_DEBUGS("RestrainedLove") << "RestrainedLove commands in force for object '";
  709. const std::string& name = item->getColumn(0)->getValue().asString();
  710. LL_CONT << name << "': " << commands << LL_ENDL;
  711. gNotifications.add("RLVCommandsCopiedtoClipboard");
  712. }
  713. ///////////////////////////////////////////////////////////////////////////////
  714. // HBFloaterBlacklistRLV class
  715. ///////////////////////////////////////////////////////////////////////////////
  716. // Helper functions
  717. std::string getCommands(S32 type, bool csv = false)
  718. {
  719. std::string commands = gRLInterface.getCommandsByType(type, true);
  720. if (!commands.empty())
  721. {
  722. commands = commands.substr(1);
  723. if (csv)
  724. {
  725. commands = "," + commands;
  726. LLStringUtil::replaceString(commands, "/", ",");
  727. }
  728. else
  729. {
  730. commands = "@" + commands + ",";
  731. LLStringUtil::replaceString(commands, "/", ", @");
  732. LLStringUtil::replaceString(commands, "%f", "=force");
  733. LLStringUtil::replaceString(commands, "_=", "_*=");
  734. LLStringUtil::replaceString(commands, "_,", "_*,");
  735. commands = commands.substr(0, commands.length() - 1);
  736. }
  737. }
  738. return commands;
  739. }
  740. bool isGroupInBlacklist(S32 type)
  741. {
  742. std::string blacklist = gSavedSettings.getString("RestrainedLoveBlacklist");
  743. blacklist = "," + blacklist + ",";
  744. for (RLInterface::rl_command_map_it_t
  745. it = RLInterface::sCommandsMap.begin(),
  746. end = RLInterface::sCommandsMap.end();
  747. it != end; ++it)
  748. {
  749. if ((S32)it->second == type &&
  750. blacklist.find("," + it->first + ",") == std::string::npos)
  751. {
  752. return false;
  753. }
  754. }
  755. return true;
  756. }
  757. // Member functions
  758. HBFloaterBlacklistRLV::HBFloaterBlacklistRLV(const LLSD&)
  759. {
  760. LLUICtrlFactory::getInstance()->buildFloater(this,
  761. "floater_rlv_blacklist.xml");
  762. }
  763. //virtual
  764. bool HBFloaterBlacklistRLV::postBuild()
  765. {
  766. childSetAction("apply", onButtonApply, this);
  767. childSetAction("cancel", onButtonCancel, this);
  768. // Tool tips creation:
  769. std::string prefix = getString("tool_tip_prefix") + " ";
  770. std::string tooltip = getCommands(RLInterface::RL_INSTANTMESSAGE);
  771. LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("instantmessage");
  772. check->setToolTip(prefix + tooltip);
  773. check->set(isGroupInBlacklist(RLInterface::RL_INSTANTMESSAGE));
  774. tooltip = getCommands(RLInterface::RL_CHANNEL);
  775. check = getChild<LLCheckBoxCtrl>("channel");
  776. check->setToolTip(prefix + tooltip);
  777. check->set(isGroupInBlacklist(RLInterface::RL_CHANNEL));
  778. tooltip = getCommands(RLInterface::RL_SENDCHAT);
  779. check = getChild<LLCheckBoxCtrl>("sendchat");
  780. check->setToolTip(prefix + tooltip);
  781. check->set(isGroupInBlacklist(RLInterface::RL_SENDCHAT));
  782. tooltip = getCommands(RLInterface::RL_RECEIVECHAT);
  783. check = getChild<LLCheckBoxCtrl>("receivechat");
  784. check->setToolTip(prefix + tooltip);
  785. check->set(isGroupInBlacklist(RLInterface::RL_RECEIVECHAT));
  786. tooltip = getCommands(RLInterface::RL_EMOTE);
  787. check = getChild<LLCheckBoxCtrl>("emote");
  788. check->setToolTip(prefix + tooltip);
  789. check->set(isGroupInBlacklist(RLInterface::RL_EMOTE));
  790. tooltip = getCommands(RLInterface::RL_REDIRECTION);
  791. check = getChild<LLCheckBoxCtrl>("redirection");
  792. check->setToolTip(prefix + tooltip);
  793. check->set(isGroupInBlacklist(RLInterface::RL_REDIRECTION));
  794. tooltip = getCommands(RLInterface::RL_MOVE);
  795. check = getChild<LLCheckBoxCtrl>("move");
  796. check->setToolTip(prefix + tooltip);
  797. check->set(isGroupInBlacklist(RLInterface::RL_MOVE));
  798. tooltip = getCommands(RLInterface::RL_SIT);
  799. check = getChild<LLCheckBoxCtrl>("sit");
  800. check->setToolTip(prefix + tooltip);
  801. check->set(isGroupInBlacklist(RLInterface::RL_SIT));
  802. tooltip = getCommands(RLInterface::RL_TELEPORT);
  803. check = getChild<LLCheckBoxCtrl>("teleport");
  804. check->setToolTip(prefix + tooltip);
  805. check->set(isGroupInBlacklist(RLInterface::RL_TELEPORT));
  806. tooltip = getCommands(RLInterface::RL_TOUCH);
  807. check = getChild<LLCheckBoxCtrl>("touch");
  808. check->setToolTip(prefix + tooltip);
  809. check->set(isGroupInBlacklist(RLInterface::RL_TOUCH));
  810. tooltip = getCommands(RLInterface::RL_INVENTORY);
  811. check = getChild<LLCheckBoxCtrl>("inventory");
  812. check->setToolTip(prefix + tooltip);
  813. check->set(isGroupInBlacklist(RLInterface::RL_INVENTORY));
  814. tooltip = getCommands(RLInterface::RL_INVENTORYLOCK);
  815. check = getChild<LLCheckBoxCtrl>("inventorylock");
  816. check->setToolTip(prefix + tooltip);
  817. check->set(isGroupInBlacklist(RLInterface::RL_INVENTORYLOCK));
  818. tooltip = getCommands(RLInterface::RL_LOCK);
  819. check = getChild<LLCheckBoxCtrl>("lock");
  820. check->setToolTip(prefix + tooltip);
  821. check->set(isGroupInBlacklist(RLInterface::RL_LOCK));
  822. tooltip = getCommands(RLInterface::RL_BUILD);
  823. check = getChild<LLCheckBoxCtrl>("build");
  824. check->setToolTip(prefix + tooltip);
  825. check->set(isGroupInBlacklist(RLInterface::RL_BUILD));
  826. tooltip = getCommands(RLInterface::RL_ATTACH);
  827. check = getChild<LLCheckBoxCtrl>("attach");
  828. check->setToolTip(prefix + tooltip);
  829. check->set(isGroupInBlacklist(RLInterface::RL_ATTACH));
  830. tooltip = getCommands(RLInterface::RL_DETACH);
  831. check = getChild<LLCheckBoxCtrl>("detach");
  832. check->setToolTip(prefix + tooltip);
  833. check->set(isGroupInBlacklist(RLInterface::RL_DETACH));
  834. tooltip = getCommands(RLInterface::RL_NAME);
  835. check = getChild<LLCheckBoxCtrl>("name");
  836. check->setToolTip(prefix + tooltip);
  837. check->set(isGroupInBlacklist(RLInterface::RL_NAME));
  838. tooltip = getCommands(RLInterface::RL_LOCATION);
  839. check = getChild<LLCheckBoxCtrl>("location");
  840. check->setToolTip(prefix + tooltip);
  841. check->set(isGroupInBlacklist(RLInterface::RL_LOCATION));
  842. tooltip = getCommands(RLInterface::RL_CAMERA);
  843. check = getChild<LLCheckBoxCtrl>("camera");
  844. check->setToolTip(prefix + tooltip);
  845. check->set(isGroupInBlacklist(RLInterface::RL_CAMERA));
  846. tooltip = getCommands(RLInterface::RL_GROUP);
  847. check = getChild<LLCheckBoxCtrl>("group");
  848. check->setToolTip(prefix + tooltip);
  849. check->set(isGroupInBlacklist(RLInterface::RL_GROUP));
  850. tooltip = getCommands(RLInterface::RL_DEBUG);
  851. check = getChild<LLCheckBoxCtrl>("debug");
  852. check->setToolTip(prefix + tooltip);
  853. check->set(isGroupInBlacklist(RLInterface::RL_DEBUG));
  854. tooltip = getCommands(RLInterface::RL_SHARE);
  855. check = getChild<LLCheckBoxCtrl>("share");
  856. check->setToolTip(prefix + tooltip);
  857. check->set(isGroupInBlacklist(RLInterface::RL_SHARE));
  858. center();
  859. return true;
  860. }
  861. //static
  862. void HBFloaterBlacklistRLV::onButtonCancel(void* data)
  863. {
  864. HBFloaterBlacklistRLV* self = (HBFloaterBlacklistRLV*)data;
  865. if (self)
  866. {
  867. self->close();
  868. }
  869. }
  870. //static
  871. void HBFloaterBlacklistRLV::onButtonApply(void* data)
  872. {
  873. HBFloaterBlacklistRLV* self = (HBFloaterBlacklistRLV*)data;
  874. if (self)
  875. {
  876. std::string blacklist;
  877. LLCheckBoxCtrl* check = self->getChild<LLCheckBoxCtrl>("instantmessage");
  878. if (check->get())
  879. {
  880. blacklist += getCommands(RLInterface::RL_INSTANTMESSAGE, true);
  881. }
  882. check = self->getChild<LLCheckBoxCtrl>("channel");
  883. if (check->get())
  884. {
  885. blacklist += getCommands(RLInterface::RL_CHANNEL, true);
  886. }
  887. check = self->getChild<LLCheckBoxCtrl>("sendchat");
  888. if (check->get())
  889. {
  890. blacklist += getCommands(RLInterface::RL_SENDCHAT, true);
  891. }
  892. check = self->getChild<LLCheckBoxCtrl>("receivechat");
  893. if (check->get())
  894. {
  895. blacklist += getCommands(RLInterface::RL_RECEIVECHAT, true);
  896. }
  897. check = self->getChild<LLCheckBoxCtrl>("emote");
  898. if (check->get())
  899. {
  900. blacklist += getCommands(RLInterface::RL_EMOTE, true);
  901. }
  902. check = self->getChild<LLCheckBoxCtrl>("redirection");
  903. if (check->get())
  904. {
  905. blacklist += getCommands(RLInterface::RL_REDIRECTION, true);
  906. }
  907. check = self->getChild<LLCheckBoxCtrl>("move");
  908. if (check->get())
  909. {
  910. blacklist += getCommands(RLInterface::RL_MOVE, true);
  911. }
  912. check = self->getChild<LLCheckBoxCtrl>("sit");
  913. if (check->get())
  914. {
  915. blacklist += getCommands(RLInterface::RL_SIT, true);
  916. }
  917. check = self->getChild<LLCheckBoxCtrl>("teleport");
  918. if (check->get())
  919. {
  920. blacklist += getCommands(RLInterface::RL_TELEPORT, true);
  921. }
  922. check = self->getChild<LLCheckBoxCtrl>("touch");
  923. if (check->get())
  924. {
  925. blacklist += getCommands(RLInterface::RL_TOUCH, true);
  926. }
  927. check = self->getChild<LLCheckBoxCtrl>("inventory");
  928. if (check->get())
  929. {
  930. blacklist += getCommands(RLInterface::RL_INVENTORY, true);
  931. }
  932. check = self->getChild<LLCheckBoxCtrl>("inventorylock");
  933. if (check->get())
  934. {
  935. blacklist += getCommands(RLInterface::RL_INVENTORYLOCK, true);
  936. }
  937. check = self->getChild<LLCheckBoxCtrl>("lock");
  938. if (check->get())
  939. {
  940. blacklist += getCommands(RLInterface::RL_LOCK, true);
  941. }
  942. check = self->getChild<LLCheckBoxCtrl>("build");
  943. if (check->get())
  944. {
  945. blacklist += getCommands(RLInterface::RL_BUILD, true);
  946. }
  947. check = self->getChild<LLCheckBoxCtrl>("attach");
  948. if (check->get())
  949. {
  950. blacklist += getCommands(RLInterface::RL_ATTACH, true);
  951. }
  952. check = self->getChild<LLCheckBoxCtrl>("detach");
  953. if (check->get())
  954. {
  955. blacklist += getCommands(RLInterface::RL_DETACH, true);
  956. }
  957. check = self->getChild<LLCheckBoxCtrl>("name");
  958. if (check->get())
  959. {
  960. blacklist += getCommands(RLInterface::RL_NAME, true);
  961. }
  962. check = self->getChild<LLCheckBoxCtrl>("location");
  963. if (check->get())
  964. {
  965. blacklist += getCommands(RLInterface::RL_LOCATION, true);
  966. }
  967. check = self->getChild<LLCheckBoxCtrl>("camera");
  968. if (check->get())
  969. {
  970. blacklist += getCommands(RLInterface::RL_CAMERA, true);
  971. }
  972. check = self->getChild<LLCheckBoxCtrl>("group");
  973. if (check->get())
  974. {
  975. blacklist += getCommands(RLInterface::RL_GROUP, true);
  976. }
  977. check = self->getChild<LLCheckBoxCtrl>("debug");
  978. if (check->get())
  979. {
  980. blacklist += getCommands(RLInterface::RL_DEBUG, true);
  981. }
  982. check = self->getChild<LLCheckBoxCtrl>("share");
  983. if (check->get())
  984. {
  985. blacklist += getCommands(RLInterface::RL_SHARE, true);
  986. }
  987. if (!blacklist.empty())
  988. {
  989. blacklist = blacklist.substr(1);
  990. }
  991. gSavedSettings.setString("RestrainedLoveBlacklist", blacklist);
  992. self->close();
  993. }
  994. }