PageRenderTime 42ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llavatarlist.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 626 lines | 444 code | 97 blank | 85 comment | 68 complexity | be35dc2eac7e75ff43899da537b30826 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llavatarlist.h
  3. * @brief Generic avatar list
  4. *
  5. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llavatarlist.h"
  28. // common
  29. #include "lltrans.h"
  30. #include "llcommonutils.h"
  31. // llui
  32. #include "lltextutil.h"
  33. // newview
  34. #include "llagentdata.h" // for comparator
  35. #include "llavatariconctrl.h"
  36. #include "llavatarnamecache.h"
  37. #include "llcallingcard.h" // for LLAvatarTracker
  38. #include "llcachename.h"
  39. #include "lllistcontextmenu.h"
  40. #include "llrecentpeople.h"
  41. #include "lluuid.h"
  42. #include "llvoiceclient.h"
  43. #include "llviewercontrol.h" // for gSavedSettings
  44. static LLDefaultChildRegistry::Register<LLAvatarList> r("avatar_list");
  45. // Last interaction time update period.
  46. static const F32 LIT_UPDATE_PERIOD = 5;
  47. // Maximum number of avatars that can be added to a list in one pass.
  48. // Used to limit time spent for avatar list update per frame.
  49. static const unsigned ADD_LIMIT = 50;
  50. bool LLAvatarList::contains(const LLUUID& id)
  51. {
  52. const uuid_vec_t& ids = getIDs();
  53. return std::find(ids.begin(), ids.end(), id) != ids.end();
  54. }
  55. void LLAvatarList::toggleIcons()
  56. {
  57. // Save the new value for new items to use.
  58. mShowIcons = !mShowIcons;
  59. gSavedSettings.setBOOL(mIconParamName, mShowIcons);
  60. // Show/hide icons for all existing items.
  61. std::vector<LLPanel*> items;
  62. getItems(items);
  63. for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
  64. {
  65. static_cast<LLAvatarListItem*>(*it)->setAvatarIconVisible(mShowIcons);
  66. }
  67. }
  68. void LLAvatarList::setSpeakingIndicatorsVisible(bool visible)
  69. {
  70. // Save the new value for new items to use.
  71. mShowSpeakingIndicator = visible;
  72. // Show/hide icons for all existing items.
  73. std::vector<LLPanel*> items;
  74. getItems(items);
  75. for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
  76. {
  77. static_cast<LLAvatarListItem*>(*it)->showSpeakingIndicator(mShowSpeakingIndicator);
  78. }
  79. }
  80. void LLAvatarList::showPermissions(bool visible)
  81. {
  82. // Save the value for new items to use.
  83. mShowPermissions = visible;
  84. // Enable or disable showing permissions icons for all existing items.
  85. std::vector<LLPanel*> items;
  86. getItems(items);
  87. for(std::vector<LLPanel*>::const_iterator it = items.begin(), end_it = items.end(); it != end_it; ++it)
  88. {
  89. static_cast<LLAvatarListItem*>(*it)->setShowPermissions(mShowPermissions);
  90. }
  91. }
  92. static bool findInsensitive(std::string haystack, const std::string& needle_upper)
  93. {
  94. LLStringUtil::toUpper(haystack);
  95. return haystack.find(needle_upper) != std::string::npos;
  96. }
  97. //comparators
  98. static const LLAvatarItemNameComparator NAME_COMPARATOR;
  99. static const LLFlatListView::ItemReverseComparator REVERSE_NAME_COMPARATOR(NAME_COMPARATOR);
  100. LLAvatarList::Params::Params()
  101. : ignore_online_status("ignore_online_status", false)
  102. , show_last_interaction_time("show_last_interaction_time", false)
  103. , show_info_btn("show_info_btn", true)
  104. , show_profile_btn("show_profile_btn", true)
  105. , show_speaking_indicator("show_speaking_indicator", true)
  106. , show_permissions_granted("show_permissions_granted", false)
  107. {
  108. }
  109. LLAvatarList::LLAvatarList(const Params& p)
  110. : LLFlatListViewEx(p)
  111. , mIgnoreOnlineStatus(p.ignore_online_status)
  112. , mShowLastInteractionTime(p.show_last_interaction_time)
  113. , mContextMenu(NULL)
  114. , mDirty(true) // to force initial update
  115. , mNeedUpdateNames(false)
  116. , mLITUpdateTimer(NULL)
  117. , mShowIcons(true)
  118. , mShowInfoBtn(p.show_info_btn)
  119. , mShowProfileBtn(p.show_profile_btn)
  120. , mShowSpeakingIndicator(p.show_speaking_indicator)
  121. , mShowPermissions(p.show_permissions_granted)
  122. {
  123. setCommitOnSelectionChange(true);
  124. // Set default sort order.
  125. setComparator(&NAME_COMPARATOR);
  126. if (mShowLastInteractionTime)
  127. {
  128. mLITUpdateTimer = new LLTimer();
  129. mLITUpdateTimer->setTimerExpirySec(0); // zero to force initial update
  130. mLITUpdateTimer->start();
  131. }
  132. LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLAvatarList::handleDisplayNamesOptionChanged, this));
  133. }
  134. void LLAvatarList::handleDisplayNamesOptionChanged()
  135. {
  136. mNeedUpdateNames = true;
  137. }
  138. LLAvatarList::~LLAvatarList()
  139. {
  140. delete mLITUpdateTimer;
  141. }
  142. void LLAvatarList::setShowIcons(std::string param_name)
  143. {
  144. mIconParamName= param_name;
  145. mShowIcons = gSavedSettings.getBOOL(mIconParamName);
  146. }
  147. // virtual
  148. void LLAvatarList::draw()
  149. {
  150. // *NOTE dzaporozhan
  151. // Call refresh() after draw() to avoid flickering of avatar list items.
  152. LLFlatListViewEx::draw();
  153. if (mNeedUpdateNames)
  154. {
  155. updateAvatarNames();
  156. }
  157. if (mDirty)
  158. refresh();
  159. if (mShowLastInteractionTime && mLITUpdateTimer->hasExpired())
  160. {
  161. updateLastInteractionTimes();
  162. mLITUpdateTimer->setTimerExpirySec(LIT_UPDATE_PERIOD); // restart the timer
  163. }
  164. }
  165. //virtual
  166. void LLAvatarList::clear()
  167. {
  168. getIDs().clear();
  169. setDirty(true);
  170. LLFlatListViewEx::clear();
  171. }
  172. void LLAvatarList::setNameFilter(const std::string& filter)
  173. {
  174. std::string filter_upper = filter;
  175. LLStringUtil::toUpper(filter_upper);
  176. if (mNameFilter != filter_upper)
  177. {
  178. mNameFilter = filter_upper;
  179. // update message for empty state here instead of refresh() to avoid blinking when switch
  180. // between tabs.
  181. updateNoItemsMessage(filter);
  182. setDirty();
  183. }
  184. }
  185. void LLAvatarList::sortByName()
  186. {
  187. setComparator(&NAME_COMPARATOR);
  188. sort();
  189. }
  190. void LLAvatarList::setDirty(bool val /*= true*/, bool force_refresh /*= false*/)
  191. {
  192. mDirty = val;
  193. if(mDirty && force_refresh)
  194. {
  195. refresh();
  196. }
  197. }
  198. void LLAvatarList::addAvalineItem(const LLUUID& item_id, const LLUUID& session_id, const std::string& item_name)
  199. {
  200. LL_DEBUGS("Avaline") << "Adding avaline item into the list: " << item_name << "|" << item_id << ", session: " << session_id << LL_ENDL;
  201. LLAvalineListItem* item = new LLAvalineListItem(/*hide_number=*/false);
  202. item->setAvatarId(item_id, session_id, true, false);
  203. item->setName(item_name);
  204. item->showLastInteractionTime(mShowLastInteractionTime);
  205. item->showSpeakingIndicator(mShowSpeakingIndicator);
  206. item->setOnline(false);
  207. addItem(item, item_id);
  208. mIDs.push_back(item_id);
  209. sort();
  210. }
  211. //////////////////////////////////////////////////////////////////////////
  212. // PROTECTED SECTION
  213. //////////////////////////////////////////////////////////////////////////
  214. void LLAvatarList::refresh()
  215. {
  216. bool have_names = TRUE;
  217. bool add_limit_exceeded = false;
  218. bool modified = false;
  219. bool have_filter = !mNameFilter.empty();
  220. // Save selection.
  221. uuid_vec_t selected_ids;
  222. getSelectedUUIDs(selected_ids);
  223. LLUUID current_id = getSelectedUUID();
  224. // Determine what to add and what to remove.
  225. uuid_vec_t added, removed;
  226. LLAvatarList::computeDifference(getIDs(), added, removed);
  227. // Handle added items.
  228. unsigned nadded = 0;
  229. const std::string waiting_str = LLTrans::getString("AvatarNameWaiting");
  230. for (uuid_vec_t::const_iterator it=added.begin(); it != added.end(); it++)
  231. {
  232. const LLUUID& buddy_id = *it;
  233. LLAvatarName av_name;
  234. have_names &= LLAvatarNameCache::get(buddy_id, &av_name);
  235. if (!have_filter || findInsensitive(av_name.mDisplayName, mNameFilter))
  236. {
  237. if (nadded >= ADD_LIMIT)
  238. {
  239. add_limit_exceeded = true;
  240. break;
  241. }
  242. else
  243. {
  244. // *NOTE: If you change the UI to show a different string,
  245. // be sure to change the filter code below.
  246. if (LLRecentPeople::instance().isAvalineCaller(buddy_id))
  247. {
  248. const LLSD& call_data = LLRecentPeople::instance().getData(buddy_id);
  249. addAvalineItem(buddy_id, call_data["session_id"].asUUID(), call_data["call_number"].asString());
  250. }
  251. else
  252. {
  253. addNewItem(buddy_id,
  254. av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName,
  255. LLAvatarTracker::instance().isBuddyOnline(buddy_id));
  256. }
  257. modified = true;
  258. nadded++;
  259. }
  260. }
  261. }
  262. // Handle removed items.
  263. for (uuid_vec_t::const_iterator it=removed.begin(); it != removed.end(); it++)
  264. {
  265. removeItemByUUID(*it);
  266. modified = true;
  267. }
  268. // Handle filter.
  269. if (have_filter)
  270. {
  271. std::vector<LLSD> cur_values;
  272. getValues(cur_values);
  273. for (std::vector<LLSD>::const_iterator it=cur_values.begin(); it != cur_values.end(); it++)
  274. {
  275. const LLUUID& buddy_id = it->asUUID();
  276. LLAvatarName av_name;
  277. have_names &= LLAvatarNameCache::get(buddy_id, &av_name);
  278. if (!findInsensitive(av_name.mDisplayName, mNameFilter))
  279. {
  280. removeItemByUUID(buddy_id);
  281. modified = true;
  282. }
  283. }
  284. }
  285. // Changed item in place, need to request sort and update columns
  286. // because we might have changed data in a column on which the user
  287. // has already sorted. JC
  288. sort();
  289. // re-select items
  290. // selectMultiple(selected_ids); // TODO: implement in LLFlatListView if need
  291. selectItemByUUID(current_id);
  292. // If the name filter is specified and the names are incomplete,
  293. // we need to re-update when the names are complete so that
  294. // the filter can be applied correctly.
  295. //
  296. // Otherwise, if we have no filter then no need to update again
  297. // because the items will update their names.
  298. bool dirty = add_limit_exceeded || (have_filter && !have_names);
  299. setDirty(dirty);
  300. // Refreshed all items.
  301. if(!dirty)
  302. {
  303. // Highlight items matching the filter.
  304. std::vector<LLPanel*> items;
  305. getItems(items);
  306. for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
  307. {
  308. static_cast<LLAvatarListItem*>(*it)->setHighlight(mNameFilter);
  309. }
  310. // Send refresh_complete signal.
  311. mRefreshCompleteSignal(this, LLSD((S32)size(false)));
  312. }
  313. // Commit if we've added/removed items.
  314. if (modified)
  315. onCommit();
  316. }
  317. void LLAvatarList::updateAvatarNames()
  318. {
  319. std::vector<LLPanel*> items;
  320. getItems(items);
  321. for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
  322. {
  323. LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it);
  324. item->updateAvatarName();
  325. }
  326. mNeedUpdateNames = false;
  327. }
  328. bool LLAvatarList::filterHasMatches()
  329. {
  330. uuid_vec_t values = getIDs();
  331. for (uuid_vec_t::const_iterator it=values.begin(); it != values.end(); it++)
  332. {
  333. const LLUUID& buddy_id = *it;
  334. LLAvatarName av_name;
  335. bool have_name = LLAvatarNameCache::get(buddy_id, &av_name);
  336. // If name has not been loaded yet we consider it as a match.
  337. // When the name will be loaded the filter will be applied again(in refresh()).
  338. if (have_name && !findInsensitive(av_name.mDisplayName, mNameFilter))
  339. {
  340. continue;
  341. }
  342. return true;
  343. }
  344. return false;
  345. }
  346. boost::signals2::connection LLAvatarList::setRefreshCompleteCallback(const commit_signal_t::slot_type& cb)
  347. {
  348. return mRefreshCompleteSignal.connect(cb);
  349. }
  350. boost::signals2::connection LLAvatarList::setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb)
  351. {
  352. return mItemDoubleClickSignal.connect(cb);
  353. }
  354. //virtual
  355. S32 LLAvatarList::notifyParent(const LLSD& info)
  356. {
  357. if (info.has("sort") && &NAME_COMPARATOR == mItemComparator)
  358. {
  359. sort();
  360. return 1;
  361. }
  362. return LLFlatListViewEx::notifyParent(info);
  363. }
  364. void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos)
  365. {
  366. LLAvatarListItem* item = new LLAvatarListItem();
  367. // This sets the name as a side effect
  368. item->setAvatarId(id, mSessionID, mIgnoreOnlineStatus);
  369. item->setOnline(mIgnoreOnlineStatus ? true : is_online);
  370. item->showLastInteractionTime(mShowLastInteractionTime);
  371. item->setAvatarIconVisible(mShowIcons);
  372. item->setShowInfoBtn(mShowInfoBtn);
  373. item->setShowProfileBtn(mShowProfileBtn);
  374. item->showSpeakingIndicator(mShowSpeakingIndicator);
  375. item->setShowPermissions(mShowPermissions);
  376. item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoubleClicked, this, _1, _2, _3, _4));
  377. addItem(item, id, pos);
  378. }
  379. // virtual
  380. BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask)
  381. {
  382. BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask);
  383. if ( mContextMenu && !isAvalineItemSelected())
  384. {
  385. uuid_vec_t selected_uuids;
  386. getSelectedUUIDs(selected_uuids);
  387. mContextMenu->show(this, selected_uuids, x, y);
  388. }
  389. return handled;
  390. }
  391. bool LLAvatarList::isAvalineItemSelected()
  392. {
  393. std::vector<LLPanel*> selected_items;
  394. getSelectedItems(selected_items);
  395. std::vector<LLPanel*>::iterator it = selected_items.begin();
  396. for(; it != selected_items.end(); ++it)
  397. {
  398. if (dynamic_cast<LLAvalineListItem*>(*it))
  399. return true;
  400. }
  401. return false;
  402. }
  403. void LLAvatarList::setVisible(BOOL visible)
  404. {
  405. if ( visible == FALSE && mContextMenu )
  406. {
  407. mContextMenu->hide();
  408. }
  409. LLFlatListViewEx::setVisible(visible);
  410. }
  411. void LLAvatarList::computeDifference(
  412. const uuid_vec_t& vnew_unsorted,
  413. uuid_vec_t& vadded,
  414. uuid_vec_t& vremoved)
  415. {
  416. uuid_vec_t vcur;
  417. // Convert LLSDs to LLUUIDs.
  418. {
  419. std::vector<LLSD> vcur_values;
  420. getValues(vcur_values);
  421. for (size_t i=0; i<vcur_values.size(); i++)
  422. vcur.push_back(vcur_values[i].asUUID());
  423. }
  424. LLCommonUtils::computeDifference(vnew_unsorted, vcur, vadded, vremoved);
  425. }
  426. // Refresh shown time of our last interaction with all listed avatars.
  427. void LLAvatarList::updateLastInteractionTimes()
  428. {
  429. S32 now = (S32) LLDate::now().secondsSinceEpoch();
  430. std::vector<LLPanel*> items;
  431. getItems(items);
  432. for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
  433. {
  434. // *TODO: error handling
  435. LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it);
  436. S32 secs_since = now - (S32) LLRecentPeople::instance().getDate(item->getAvatarId()).secondsSinceEpoch();
  437. if (secs_since >= 0)
  438. item->setLastInteractionTime(secs_since);
  439. }
  440. }
  441. void LLAvatarList::onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
  442. {
  443. mItemDoubleClickSignal(ctrl, x, y, mask);
  444. }
  445. bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
  446. {
  447. const LLAvatarListItem* avatar_item1 = dynamic_cast<const LLAvatarListItem*>(item1);
  448. const LLAvatarListItem* avatar_item2 = dynamic_cast<const LLAvatarListItem*>(item2);
  449. if (!avatar_item1 || !avatar_item2)
  450. {
  451. llerror("item1 and item2 cannot be null", 0);
  452. return true;
  453. }
  454. return doCompare(avatar_item1, avatar_item2);
  455. }
  456. bool LLAvatarItemNameComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
  457. {
  458. std::string name1 = avatar_item1->getAvatarName();
  459. std::string name2 = avatar_item2->getAvatarName();
  460. LLStringUtil::toUpper(name1);
  461. LLStringUtil::toUpper(name2);
  462. return name1 < name2;
  463. }
  464. bool LLAvatarItemAgentOnTopComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
  465. {
  466. //keep agent on top, if first is agent,
  467. //then we need to return true to elevate this id, otherwise false.
  468. if(avatar_item1->getAvatarId() == gAgentID)
  469. {
  470. return true;
  471. }
  472. else if (avatar_item2->getAvatarId() == gAgentID)
  473. {
  474. return false;
  475. }
  476. return LLAvatarItemNameComparator::doCompare(avatar_item1,avatar_item2);
  477. }
  478. /************************************************************************/
  479. /* class LLAvalineListItem */
  480. /************************************************************************/
  481. LLAvalineListItem::LLAvalineListItem(bool hide_number/* = true*/) : LLAvatarListItem(false)
  482. , mIsHideNumber(hide_number)
  483. {
  484. // should not use buildPanel from the base class to ensure LLAvalineListItem::postBuild is called.
  485. buildFromFile( "panel_avatar_list_item.xml");
  486. }
  487. BOOL LLAvalineListItem::postBuild()
  488. {
  489. BOOL rv = LLAvatarListItem::postBuild();
  490. if (rv)
  491. {
  492. setOnline(true);
  493. showLastInteractionTime(false);
  494. setShowProfileBtn(false);
  495. setShowInfoBtn(false);
  496. mAvatarIcon->setValue("Avaline_Icon");
  497. mAvatarIcon->setToolTip(std::string(""));
  498. }
  499. return rv;
  500. }
  501. // to work correctly this method should be called AFTER setAvatarId for avaline callers with hidden phone number
  502. void LLAvalineListItem::setName(const std::string& name)
  503. {
  504. if (mIsHideNumber)
  505. {
  506. static U32 order = 0;
  507. typedef std::map<LLUUID, U32> avaline_callers_nums_t;
  508. static avaline_callers_nums_t mAvalineCallersNums;
  509. llassert(getAvatarId() != LLUUID::null);
  510. const LLUUID &uuid = getAvatarId();
  511. if (mAvalineCallersNums.find(uuid) == mAvalineCallersNums.end())
  512. {
  513. mAvalineCallersNums[uuid] = ++order;
  514. LL_DEBUGS("Avaline") << "Set name for new avaline caller: " << uuid << ", order: " << order << LL_ENDL;
  515. }
  516. LLStringUtil::format_map_t args;
  517. args["[ORDER]"] = llformat("%u", mAvalineCallersNums[uuid]);
  518. std::string hidden_name = LLTrans::getString("AvalineCaller", args);
  519. LL_DEBUGS("Avaline") << "Avaline caller: " << uuid << ", name: " << hidden_name << LL_ENDL;
  520. LLAvatarListItem::setAvatarName(hidden_name);
  521. LLAvatarListItem::setAvatarToolTip(hidden_name);
  522. }
  523. else
  524. {
  525. const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name);
  526. LLAvatarListItem::setAvatarName(formatted_phone);
  527. LLAvatarListItem::setAvatarToolTip(formatted_phone);
  528. }
  529. }