PageRenderTime 760ms CodeModel.GetById 112ms app.highlight 440ms RepoModel.GetById 124ms app.codeStats 40ms

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