llfloaterfriends.cpp 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /**
  2. * @file llfloaterfriends.cpp
  3. * @author Phoenix
  4. * @date 2005-01-13
  5. * @brief Implementation of the friends floater
  6. *
  7. * $LicenseInfo:firstyear=2005&license=viewergpl$
  8. *
  9. * Copyright (c) 2005-2009, Linden Research, Inc.
  10. *
  11. * Second Life Viewer Source Code
  12. * The source code in this file ("Source Code") is provided by Linden Lab
  13. * to you under the terms of the GNU General Public License, version 2.0
  14. * ("GPL"), unless you have obtained a separate licensing agreement
  15. * ("Other License"), formally executed by you and Linden Lab. Terms of
  16. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18. *
  19. * There are special exceptions to the terms and conditions of the GPL as
  20. * it is applied to this Source Code. View the full text of the exception
  21. * in the file doc/FLOSS-exception.txt in this software distribution, or
  22. * online at
  23. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24. *
  25. * By copying, modifying or distributing this software, you acknowledge
  26. * that you have read and understood your obligations described above,
  27. * and agree to abide by those obligations.
  28. *
  29. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31. * COMPLETENESS OR PERFORMANCE.
  32. * $/LicenseInfo$
  33. */
  34. #include "llviewerprecompiledheaders.h"
  35. #include <sstream>
  36. #include "llfloaterfriends.h"
  37. #include "llavatarnamecache.h"
  38. #include "llbutton.h"
  39. #include "lldir.h"
  40. #include "lllineeditor.h"
  41. #include "llnamelistctrl.h"
  42. #include "lltextbox.h"
  43. #include "lluictrlfactory.h"
  44. #include "llagent.h"
  45. #include "llavataractions.h"
  46. #include "llfloateravatarinfo.h"
  47. #include "llfloateravatarpicker.h"
  48. #include "llinventorymodel.h"
  49. #include "llviewercontrol.h"
  50. // Maximum number of people you can select to do an operation on at once.
  51. #define MAX_FRIEND_SELECT 20
  52. #define DEFAULT_PERIOD 5.0
  53. #define RIGHTS_CHANGE_TIMEOUT 5.0
  54. #define OBSERVER_TIMEOUT 0.5
  55. #define COMMENT_PREFIX "\342\200\243 "
  56. static uuid_list_t sNoBuddy;
  57. // Simple class to observe the calling cards.
  58. class LLLocalFriendsObserver final : public LLFriendObserver,
  59. public LLEventTimer
  60. {
  61. protected:
  62. LOG_CLASS(LLLocalFriendsObserver);
  63. public:
  64. LLLocalFriendsObserver(LLFloaterFriends* floater)
  65. : mFloater(floater),
  66. LLEventTimer(OBSERVER_TIMEOUT)
  67. {
  68. mEventTimer.stop();
  69. }
  70. ~LLLocalFriendsObserver() override
  71. {
  72. mFloater = NULL;
  73. }
  74. void changed(U32 mask) override
  75. {
  76. LL_DEBUGS("Friends") << "Changed event with mask=" << mask << LL_ENDL;
  77. // Events can arrive quickly in bulk - we need not process EVERY one of
  78. // them - so we wait a short while to let others pile-in, and process
  79. // them in aggregate.
  80. mEventTimer.start();
  81. // Save-up all the mask-bits which have come-in
  82. mMask |= mask;
  83. }
  84. void changedBuddies(const uuid_list_t& buddies) override
  85. {
  86. for (uuid_list_t::const_iterator it = buddies.begin(),
  87. end = buddies.end();
  88. it != end; ++it)
  89. {
  90. LL_DEBUGS("Friends") << "Changed buddy: " << *it << LL_ENDL;
  91. mChangedBuddies.emplace(*it);
  92. }
  93. }
  94. bool tick() override
  95. {
  96. LL_DEBUGS("Friends") << "Updating friends list. Mask=" << mMask
  97. << LL_ENDL;
  98. mFloater->updateFriends(mMask, mChangedBuddies);
  99. mMask = 0;
  100. mChangedBuddies.clear();
  101. mEventTimer.stop();
  102. return false;
  103. }
  104. protected:
  105. LLFloaterFriends* mFloater;
  106. uuid_list_t mChangedBuddies;
  107. U32 mMask;
  108. };
  109. LLFloaterFriends::LLFloaterFriends(const LLSD&)
  110. : LLEventTimer(DEFAULT_PERIOD),
  111. mListComment(NULL),
  112. mObserver(NULL),
  113. mNumRightsChanged(0)
  114. {
  115. mEventTimer.stop();
  116. mObserver = new LLLocalFriendsObserver(this);
  117. gAvatarTracker.addObserver(mObserver);
  118. gSavedSettings.setBool("ShowFriends", true);
  119. LLUICtrlFactory::getInstance()->buildFloater(this, "floater_friends.xml");
  120. refreshUI();
  121. }
  122. LLFloaterFriends::~LLFloaterFriends()
  123. {
  124. gAvatarTracker.removeObserver(mObserver);
  125. delete mObserver;
  126. gSavedSettings.setBool("ShowFriends", false);
  127. }
  128. //virtual
  129. bool LLFloaterFriends::postBuild()
  130. {
  131. mFriendsList = getChild<LLScrollListCtrl>("friend_list");
  132. mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
  133. mFriendsList->setMaximumSelectCallback(onMaximumSelect);
  134. mFriendsList->setCommitOnSelectionChange(true);
  135. mFriendsList->setCommitCallback(onSelectName);
  136. mFriendsList->setCallbackUserData(this);
  137. mFriendsList->setDoubleClickCallback(onClickIM);
  138. LLSearchEditor* editp = getChild<LLSearchEditor>("filter_search");
  139. editp->setSearchCallback(onSearchEdit, this);
  140. mIMButton = getChild<LLButton>("im_btn");
  141. mIMButton->setClickedCallback(onClickIM, this);
  142. mProfileButton = getChild<LLButton>("profile_btn");
  143. mProfileButton->setClickedCallback(onClickProfile, this);
  144. mOfferTPButton = getChild<LLButton>("offer_teleport_btn");
  145. mOfferTPButton->setClickedCallback(onClickOfferTeleport, this);
  146. mRequestTPButton = getChild<LLButton>("request_teleport_btn");
  147. mRequestTPButton->setClickedCallback(onClickRequestTeleport, this);
  148. mPayButton = getChild<LLButton>("pay_btn");
  149. mPayButton->setClickedCallback(onClickPay, this);
  150. mRemoveButton = getChild<LLButton>("remove_btn");
  151. mRemoveButton->setClickedCallback(onClickRemove, this);
  152. childSetAction("add_btn", onClickAddFriend, this);
  153. childSetAction("close_btn", onClickClose, this);
  154. setDefaultBtn(mIMButton);
  155. refreshNames();
  156. updateFriends(LLFriendObserver::ADD, sNoBuddy);
  157. refreshUI();
  158. // Primary sort = online status, secondary sort = name
  159. mFriendsList->sortByColumn("friend_name", true);
  160. mFriendsList->sortByColumn("icon_online_status", false);
  161. // Force a refesh to get the latest display names.
  162. gAvatarTracker.dirtyBuddies();
  163. return true;
  164. }
  165. //virtual
  166. bool LLFloaterFriends::tick()
  167. {
  168. mEventTimer.stop();
  169. mPeriod = DEFAULT_PERIOD;
  170. updateFriends(LLFriendObserver::ADD, sNoBuddy);
  171. return false;
  172. }
  173. void LLFloaterFriends::updateFriends(U32 changed_mask,
  174. const uuid_list_t& buddies)
  175. {
  176. LLFloaterFriends* self = findInstance();
  177. if (!self) return;
  178. uuid_vec_t selected_friends = self->getSelectedIDs();
  179. if (changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
  180. {
  181. // Rebuild the whole list unconditionnally
  182. refreshNames();
  183. }
  184. else if (changed_mask & LLFriendObserver::ONLINE)
  185. {
  186. bool success = true;
  187. // Refresh only the changed items
  188. for (uuid_list_t::const_iterator it = buddies.begin(),
  189. end = buddies.end();
  190. success && it != end; ++it)
  191. {
  192. success &= updateFriendItem(*it);
  193. }
  194. if (!success)
  195. {
  196. // Rebuild the whole list unconditionnally
  197. refreshNames();
  198. }
  199. }
  200. if (changed_mask & LLFriendObserver::POWERS)
  201. {
  202. if (--mNumRightsChanged > 0)
  203. {
  204. mPeriod = RIGHTS_CHANGE_TIMEOUT;
  205. mEventTimer.start();
  206. }
  207. else
  208. {
  209. tick();
  210. }
  211. }
  212. if (selected_friends.size() > 0)
  213. {
  214. // Only non-null if friends was already found. This may fail, but we
  215. // do not really care here, because refreshUI() will clean up the
  216. // interface.
  217. for (uuid_vec_t::iterator it = selected_friends.begin();
  218. it != selected_friends.end(); ++it)
  219. {
  220. self->mFriendsList->setSelectedByValue(*it, true);
  221. }
  222. }
  223. refreshUI();
  224. }
  225. bool LLFloaterFriends::addFriend(const LLUUID& agent_id)
  226. {
  227. const LLRelationship* infop = gAvatarTracker.getBuddyInfo(agent_id);
  228. if (!infop) return false;
  229. std::string fullname;
  230. bool has_name = gCacheNamep &&
  231. gCacheNamep->getFullName(agent_id, fullname);
  232. if (has_name)
  233. {
  234. if (!LLAvatarName::sLegacyNamesForFriends &&
  235. LLAvatarNameCache::useDisplayNames())
  236. {
  237. LLAvatarName avatar_name;
  238. if (LLAvatarNameCache::get(agent_id, &avatar_name))
  239. {
  240. if (LLAvatarNameCache::useDisplayNames() == 2)
  241. {
  242. fullname = avatar_name.mDisplayName;
  243. }
  244. else
  245. {
  246. fullname = avatar_name.getNames();
  247. }
  248. }
  249. }
  250. }
  251. if (has_name && !mFilterString.empty())
  252. {
  253. std::string name = fullname;
  254. LLStringUtil::toLower(name);
  255. if (name.find(mFilterString) == std::string::npos)
  256. {
  257. // Friend name does not match the filter, skip it. HB
  258. return true;
  259. }
  260. }
  261. LLSD element;
  262. element["id"] = agent_id;
  263. LLSD& friend_column = element["columns"][LIST_FRIEND_NAME];
  264. friend_column["column"] = "friend_name";
  265. friend_column["value"] = fullname;
  266. friend_column["font"] = "SANSSERIF";
  267. friend_column["font-style"] = "NORMAL";
  268. LLSD& online_status_column = element["columns"][LIST_ONLINE_STATUS];
  269. online_status_column["column"] = "icon_online_status";
  270. online_status_column["type"] = "icon";
  271. bool online = infop->isOnline();
  272. if (online)
  273. {
  274. friend_column["font-style"] = "BOLD";
  275. online_status_column["value"] = "icon_avatar_online.tga";
  276. }
  277. LLSD& online_column = element["columns"][LIST_VISIBLE_ONLINE];
  278. online_column["column"] = "icon_visible_online";
  279. online_column["type"] = "checkbox";
  280. online_column["value"] =
  281. infop->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS);
  282. LLSD& visible_map_column = element["columns"][LIST_VISIBLE_MAP];
  283. visible_map_column["column"] = "icon_visible_map";
  284. visible_map_column["type"] = "checkbox";
  285. visible_map_column["value"] =
  286. infop->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION);
  287. LLSD& edit_my_object_column = element["columns"][LIST_EDIT_MINE];
  288. edit_my_object_column["column"] = "icon_edit_mine";
  289. edit_my_object_column["type"] = "checkbox";
  290. edit_my_object_column["value"] =
  291. infop->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
  292. LLSD& see_them = element["columns"][LIST_ONLINE_OR_MAP_THEIRS];
  293. see_them["column"] = "icon_visible_online_or_map_theirs";
  294. see_them["type"] = "icon";
  295. if (infop->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
  296. {
  297. see_them["value"] = "ff_visible_map_theirs.tga";
  298. }
  299. else if (online ||
  300. infop->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS))
  301. {
  302. see_them["value"] = "ff_visible_online_theirs.tga";
  303. }
  304. LLSD& edit_their_object_column = element["columns"][LIST_EDIT_THEIRS];
  305. edit_their_object_column["column"] = "icon_edit_theirs";
  306. edit_their_object_column["type"] = "icon";
  307. if (infop->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
  308. {
  309. edit_their_object_column["value"] = "ff_edit_theirs.tga";
  310. }
  311. LLSD& update_gen_column = element["columns"][LIST_FRIEND_UPDATE_GEN];
  312. update_gen_column["column"] = "friend_last_update_generation";
  313. update_gen_column["value"] = has_name ? infop->getChangeSerialNum() : -1;
  314. mFriendsList->addElement(element, ADD_BOTTOM);
  315. return has_name;
  316. }
  317. // Propagate actual relationship to UI. Does not re-sort the UI list because it
  318. // can be called frequently. JC
  319. bool LLFloaterFriends::updateFriendItem(const LLUUID& agent_id)
  320. {
  321. const LLRelationship* info = gAvatarTracker.getBuddyInfo(agent_id);
  322. if (!info) return false;
  323. LLScrollListItem* itemp = mFriendsList->getItem(agent_id);
  324. if (!itemp) return false;
  325. bool online = info->isOnline();
  326. std::string fullname;
  327. bool has_name = gCacheNamep &&
  328. gCacheNamep->getFullName(agent_id, fullname);
  329. if (has_name)
  330. {
  331. if (!LLAvatarName::sLegacyNamesForFriends &&
  332. LLAvatarNameCache::useDisplayNames())
  333. {
  334. LLAvatarName avatar_name;
  335. if (LLAvatarNameCache::get(agent_id, &avatar_name))
  336. {
  337. if (LLAvatarNameCache::useDisplayNames() == 2)
  338. {
  339. fullname = avatar_name.mDisplayName;
  340. }
  341. else
  342. {
  343. fullname = avatar_name.getNames();
  344. }
  345. }
  346. }
  347. }
  348. LL_DEBUGS("Friends") << "Updating entry for: " << fullname << " - Online: "
  349. << (online ? "yes" : "no") << LL_ENDL;
  350. // Name of the status icon to use
  351. std::string status_icon;
  352. if (online)
  353. {
  354. status_icon = "icon_avatar_online.tga";
  355. }
  356. itemp->getColumn(LIST_ONLINE_STATUS)->setValue(status_icon);
  357. itemp->getColumn(LIST_FRIEND_NAME)->setValue(fullname);
  358. // Render name of online friends in bold text
  359. LLScrollListText* textp =
  360. (LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME);
  361. textp->setFontStyle(online ? LLFontGL::BOLD : LLFontGL::NORMAL);
  362. LLScrollListCell* cellp = itemp->getColumn(LIST_VISIBLE_ONLINE);
  363. cellp->setValue(info->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS));
  364. cellp = itemp->getColumn(LIST_VISIBLE_MAP);
  365. cellp->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION));
  366. cellp = itemp->getColumn(LIST_EDIT_MINE);
  367. cellp->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
  368. if (info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
  369. {
  370. status_icon = "ff_visible_map_theirs.tga";
  371. }
  372. else if (online ||
  373. info->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS))
  374. {
  375. status_icon = "ff_visible_online_theirs.tga";
  376. }
  377. else
  378. {
  379. status_icon.clear();
  380. }
  381. itemp->getColumn(LIST_ONLINE_OR_MAP_THEIRS)->setValue(status_icon);
  382. if (info->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
  383. {
  384. status_icon = "ff_edit_theirs.tga";
  385. }
  386. else
  387. {
  388. status_icon.clear();
  389. }
  390. itemp->getColumn(LIST_EDIT_THEIRS)->setValue(status_icon);
  391. S32 change_generation = has_name ? info->getChangeSerialNum() : -1;
  392. itemp->getColumn(LIST_FRIEND_UPDATE_GEN)->setValue(change_generation);
  393. // Enable these items, in case they were disabled after user input
  394. itemp->getColumn(LIST_VISIBLE_ONLINE)->setEnabled(true);
  395. itemp->getColumn(LIST_VISIBLE_MAP)->setEnabled(true);
  396. itemp->getColumn(LIST_EDIT_MINE)->setEnabled(true);
  397. // Do not resort, this function can be called frequently.
  398. return has_name;
  399. }
  400. void LLFloaterFriends::refreshRightsChangeList()
  401. {
  402. uuid_vec_t friends = getSelectedIDs();
  403. S32 num_selected = friends.size();
  404. bool can_offer_teleport = num_selected >= 1;
  405. bool selected_friends_online = true;
  406. const LLRelationship* friend_status = NULL;
  407. for (uuid_vec_t::iterator it = friends.begin();
  408. it != friends.end(); ++it)
  409. {
  410. friend_status = gAvatarTracker.getBuddyInfo(*it);
  411. if (friend_status)
  412. {
  413. if (!friend_status->isOnline())
  414. {
  415. can_offer_teleport = false;
  416. selected_friends_online = false;
  417. }
  418. }
  419. else // Missing buddy info, do not allow any operations
  420. {
  421. can_offer_teleport = false;
  422. }
  423. }
  424. if (num_selected == 0) // nothing selected
  425. {
  426. mIMButton->setEnabled(false);
  427. mOfferTPButton->setEnabled(false);
  428. mRequestTPButton->setEnabled(false);
  429. }
  430. else // we have at least one friend selected...
  431. {
  432. // Only allow IMs to groups when everyone in the group is online to be
  433. // consistent with context menus in inventory and because otherwise
  434. // offline friends would be silently dropped from the session
  435. mIMButton->setEnabled(selected_friends_online || num_selected == 1);
  436. mOfferTPButton->setEnabled(can_offer_teleport);
  437. mRequestTPButton->setEnabled(can_offer_teleport && num_selected == 1);
  438. }
  439. }
  440. struct SortFriendsByID
  441. {
  442. bool operator() (const LLScrollListItem* const a,
  443. const LLScrollListItem* const b) const
  444. {
  445. return a->getValue().asUUID() < b->getValue().asUUID();
  446. }
  447. };
  448. void LLFloaterFriends::refreshNames()
  449. {
  450. LL_DEBUGS("Friends") << "Refreshing all names" << LL_ENDL;
  451. uuid_vec_t selected_ids = getSelectedIDs();
  452. S32 pos = mFriendsList->getScrollPos();
  453. mFriendsList->deleteAllItems();
  454. // Get all buddies we know about
  455. LLAvatarTracker::buddy_map_t all_buddies;
  456. gAvatarTracker.copyBuddyList(all_buddies);
  457. bool has_names = true;
  458. for (LLAvatarTracker::buddy_map_t::const_iterator it = all_buddies.begin(),
  459. end = all_buddies.end();
  460. it != end; ++it)
  461. {
  462. has_names &= addFriend(it->first);
  463. }
  464. if (!has_names)
  465. {
  466. mEventTimer.start();
  467. }
  468. // Changed item in place, need to request sort and update columns because
  469. // we might have changed data in a column on which the user has already
  470. // sorted. JC
  471. mFriendsList->sortItems();
  472. // Re-select items
  473. mFriendsList->selectMultiple(selected_ids);
  474. mFriendsList->setScrollPos(pos);
  475. }
  476. void LLFloaterFriends::refreshUI()
  477. {
  478. if (mListComment)
  479. {
  480. mFriendsList->deleteItem(mListComment);
  481. mListComment = NULL;
  482. }
  483. S32 num_selected = mFriendsList->getAllSelected().size();
  484. bool single_selected = num_selected == 1;
  485. bool some_selected = num_selected > 0;
  486. // Options that can only be performed with one friend selected
  487. mProfileButton->setEnabled(single_selected);
  488. mPayButton->setEnabled(single_selected);
  489. // Options that can be performed with up to MAX_FRIEND_SELECT friends
  490. // selected
  491. mRemoveButton->setEnabled(some_selected);
  492. mIMButton->setEnabled(some_selected);
  493. refreshRightsChangeList();
  494. S32 count = mFriendsList->getItemCount();
  495. std::string comment = COMMENT_PREFIX;
  496. if (count > 0)
  497. {
  498. comment += getString("total_friends") + llformat(" %d", count);
  499. }
  500. else
  501. {
  502. comment += getString("no_friend");
  503. }
  504. mListComment = mFriendsList->addCommentText(comment);
  505. }
  506. //static
  507. uuid_vec_t LLFloaterFriends::getSelectedIDs()
  508. {
  509. uuid_vec_t friend_ids;
  510. LLFloaterFriends* self = findInstance();
  511. if (self)
  512. {
  513. std::vector<LLScrollListItem*> selected =
  514. self->mFriendsList->getAllSelected();
  515. for (S32 i = 0, count = selected.size(); i < count; ++i)
  516. {
  517. friend_ids.emplace_back(selected[i]->getUUID());
  518. }
  519. }
  520. return friend_ids;
  521. }
  522. //static
  523. void LLFloaterFriends::onSelectName(LLUICtrl* ctrl, void* data)
  524. {
  525. LLFloaterFriends* self = (LLFloaterFriends*)data;
  526. if (self)
  527. {
  528. self->refreshUI();
  529. // check to see if rights have changed
  530. self->applyRightsToFriends();
  531. }
  532. }
  533. //static
  534. void LLFloaterFriends::onSearchEdit(const std::string& search_string,
  535. void* data)
  536. {
  537. LLFloaterFriends* self = (LLFloaterFriends*)data;
  538. if (self)
  539. {
  540. self->mFilterString = search_string;
  541. LLStringUtil::trim(self->mFilterString);
  542. LLStringUtil::toLower(self->mFilterString);
  543. self->refreshNames();
  544. self->refreshUI();
  545. }
  546. }
  547. //static
  548. void LLFloaterFriends::onMaximumSelect(void* data)
  549. {
  550. LLSD args;
  551. args["MAX_SELECT"] = llformat("%d", MAX_FRIEND_SELECT);
  552. gNotifications.add("MaxListSelectMessage", args);
  553. }
  554. //static
  555. void LLFloaterFriends::onClickProfile(void* data)
  556. {
  557. LLFloaterFriends* self = (LLFloaterFriends*)data;
  558. if (self)
  559. {
  560. uuid_vec_t ids = self->getSelectedIDs();
  561. if (ids.size() > 0)
  562. {
  563. const LLUUID& agent_id = ids[0];
  564. bool online = gAvatarTracker.isBuddyOnline(agent_id);
  565. LLFloaterAvatarInfo::showFromFriend(agent_id, online);
  566. }
  567. }
  568. }
  569. //static
  570. void LLFloaterFriends::onClickIM(void* data)
  571. {
  572. LLFloaterFriends* self = (LLFloaterFriends*)data;
  573. if (self)
  574. {
  575. uuid_vec_t ids = self->getSelectedIDs();
  576. LLAvatarActions::startIM(ids, true);
  577. }
  578. }
  579. //static
  580. void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
  581. const std::vector<LLUUID>& ids, void*)
  582. {
  583. if (!names.empty() && !ids.empty())
  584. {
  585. LLAvatarActions::requestFriendshipDialog(ids[0], names[0]);
  586. }
  587. }
  588. //static
  589. void LLFloaterFriends::onClickAddFriend(void* data)
  590. {
  591. LLFloaterFriends* self = (LLFloaterFriends*)data;
  592. if (self)
  593. {
  594. LLFloaterAvatarPicker* pickerp =
  595. LLFloaterAvatarPicker::show(onPickAvatar, data, false, true);
  596. if (pickerp)
  597. {
  598. self->addDependentFloater(pickerp);
  599. }
  600. }
  601. }
  602. //static
  603. void LLFloaterFriends::onClickRemove(void* data)
  604. {
  605. LLFloaterFriends* self = (LLFloaterFriends*)data;
  606. if (!self) return;
  607. uuid_vec_t ids = self->getSelectedIDs();
  608. if (!ids.size())
  609. {
  610. return;
  611. }
  612. LLSD args;
  613. std::string type;
  614. if (ids.size() == 1)
  615. {
  616. const LLUUID& agent_id = ids[0];
  617. std::string name;
  618. if (gCacheNamep && gCacheNamep->getFullName(agent_id, name))
  619. {
  620. if (!LLAvatarName::sLegacyNamesForFriends &&
  621. LLAvatarNameCache::useDisplayNames())
  622. {
  623. LLAvatarName avatar_name;
  624. if (LLAvatarNameCache::get(agent_id, &avatar_name))
  625. {
  626. // Always show "Display Name [Legacy Name]" for
  627. // security reasons
  628. name = avatar_name.getNames();
  629. }
  630. }
  631. args["NAME"] = name;
  632. }
  633. type = "RemoveFromFriends";
  634. }
  635. else
  636. {
  637. type = "RemoveMultipleFromFriends";
  638. }
  639. LLSD payload;
  640. for (uuid_vec_t::iterator it = ids.begin(); it != ids.end(); ++it)
  641. {
  642. payload["ids"].append(*it);
  643. }
  644. gNotifications.add(type, args, payload, handleRemove);
  645. }
  646. //static
  647. void LLFloaterFriends::onClickOfferTeleport(void* data)
  648. {
  649. LLFloaterFriends* self = (LLFloaterFriends*)data;
  650. if (self)
  651. {
  652. uuid_vec_t ids = self->getSelectedIDs();
  653. LLAvatarActions::offerTeleport(ids);
  654. }
  655. }
  656. //static
  657. void LLFloaterFriends::onClickRequestTeleport(void* data)
  658. {
  659. LLFloaterFriends* self = (LLFloaterFriends*)data;
  660. if (self)
  661. {
  662. uuid_vec_t ids = self->getSelectedIDs();
  663. if (ids.size() == 1)
  664. {
  665. LLAvatarActions::teleportRequest(ids[0]);
  666. }
  667. }
  668. }
  669. //static
  670. void LLFloaterFriends::onClickPay(void* data)
  671. {
  672. LLFloaterFriends* self = (LLFloaterFriends*)data;
  673. if (self)
  674. {
  675. uuid_vec_t ids = self->getSelectedIDs();
  676. if (ids.size() == 1)
  677. {
  678. LLAvatarActions::pay(ids[0]);
  679. }
  680. }
  681. }
  682. //static
  683. void LLFloaterFriends::onClickClose(void* data)
  684. {
  685. LLFloaterFriends* self = (LLFloaterFriends*)data;
  686. if (self)
  687. {
  688. self->close();
  689. }
  690. }
  691. void LLFloaterFriends::confirmModifyRights(rights_map_t& ids,
  692. EGrantRevoke command)
  693. {
  694. if (ids.empty()) return;
  695. LLSD args;
  696. if (ids.size() > 0)
  697. {
  698. rights_map_t* rights = new rights_map_t(ids);
  699. // For a single friend, show their name
  700. if (ids.size() == 1)
  701. {
  702. const LLUUID& agent_id = ids.begin()->first;
  703. std::string name;
  704. if (gCacheNamep && gCacheNamep->getFullName(agent_id, name))
  705. {
  706. if (!LLAvatarName::sLegacyNamesForFriends &&
  707. LLAvatarNameCache::useDisplayNames())
  708. {
  709. LLAvatarName avatar_name;
  710. if (LLAvatarNameCache::get(agent_id, &avatar_name))
  711. {
  712. // Always show "Display Name [Legacy Name]" for
  713. // security reasons
  714. name = avatar_name.getNames();
  715. }
  716. }
  717. args["NAME"] = name;
  718. }
  719. if (command == GRANT)
  720. {
  721. gNotifications.add("GrantModifyRights", args, LLSD(),
  722. boost::bind(&LLFloaterFriends::modifyRightsConfirmation,
  723. this, _1, _2, rights));
  724. }
  725. else
  726. {
  727. gNotifications.add("RevokeModifyRights", args, LLSD(),
  728. boost::bind(&LLFloaterFriends::modifyRightsConfirmation,
  729. this, _1, _2, rights));
  730. }
  731. }
  732. else if (command == GRANT)
  733. {
  734. gNotifications.add("GrantModifyRightsMultiple", args, LLSD(),
  735. boost::bind(&LLFloaterFriends::modifyRightsConfirmation,
  736. this, _1, _2, rights));
  737. }
  738. else
  739. {
  740. gNotifications.add("RevokeModifyRightsMultiple", args, LLSD(),
  741. boost::bind(&LLFloaterFriends::modifyRightsConfirmation,
  742. this, _1, _2, rights));
  743. }
  744. }
  745. }
  746. bool LLFloaterFriends::modifyRightsConfirmation(const LLSD& notification,
  747. const LLSD& response,
  748. rights_map_t* rights)
  749. {
  750. if (LLNotification::getSelectedOption(notification, response) == 0)
  751. {
  752. sendRightsGrant(*rights);
  753. }
  754. else
  755. {
  756. // We need to resync view with model, since user cancelled operation
  757. rights_map_t::iterator rights_it;
  758. for (rights_it = rights->begin(); rights_it != rights->end();
  759. ++rights_it)
  760. {
  761. updateFriendItem(rights_it->first);
  762. }
  763. }
  764. delete rights;
  765. return false;
  766. }
  767. void LLFloaterFriends::applyRightsToFriends()
  768. {
  769. bool rights_changed = false;
  770. // store modify rights separately for confirmation
  771. rights_map_t rights_updates;
  772. bool need_confirmation = false;
  773. EGrantRevoke confirmation_type = GRANT;
  774. // This assumes that changes only happened to selected items
  775. LLUUID id;
  776. std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected();
  777. for (S32 i = 0, count = selected.size(); i < count; ++i)
  778. {
  779. LLScrollListItem* itemp = selected[i];
  780. id = itemp->getUUID();
  781. const LLRelationship* rel = gAvatarTracker.getBuddyInfo(id);
  782. if (!rel) continue;
  783. bool show_online_status =
  784. itemp->getColumn(LIST_VISIBLE_ONLINE)->getValue().asBoolean();
  785. bool show_map_location =
  786. itemp->getColumn(LIST_VISIBLE_MAP)->getValue().asBoolean();
  787. bool allow_modify_objects =
  788. itemp->getColumn(LIST_EDIT_MINE)->getValue().asBoolean();
  789. S32 rights = rel->getRightsGrantedTo();
  790. if (rel->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) !=
  791. show_online_status)
  792. {
  793. rights_changed = true;
  794. if (show_online_status)
  795. {
  796. rights |= LLRelationship::GRANT_ONLINE_STATUS;
  797. }
  798. else
  799. {
  800. // ONLINE_STATUS necessary for MAP_LOCATION
  801. rights &= ~LLRelationship::GRANT_ONLINE_STATUS;
  802. rights &= ~LLRelationship::GRANT_MAP_LOCATION;
  803. // propagate rights constraint to UI
  804. itemp->getColumn(LIST_VISIBLE_MAP)->setValue(false);
  805. }
  806. }
  807. if (rel->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) !=
  808. show_map_location)
  809. {
  810. rights_changed = true;
  811. if (show_map_location)
  812. {
  813. // ONLINE_STATUS necessary for MAP_LOCATION
  814. rights |= LLRelationship::GRANT_MAP_LOCATION;
  815. rights |= LLRelationship::GRANT_ONLINE_STATUS;
  816. itemp->getColumn(LIST_VISIBLE_ONLINE)->setValue(true);
  817. }
  818. else
  819. {
  820. rights &= ~LLRelationship::GRANT_MAP_LOCATION;
  821. }
  822. }
  823. // Now check for change in modify object rights, which requires
  824. // confirmation
  825. if (rel->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS) !=
  826. allow_modify_objects)
  827. {
  828. rights_changed = need_confirmation = true;
  829. if (allow_modify_objects)
  830. {
  831. rights |= LLRelationship::GRANT_MODIFY_OBJECTS;
  832. confirmation_type = GRANT;
  833. }
  834. else
  835. {
  836. rights &= ~LLRelationship::GRANT_MODIFY_OBJECTS;
  837. confirmation_type = REVOKE;
  838. }
  839. }
  840. if (rights_changed)
  841. {
  842. rights_updates.emplace(id, rights);
  843. // Disable these UI elements until response from server to avoid
  844. // race conditions
  845. itemp->getColumn(LIST_VISIBLE_ONLINE)->setEnabled(false);
  846. itemp->getColumn(LIST_VISIBLE_MAP)->setEnabled(false);
  847. itemp->getColumn(LIST_EDIT_MINE)->setEnabled(false);
  848. }
  849. }
  850. // Separately confirm grant and revoke of modify rights
  851. if (need_confirmation)
  852. {
  853. confirmModifyRights(rights_updates, confirmation_type);
  854. }
  855. else
  856. {
  857. sendRightsGrant(rights_updates);
  858. }
  859. }
  860. void LLFloaterFriends::sendRightsGrant(rights_map_t& ids)
  861. {
  862. LLMessageSystem* msg = gMessageSystemp;
  863. if (!msg || ids.empty()) return;
  864. // Setup message header
  865. msg->newMessageFast(_PREHASH_GrantUserRights);
  866. msg->nextBlockFast(_PREHASH_AgentData);
  867. msg->addUUID(_PREHASH_AgentID, gAgentID);
  868. msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
  869. for (rights_map_t::iterator it = ids.begin(), end = ids.end();
  870. it != end; ++it)
  871. {
  872. msg->nextBlockFast(_PREHASH_Rights);
  873. msg->addUUID(_PREHASH_AgentRelated, it->first);
  874. msg->addS32(_PREHASH_RelatedRights, it->second);
  875. }
  876. mNumRightsChanged = ids.size();
  877. gAgent.sendReliableMessage();
  878. }
  879. //static
  880. bool LLFloaterFriends::handleRemove(const LLSD& notification,
  881. const LLSD& response)
  882. {
  883. if (LLNotification::getSelectedOption(notification, response) != 0) // NO
  884. {
  885. llinfos << "No removal performed." << llendl;
  886. return false;
  887. }
  888. const LLSD& ids = notification["payload"]["ids"];
  889. for (LLSD::array_const_iterator it = ids.beginArray(),
  890. end = ids.endArray();
  891. it != end; ++it)
  892. {
  893. LLUUID id = it->asUUID();
  894. const LLRelationship* ip = gAvatarTracker.getBuddyInfo(id);
  895. if (ip)
  896. {
  897. if (ip->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS))
  898. {
  899. #if TRACK_POWER
  900. gAvatarTracker.empower(id, false);
  901. #endif
  902. gAvatarTracker.notifyObservers();
  903. }
  904. gAvatarTracker.terminateBuddy(id);
  905. gAvatarTracker.notifyObservers();
  906. gInventory.addChangedMask(LLInventoryObserver::LABEL |
  907. LLInventoryObserver::CALLING_CARD,
  908. LLUUID::null);
  909. gInventory.notifyObservers();
  910. }
  911. }
  912. return false;
  913. }