PageRenderTime 185ms CodeModel.GetById 2ms app.highlight 148ms RepoModel.GetById 15ms app.codeStats 0ms

/indra/newview/llfloaterfriends.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 807 lines | 613 code | 111 blank | 83 comment | 94 complexity | 41cfc4f332c2507dabec6140e5882c5a MD5 | raw file
  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=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29
 30#include "llviewerprecompiledheaders.h"
 31
 32#include "llfloaterfriends.h"
 33
 34#include <sstream>
 35
 36#include "lldir.h"
 37
 38#include "llagent.h"
 39#include "llappviewer.h"	// for gLastVersionChannel
 40#include "llfloateravatarpicker.h"
 41#include "llviewerwindow.h"
 42#include "llbutton.h"
 43#include "llavataractions.h"
 44#include "llinventorymodel.h"
 45#include "llnamelistctrl.h"
 46#include "llnotificationsutil.h"
 47#include "llresmgr.h"
 48#include "llscrolllistctrl.h"
 49#include "llscrolllistitem.h"
 50#include "llscrolllistcell.h"
 51#include "lluictrlfactory.h"
 52#include "llmenucommands.h"
 53#include "llviewercontrol.h"
 54#include "llviewermessage.h"
 55#include "lleventtimer.h"
 56#include "lltextbox.h"
 57#include "llvoiceclient.h"
 58
 59// *TODO: Move more common stuff to LLAvatarActions?
 60
 61//Maximum number of people you can select to do an operation on at once.
 62#define MAX_FRIEND_SELECT 20
 63#define DEFAULT_PERIOD 5.0
 64#define RIGHTS_CHANGE_TIMEOUT 5.0
 65#define OBSERVER_TIMEOUT 0.5
 66
 67#define ONLINE_SIP_ICON_NAME "slim_icon_16_viewer.tga"
 68
 69// simple class to observe the calling cards.
 70class LLLocalFriendsObserver : public LLFriendObserver, public LLEventTimer
 71{
 72public: 
 73	LLLocalFriendsObserver(LLPanelFriends* floater) : mFloater(floater), LLEventTimer(OBSERVER_TIMEOUT)
 74	{
 75		mEventTimer.stop();
 76	}
 77	virtual ~LLLocalFriendsObserver()
 78	{
 79		mFloater = NULL;
 80	}
 81	virtual void changed(U32 mask)
 82	{
 83		// events can arrive quickly in bulk - we need not process EVERY one of them -
 84		// so we wait a short while to let others pile-in, and process them in aggregate.
 85		mEventTimer.start();
 86
 87		// save-up all the mask-bits which have come-in
 88		mMask |= mask;
 89	}
 90	virtual BOOL tick()
 91	{
 92		mFloater->updateFriends(mMask);
 93
 94		mEventTimer.stop();
 95		mMask = 0;
 96
 97		return FALSE;
 98	}
 99	
100protected:
101	LLPanelFriends* mFloater;
102	U32 mMask;
103};
104
105LLPanelFriends::LLPanelFriends() :
106	LLPanel(),
107	LLEventTimer(DEFAULT_PERIOD),
108	mObserver(NULL),
109	mShowMaxSelectWarning(TRUE),
110	mAllowRightsChange(TRUE),
111	mNumRightsChanged(0)
112{
113	mEventTimer.stop();
114	mObserver = new LLLocalFriendsObserver(this);
115	LLAvatarTracker::instance().addObserver(mObserver);
116	// For notification when SIP online status changes.
117	LLVoiceClient::getInstance()->addObserver(mObserver);
118}
119
120LLPanelFriends::~LLPanelFriends()
121{
122	// For notification when SIP online status changes.
123	LLVoiceClient::getInstance()->removeObserver(mObserver);
124	LLAvatarTracker::instance().removeObserver(mObserver);
125	delete mObserver;
126}
127
128BOOL LLPanelFriends::tick()
129{
130	mEventTimer.stop();
131	mPeriod = DEFAULT_PERIOD;
132	mAllowRightsChange = TRUE;
133	updateFriends(LLFriendObserver::ADD);
134	return FALSE;
135}
136
137void LLPanelFriends::updateFriends(U32 changed_mask)
138{
139	LLUUID selected_id;
140	LLCtrlListInterface *friends_list = childGetListInterface("friend_list");
141	if (!friends_list) return;
142	LLCtrlScrollInterface *friends_scroll = childGetScrollInterface("friend_list");
143	if (!friends_scroll) return;
144	
145	// We kill the selection warning, otherwise we'll spam with warning popups
146	// if the maximum amount of friends are selected
147	mShowMaxSelectWarning = false;
148
149	std::vector<LLUUID> selected_friends = getSelectedIDs();
150	if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
151	{
152		refreshNames(changed_mask);
153	}
154	else if(changed_mask & LLFriendObserver::POWERS)
155	{
156		--mNumRightsChanged;
157		if(mNumRightsChanged > 0)
158		{
159			mPeriod = RIGHTS_CHANGE_TIMEOUT;	
160			mEventTimer.start();
161			mAllowRightsChange = FALSE;
162		}
163		else
164		{
165			tick();
166		}
167	}
168	if(selected_friends.size() > 0)
169	{
170		// only non-null if friends was already found. This may fail,
171		// but we don't really care here, because refreshUI() will
172		// clean up the interface.
173		friends_list->setCurrentByID(selected_id);
174		for(std::vector<LLUUID>::iterator itr = selected_friends.begin(); itr != selected_friends.end(); ++itr)
175		{
176			friends_list->setSelectedByValue(*itr, true);
177		}
178	}
179
180	refreshUI();
181	mShowMaxSelectWarning = true;
182}
183
184// virtual
185BOOL LLPanelFriends::postBuild()
186{
187	mFriendsList = getChild<LLScrollListCtrl>("friend_list");
188	mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
189	mFriendsList->setMaximumSelectCallback(boost::bind(&LLPanelFriends::onMaximumSelect));
190	mFriendsList->setCommitOnSelectionChange(TRUE);
191	mFriendsList->setContextMenu(LLScrollListCtrl::MENU_AVATAR);
192	childSetCommitCallback("friend_list", onSelectName, this);
193	getChild<LLScrollListCtrl>("friend_list")->setDoubleClickCallback(onClickIM, this);
194
195	U32 changed_mask = LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE;
196	refreshNames(changed_mask);
197
198	childSetAction("im_btn", onClickIM, this);
199	childSetAction("profile_btn", onClickProfile, this);
200	childSetAction("offer_teleport_btn", onClickOfferTeleport, this);
201	childSetAction("pay_btn", onClickPay, this);
202	childSetAction("add_btn", onClickAddFriend, this);
203	childSetAction("remove_btn", onClickRemove, this);
204
205	setDefaultBtn("im_btn");
206
207	updateFriends(LLFriendObserver::ADD);
208	refreshUI();
209
210	// primary sort = online status, secondary sort = name
211	mFriendsList->sortByColumn(std::string("friend_name"), TRUE);
212	mFriendsList->sortByColumn(std::string("icon_online_status"), FALSE);
213
214	return TRUE;
215}
216
217BOOL LLPanelFriends::addFriend(const LLUUID& agent_id)
218{
219	LLAvatarTracker& at = LLAvatarTracker::instance();
220	const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
221	if(!relationInfo) return FALSE;
222
223	bool isOnlineSIP = LLVoiceClient::getInstance()->isOnlineSIP(agent_id);
224	bool isOnline = relationInfo->isOnline();
225
226	std::string fullname;
227	BOOL have_name = gCacheName->getFullName(agent_id, fullname);
228	
229	LLSD element;
230	element["id"] = agent_id;
231	LLSD& friend_column = element["columns"][LIST_FRIEND_NAME];
232	friend_column["column"] = "friend_name";
233	friend_column["value"] = fullname;
234	friend_column["font"]["name"] = "SANSSERIF";
235	friend_column["font"]["style"] = "NORMAL";	
236
237	LLSD& online_status_column = element["columns"][LIST_ONLINE_STATUS];
238	online_status_column["column"] = "icon_online_status";
239	online_status_column["type"] = "icon";
240	
241	if (isOnline)
242	{
243		friend_column["font"]["style"] = "BOLD";	
244		online_status_column["value"] = "icon_avatar_online.tga";
245	}
246	else if(isOnlineSIP)
247	{
248		friend_column["font"]["style"] = "BOLD";	
249		online_status_column["value"] = ONLINE_SIP_ICON_NAME;
250	}
251
252	LLSD& online_column = element["columns"][LIST_VISIBLE_ONLINE];
253	online_column["column"] = "icon_visible_online";
254	online_column["type"] = "checkbox";
255	online_column["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS);
256
257	LLSD& visible_map_column = element["columns"][LIST_VISIBLE_MAP];
258	visible_map_column["column"] = "icon_visible_map";
259	visible_map_column["type"] = "checkbox";
260	visible_map_column["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION);
261
262	LLSD& edit_my_object_column = element["columns"][LIST_EDIT_MINE];
263	edit_my_object_column["column"] = "icon_edit_mine";
264	edit_my_object_column["type"] = "checkbox";
265	edit_my_object_column["value"] = relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
266
267	LLSD& edit_their_object_column = element["columns"][LIST_EDIT_THEIRS];
268	edit_their_object_column["column"] = "icon_edit_theirs";
269	edit_their_object_column["type"] = "checkbox";
270	edit_their_object_column["enabled"] = "";
271	edit_their_object_column["value"] = relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS);
272
273	LLSD& update_gen_column = element["columns"][LIST_FRIEND_UPDATE_GEN];
274	update_gen_column["column"] = "friend_last_update_generation";
275	update_gen_column["value"] = have_name ? relationInfo->getChangeSerialNum() : -1;
276
277	mFriendsList->addElement(element, ADD_BOTTOM);
278	return have_name;
279}
280
281// propagate actual relationship to UI.
282// Does not resort the UI list because it can be called frequently. JC
283BOOL LLPanelFriends::updateFriendItem(const LLUUID& agent_id, const LLRelationship* info)
284{
285	if (!info) return FALSE;
286	LLScrollListItem* itemp = mFriendsList->getItem(agent_id);
287	if (!itemp) return FALSE;
288	
289	bool isOnlineSIP = LLVoiceClient::getInstance()->isOnlineSIP(itemp->getUUID());
290	bool isOnline = info->isOnline();
291
292	std::string fullname;
293	BOOL have_name = gCacheName->getFullName(agent_id, fullname);
294	
295	// Name of the status icon to use
296	std::string statusIcon;
297	
298	if(isOnline)
299	{
300		statusIcon = "icon_avatar_online.tga";
301	}
302	else if(isOnlineSIP)
303	{
304		statusIcon = ONLINE_SIP_ICON_NAME;
305	}
306
307	itemp->getColumn(LIST_ONLINE_STATUS)->setValue(statusIcon);
308	
309	itemp->getColumn(LIST_FRIEND_NAME)->setValue(fullname);
310	// render name of online friends in bold text
311	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME))->setFontStyle((isOnline || isOnlineSIP) ? LLFontGL::BOLD : LLFontGL::NORMAL);	
312	itemp->getColumn(LIST_VISIBLE_ONLINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS));
313	itemp->getColumn(LIST_VISIBLE_MAP)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION));
314	itemp->getColumn(LIST_EDIT_MINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
315	S32 change_generation = have_name ? info->getChangeSerialNum() : -1;
316	itemp->getColumn(LIST_FRIEND_UPDATE_GEN)->setValue(change_generation);
317
318	// enable this item, in case it was disabled after user input
319	itemp->setEnabled(TRUE);
320
321	// Do not resort, this function can be called frequently.
322	return have_name;
323}
324
325void LLPanelFriends::refreshRightsChangeList()
326{
327	std::vector<LLUUID> friends = getSelectedIDs();
328	S32 num_selected = friends.size();
329
330	bool can_offer_teleport = num_selected >= 1;
331	bool selected_friends_online = true;
332
333	const LLRelationship* friend_status = NULL;
334	for(std::vector<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
335	{
336		friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
337		if (friend_status)
338		{
339			if(!friend_status->isOnline())
340			{
341				can_offer_teleport = false;
342				selected_friends_online = false;
343			}
344		}
345		else // missing buddy info, don't allow any operations
346		{
347			can_offer_teleport = false;
348		}
349	}
350	
351	if (num_selected == 0)  // nothing selected
352	{
353		childSetEnabled("im_btn", FALSE);
354		childSetEnabled("offer_teleport_btn", FALSE);
355	}
356	else // we have at least one friend selected...
357	{
358		// only allow IMs to groups when everyone in the group is online
359		// to be consistent with context menus in inventory and because otherwise
360		// offline friends would be silently dropped from the session
361		childSetEnabled("im_btn", selected_friends_online || num_selected == 1);
362		childSetEnabled("offer_teleport_btn", can_offer_teleport);
363	}
364}
365
366struct SortFriendsByID
367{
368	bool operator() (const LLScrollListItem* const a, const LLScrollListItem* const b) const
369	{
370		return a->getValue().asUUID() < b->getValue().asUUID();
371	}
372};
373
374void LLPanelFriends::refreshNames(U32 changed_mask)
375{
376	std::vector<LLUUID> selected_ids = getSelectedIDs();	
377	S32 pos = mFriendsList->getScrollPos();	
378	
379	// get all buddies we know about
380	LLAvatarTracker::buddy_map_t all_buddies;
381	LLAvatarTracker::instance().copyBuddyList(all_buddies);
382
383	BOOL have_names = TRUE;
384
385	if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
386	{
387		have_names &= refreshNamesSync(all_buddies);
388	}
389
390	if(changed_mask & LLFriendObserver::ONLINE)
391	{
392		have_names &= refreshNamesPresence(all_buddies);
393	}
394
395	if (!have_names)
396	{
397		mEventTimer.start();
398	}
399	// Changed item in place, need to request sort and update columns
400	// because we might have changed data in a column on which the user
401	// has already sorted. JC
402	mFriendsList->updateSort();
403
404	// re-select items
405	mFriendsList->selectMultiple(selected_ids);
406	mFriendsList->setScrollPos(pos);
407}
408
409BOOL LLPanelFriends::refreshNamesSync(const LLAvatarTracker::buddy_map_t & all_buddies)
410{
411	mFriendsList->deleteAllItems();
412
413	BOOL have_names = TRUE;
414	LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin();
415
416	for(; buddy_it != all_buddies.end(); ++buddy_it)
417	{
418		have_names &= addFriend(buddy_it->first);
419	}
420
421	return have_names;
422}
423
424BOOL LLPanelFriends::refreshNamesPresence(const LLAvatarTracker::buddy_map_t & all_buddies)
425{
426	std::vector<LLScrollListItem*> items = mFriendsList->getAllData();
427	std::sort(items.begin(), items.end(), SortFriendsByID());
428
429	LLAvatarTracker::buddy_map_t::const_iterator buddy_it  = all_buddies.begin();
430	std::vector<LLScrollListItem*>::const_iterator item_it = items.begin();
431	BOOL have_names = TRUE;
432
433	while(true)
434	{
435		if(item_it == items.end() || buddy_it == all_buddies.end())
436		{
437			break;
438		}
439
440		const LLUUID & buddy_uuid = buddy_it->first;
441		const LLUUID & item_uuid  = (*item_it)->getValue().asUUID();
442		if(item_uuid == buddy_uuid)
443		{
444			const LLRelationship* info = buddy_it->second;
445			if (!info) 
446			{	
447				++item_it;
448				continue;
449			}
450			
451			S32 last_change_generation = (*item_it)->getColumn(LIST_FRIEND_UPDATE_GEN)->getValue().asInteger();
452			if (last_change_generation < info->getChangeSerialNum())
453			{
454				// update existing item in UI
455				have_names &= updateFriendItem(buddy_it->first, info);
456			}
457
458			++buddy_it;
459			++item_it;
460		}
461		else if(item_uuid < buddy_uuid)
462		{
463			++item_it;
464		}
465		else //if(item_uuid > buddy_uuid)
466		{
467			++buddy_it;
468		}
469	}
470
471	return have_names;
472}
473
474void LLPanelFriends::refreshUI()
475{	
476	BOOL single_selected = FALSE;
477	BOOL multiple_selected = FALSE;
478	int num_selected = mFriendsList->getAllSelected().size();
479	if(num_selected > 0)
480	{
481		single_selected = TRUE;
482		if(num_selected > 1)
483		{
484			multiple_selected = TRUE;		
485		}
486	}
487
488
489	//Options that can only be performed with one friend selected
490	childSetEnabled("profile_btn", single_selected && !multiple_selected);
491	childSetEnabled("pay_btn", single_selected && !multiple_selected);
492
493	//Options that can be performed with up to MAX_FRIEND_SELECT friends selected
494	//(single_selected will always be true in this situations)
495	childSetEnabled("remove_btn", single_selected);
496	childSetEnabled("im_btn", single_selected);
497//	childSetEnabled("friend_rights", single_selected);
498
499	refreshRightsChangeList();
500}
501
502std::vector<LLUUID> LLPanelFriends::getSelectedIDs()
503{
504	LLUUID selected_id;
505	std::vector<LLUUID> friend_ids;
506	std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected();
507	for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
508	{
509		friend_ids.push_back((*itr)->getUUID());
510	}
511	return friend_ids;
512}
513
514// static
515void LLPanelFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
516{
517	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
518
519	if(panelp)
520	{
521		panelp->refreshUI();
522		// check to see if rights have changed
523		panelp->applyRightsToFriends();
524	}
525}
526
527//static
528void LLPanelFriends::onMaximumSelect()
529{
530	LLSD args;
531	args["MAX_SELECT"] = llformat("%d", MAX_FRIEND_SELECT);
532	LLNotificationsUtil::add("MaxListSelectMessage", args);
533};
534
535// static
536void LLPanelFriends::onClickProfile(void* user_data)
537{
538	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
539
540	std::vector<LLUUID> ids = panelp->getSelectedIDs();
541	if(ids.size() > 0)
542	{
543		LLUUID agent_id = ids[0];
544		LLAvatarActions::showProfile(agent_id);
545	}
546}
547
548// static
549void LLPanelFriends::onClickIM(void* user_data)
550{
551	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
552
553	std::vector<LLUUID> ids = panelp->getSelectedIDs();
554	if(ids.size() > 0)
555	{
556		if(ids.size() == 1)
557		{
558			LLAvatarActions::startIM(ids[0]);
559		}
560		else
561		{
562			LLAvatarActions::startConference(ids);
563		}
564	}
565}
566
567// static
568void LLPanelFriends::onPickAvatar(const std::vector<std::string>& names,
569									const std::vector<LLUUID>& ids)
570{
571	if (names.empty()) return;
572	if (ids.empty()) return;
573	LLAvatarActions::requestFriendshipDialog(ids[0], names[0]);
574}
575
576// static
577void LLPanelFriends::onClickAddFriend(void* user_data)
578{
579	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
580	LLFloater* root_floater = gFloaterView->getParentFloater(panelp);
581	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelFriends::onPickAvatar, _1,_2), FALSE, TRUE);
582	if (root_floater)
583	{
584		root_floater->addDependentFloater(picker);
585	}
586}
587
588// static
589void LLPanelFriends::onClickRemove(void* user_data)
590{
591	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
592	LLAvatarActions::removeFriendsDialog(panelp->getSelectedIDs());
593}
594
595// static
596void LLPanelFriends::onClickOfferTeleport(void* user_data)
597{
598	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
599	LLAvatarActions::offerTeleport(panelp->getSelectedIDs());
600}
601
602// static
603void LLPanelFriends::onClickPay(void* user_data)
604{
605	LLPanelFriends* panelp = (LLPanelFriends*)user_data;
606
607	std::vector<LLUUID> ids = panelp->getSelectedIDs();
608	if(ids.size() == 1)
609	{	
610		LLAvatarActions::pay(ids[0]);
611	}
612}
613
614void LLPanelFriends::confirmModifyRights(rights_map_t& ids, EGrantRevoke command)
615{
616	if (ids.empty()) return;
617	
618	LLSD args;
619	if(ids.size() > 0)
620	{
621		rights_map_t* rights = new rights_map_t(ids);
622
623		// for single friend, show their name
624		if(ids.size() == 1)
625		{
626			LLUUID agent_id = ids.begin()->first;
627			std::string first, last;
628			if(gCacheName->getName(agent_id, first, last))
629			{
630				args["FIRST_NAME"] = first;
631				args["LAST_NAME"] = last;	
632			}
633			if (command == GRANT)
634			{
635				LLNotificationsUtil::add("GrantModifyRights", 
636					args, 
637					LLSD(), 
638					boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights));
639			}
640			else
641			{
642				LLNotificationsUtil::add("RevokeModifyRights", 
643					args, 
644					LLSD(), 
645					boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights));
646			}
647		}
648		else
649		{
650			if (command == GRANT)
651			{
652				LLNotificationsUtil::add("GrantModifyRightsMultiple", 
653					args, 
654					LLSD(), 
655					boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights));
656			}
657			else
658			{
659				LLNotificationsUtil::add("RevokeModifyRightsMultiple", 
660					args, 
661					LLSD(), 
662					boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights));
663			}
664		}
665	}
666}
667
668bool LLPanelFriends::modifyRightsConfirmation(const LLSD& notification, const LLSD& response, rights_map_t* rights)
669{
670	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
671	if(0 == option)
672	{
673		sendRightsGrant(*rights);
674	}
675	else
676	{
677		// need to resync view with model, since user cancelled operation
678		rights_map_t::iterator rights_it;
679		for (rights_it = rights->begin(); rights_it != rights->end(); ++rights_it)
680		{
681			const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(rights_it->first);
682			updateFriendItem(rights_it->first, info);
683		}
684	}
685	refreshUI();
686
687	delete rights;
688	return false;
689}
690
691void LLPanelFriends::applyRightsToFriends()
692{
693	BOOL rights_changed = FALSE;
694
695	// store modify rights separately for confirmation
696	rights_map_t rights_updates;
697
698	BOOL need_confirmation = FALSE;
699	EGrantRevoke confirmation_type = GRANT;
700
701	// this assumes that changes only happened to selected items
702	std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected();
703	for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
704	{
705		LLUUID id = (*itr)->getValue();
706		const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(id);
707		if (buddy_relationship == NULL) continue;
708
709		bool show_online_staus = (*itr)->getColumn(LIST_VISIBLE_ONLINE)->getValue().asBoolean();
710		bool show_map_location = (*itr)->getColumn(LIST_VISIBLE_MAP)->getValue().asBoolean();
711		bool allow_modify_objects = (*itr)->getColumn(LIST_EDIT_MINE)->getValue().asBoolean();
712
713		S32 rights = buddy_relationship->getRightsGrantedTo();
714		if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) != show_online_staus)
715		{
716			rights_changed = TRUE;
717			if(show_online_staus) 
718			{
719				rights |= LLRelationship::GRANT_ONLINE_STATUS;
720			}
721			else 
722			{
723				// ONLINE_STATUS necessary for MAP_LOCATION
724				rights &= ~LLRelationship::GRANT_ONLINE_STATUS;
725				rights &= ~LLRelationship::GRANT_MAP_LOCATION;
726				// propagate rights constraint to UI
727				(*itr)->getColumn(LIST_VISIBLE_MAP)->setValue(FALSE);
728			}
729		}
730		if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) != show_map_location)
731		{
732			rights_changed = TRUE;
733			if(show_map_location) 
734			{
735				// ONLINE_STATUS necessary for MAP_LOCATION
736				rights |= LLRelationship::GRANT_MAP_LOCATION;
737				rights |= LLRelationship::GRANT_ONLINE_STATUS;
738				(*itr)->getColumn(LIST_VISIBLE_ONLINE)->setValue(TRUE);
739			}
740			else 
741			{
742				rights &= ~LLRelationship::GRANT_MAP_LOCATION;
743			}
744		}
745		
746		// now check for change in modify object rights, which requires confirmation
747		if(buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)
748		{
749			rights_changed = TRUE;
750			need_confirmation = TRUE;
751
752			if(allow_modify_objects)
753			{
754				rights |= LLRelationship::GRANT_MODIFY_OBJECTS;
755				confirmation_type = GRANT;
756			}
757			else
758			{
759				rights &= ~LLRelationship::GRANT_MODIFY_OBJECTS;
760				confirmation_type = REVOKE;
761			}
762		}
763
764		if (rights_changed)
765		{
766			rights_updates.insert(std::make_pair(id, rights));
767			// disable these ui elements until response from server
768			// to avoid race conditions
769			(*itr)->setEnabled(FALSE);
770		}
771	}
772
773	// separately confirm grant and revoke of modify rights
774	if (need_confirmation)
775	{
776		confirmModifyRights(rights_updates, confirmation_type);
777	}
778	else
779	{
780		sendRightsGrant(rights_updates);
781	}
782}
783
784void LLPanelFriends::sendRightsGrant(rights_map_t& ids)
785{
786	if (ids.empty()) return;
787
788	LLMessageSystem* msg = gMessageSystem;
789
790	// setup message header
791	msg->newMessageFast(_PREHASH_GrantUserRights);
792	msg->nextBlockFast(_PREHASH_AgentData);
793	msg->addUUID(_PREHASH_AgentID, gAgent.getID());
794	msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
795
796	rights_map_t::iterator id_it;
797	rights_map_t::iterator end_it = ids.end();
798	for(id_it = ids.begin(); id_it != end_it; ++id_it)
799	{
800		msg->nextBlockFast(_PREHASH_Rights);
801		msg->addUUID(_PREHASH_AgentRelated, id_it->first);
802		msg->addS32(_PREHASH_RelatedRights, id_it->second);
803	}
804
805	mNumRightsChanged = ids.size();
806	gAgent.sendReliableMessage();
807}