/** 
 * @file llpanelgrouproles.cpp
 * @brief Panel for roles information about a particular group.
 *
 * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include "llcheckboxctrl.h"

#include "llagent.h"
#include "llbutton.h"
#include "llfloateravatarinfo.h"
#include "llfloatergroupinvite.h"
#include "lliconctrl.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
#include "llnotify.h"
#include "llpanelgrouproles.h"
#include "llscrolllistctrl.h"
#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltexteditor.h"
#include "llviewerimagelist.h"
#include "llviewerwindow.h"
#include "llfocusmgr.h"

#include "roles_constants.h"

bool agentCanRemoveFromRole(const LLUUID& group_id,
							const LLUUID& role_id)
{
	return gAgent.hasPowerInGroup(group_id, GP_ROLE_REMOVE_MEMBER);
}

bool agentCanAddToRole(const LLUUID& group_id,
					   const LLUUID& role_id)
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(group_id);
	if (!gdatap) 
	{
		llwarns << "agentCanAddToRole "
				<< "-- No group data!" << llendl;
		return false;
	}

	//make sure the agent is in the group
	LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
	if (mi == gdatap->mMembers.end())
	{
		return false;
	}
	
	LLGroupMemberData* member_data = (*mi).second;

	// Owners can add to any role.
	if ( member_data->isInRole(gdatap->mOwnerRole) )
	{
		return true;
	}

	// 'Limited assign members' can add to roles the user is in.
	if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER_LIMITED) &&
			member_data->isInRole(role_id) )
	{
		return true;
	}

	// 'assign members' can add to non-owner roles.
	if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER) &&
			 role_id != gdatap->mOwnerRole )
	{
		return true;
	}

	return false;
}

// static
void* LLPanelGroupRoles::createTab(void* data)
{
	LLUUID* group_id = static_cast<LLUUID*>(data);
	return new LLPanelGroupRoles("panel group roles", *group_id);
}

LLPanelGroupRoles::LLPanelGroupRoles(const std::string& name, const LLUUID& group_id)
:	LLPanelGroupTab(name, group_id),
	mCurrentTab(NULL),
	mRequestedTab( NULL ),
	mSubTabContainer( NULL ),
	mFirstUse( TRUE ),
	mIgnoreTransition( FALSE )
{
}

LLPanelGroupRoles::~LLPanelGroupRoles()
{
	int i;
	for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
	{
		LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);

		subtabp->removeObserver(this);
	}
}

BOOL LLPanelGroupRoles::postBuild()
{
	lldebugs << "LLPanelGroupRoles::postBuild()" << llendl;

	mSubTabContainer = (LLTabContainerCommon*) getChildByName("roles_tab_container");

	if (!mSubTabContainer) return FALSE;

	// Hook up each sub-tabs callback and widgets.
	S32 i;
	for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
	{
		LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);

		// Add click callbacks to all the tabs.
		mSubTabContainer->setTabChangeCallback(subtabp, onClickSubTab);
		mSubTabContainer->setTabUserData(subtabp, this);

		// Hand the subtab a pointer to this LLPanelGroupRoles, so that it can
		// look around for the widgets it is interested in.
		if (!subtabp->postBuildSubTab(this)) return FALSE;

		subtabp->addObserver(this);
	}

	// Set the current tab to whatever is currently being shown.
	mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (!mCurrentTab)
	{
		// Need to select a tab.
		mSubTabContainer->selectFirstTab();
		mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	}

	if (!mCurrentTab) return FALSE;

	// Act as though this tab was just activated.
	mCurrentTab->activate();

	// Read apply text from the xml file.
	LLTextBox* txt;
	// Don't recurse for this, since we don't currently have a recursive removeChild()
	txt = (LLTextBox*)getChildByName("default_needs_apply_text");
	if (txt)
	{
		mDefaultNeedsApplyMesg = txt->getText();
		removeChild(txt);
	}
	txt = (LLTextBox*)getChildByName("want_apply_text");
	if (txt)
	{
		mWantApplyMesg = txt->getText();
		removeChild(txt);
	}

	return LLPanelGroupTab::postBuild();
}

BOOL LLPanelGroupRoles::isVisibleByAgent(LLAgent* agentp)
{
	/* This power was removed to make group roles simpler
	return agentp->hasPowerInGroup(mGroupID, 
								   GP_ROLE_CREATE |
								   GP_ROLE_DELETE |
								   GP_ROLE_PROPERTIES |
								   GP_ROLE_VIEW |
								   GP_ROLE_ASSIGN_MEMBER |
								   GP_ROLE_REMOVE_MEMBER |
								   GP_ROLE_CHANGE_ACTIONS |
								   GP_MEMBER_INVITE |
								   GP_MEMBER_EJECT |
								   GP_MEMBER_OPTIONS );
	*/
	return mAllowEdit && agentp->isInGroup(mGroupID);
								   
}

// static
void LLPanelGroupRoles::onClickSubTab(void* user_data, bool from_click)
{
	LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
	self->handleClickSubTab();
}

void LLPanelGroupRoles::handleClickSubTab()
{
	// If we are already handling a transition,
	// ignore this.
	if (mIgnoreTransition)
	{
		return;
	}

	mRequestedTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();

	// Make sure they aren't just clicking the same tab...
	if (mRequestedTab == mCurrentTab)
	{
		return;
	}

	// Try to switch from the current panel to the panel the user selected.
	attemptTransition();
}

BOOL LLPanelGroupRoles::attemptTransition()
{
	// Check if the current tab needs to be applied.
	LLString mesg;
	if (mCurrentTab && mCurrentTab->needsApply(mesg))
	{
		// If no message was provided, give a generic one.
		if (mesg.empty())
		{
			mesg = mDefaultNeedsApplyMesg;
		}
		// Create a notify box, telling the user about the unapplied tab.
		LLString::format_map_t args;
		args["[NEEDS_APPLY_MESSAGE]"] = mesg;
		args["[WANT_APPLY_MESSAGE]"] = mWantApplyMesg;
		gViewerWindow->alertXml("PanelGroupApply", args,
								onNotifyCallback, (void*) this);
		mHasModal = TRUE;
		// We need to reselect the current tab, since it isn't finished.
		if (mSubTabContainer)
		{
			mIgnoreTransition = TRUE;
			mSubTabContainer->selectTabPanel( mCurrentTab );
			mIgnoreTransition = FALSE;
		}
		// Returning FALSE will block a close action from finishing until
		// we get a response back from the user.
		return FALSE;
	}
	else
	{
		// The current panel didn't have anything it needed to apply.
		if (mRequestedTab)
		{
			transitionToTab();
		}
		return TRUE;
	}
}

void LLPanelGroupRoles::transitionToTab()
{
	// Tell the current panel that it is being deactivated.
	if (mCurrentTab)
	{
		mCurrentTab->deactivate();
	}
	
	// Tell the new panel that it is being activated.
	if (mRequestedTab)
	{
		// This is now the current tab;
		mCurrentTab = mRequestedTab;
		mCurrentTab->activate();
	}
}

// static
void LLPanelGroupRoles::onNotifyCallback(S32 option, void* user_data)
{
	LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
	if (self)
	{
		self->handleNotifyCallback(option);
	}
}

void LLPanelGroupRoles::handleNotifyCallback(S32 option)
{
	mHasModal = FALSE;
	switch (option)
	{
	case 0: // "Apply Changes"
	{
		// Try to apply changes, and switch to the requested tab.
		LLString apply_mesg;
		if ( !apply( apply_mesg ) )
		{
			// There was a problem doing the apply.
			if ( !apply_mesg.empty() )
			{
				mHasModal = TRUE;
				LLString::format_map_t args;
				args["[MESSAGE]"] = apply_mesg;
				gViewerWindow->alertXml("GenericAlert", args, onModalClose, (void*) this);
			}
			// Skip switching tabs.
			break;
		}

		// This panel's info successfully applied.
		// Switch to the next panel.
		// No break!  Continue into 'Ignore Changes' which just switches tabs.
		mIgnoreTransition = TRUE;
		mSubTabContainer->selectTabPanel( mRequestedTab );
		mIgnoreTransition = FALSE;
		transitionToTab();
		break;
	}
	case 1: // "Ignore Changes"
		// Switch to the requested panel without applying changes
		cancel();
		mIgnoreTransition = TRUE;
		mSubTabContainer->selectTabPanel( mRequestedTab );
		mIgnoreTransition = FALSE;
		transitionToTab();
		break;
	case 2: // "Cancel"
	default:
		// Do nothing.  The user is canceling the action.
		break;
	}
}

// static
void LLPanelGroupRoles::onModalClose(S32 option, void* user_data)
{
	LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
	if (self)
	{
		self->mHasModal = FALSE;
	}
}


bool LLPanelGroupRoles::apply(LLString& mesg)
{
	// Pass this along to the currently visible sub tab.
	if (!mSubTabContainer) return false;

	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (!panelp) return false;
	
	// Ignore the needs apply message.
	LLString ignore_mesg;
	if ( !panelp->needsApply(ignore_mesg) )
	{
		// We don't need to apply anything.
		// We're done.
		return true;
	}

	// Try to do the actual apply.
	return panelp->apply(mesg);
}

void LLPanelGroupRoles::cancel()
{
	// Pass this along to the currently visible sub tab.
	if (!mSubTabContainer) return;

	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (!panelp) return;

	panelp->cancel();
}

// Pass all of these messages to the currently visible sub tab.
LLString LLPanelGroupRoles::getHelpText() const
{
	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (panelp)
	{
		return panelp->getHelpText();
	}
	else
	{
		return mHelpText;
	}
}

void LLPanelGroupRoles::update(LLGroupChange gc)
{
	if (mGroupID.isNull()) return;

	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (panelp)
	{
		panelp->update(gc);
	}
	else
	{
		llwarns << "LLPanelGroupRoles::update() -- No subtab to update!" << llendl;
	}
}

void LLPanelGroupRoles::activate()
{
	// Start requesting member and role data if needed.
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	//if (!gdatap || mFirstUse)
	{
		// Check member data.
		
		if (!gdatap || !gdatap->isMemberDataComplete() )
		{
			gGroupMgr->sendGroupMembersRequest(mGroupID);
		}

		// Check role data.
		if (!gdatap || !gdatap->isRoleDataComplete() )
		{
			// Mildly hackish - clear all pending changes
			cancel();

			gGroupMgr->sendGroupRoleDataRequest(mGroupID);
		}

		// Check role-member mapping data.
		if (!gdatap || !gdatap->isRoleMemberDataComplete() )
		{
			gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
		}

		// Need this to get base group member powers
		if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
		{
			gGroupMgr->sendGroupPropertiesRequest(mGroupID);
		}

		mFirstUse = FALSE;
	}

	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (panelp) panelp->activate();
}

void LLPanelGroupRoles::deactivate()
{
	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (panelp) panelp->deactivate();
}

bool LLPanelGroupRoles::needsApply(LLString& mesg)
{
	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (!panelp) return false;
		
	return panelp->needsApply(mesg);
}

BOOL LLPanelGroupRoles::hasModal()
{
	if (mHasModal) return TRUE;

	LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
	if (!panelp) return FALSE;
		
	return panelp->hasModal();
}

// PanelGroupTab observer trigger
void LLPanelGroupRoles::tabChanged()
{
	notifyObservers();
}

////////////////////////////
// LLPanelGroupSubTab
////////////////////////////
LLPanelGroupSubTab::LLPanelGroupSubTab(const std::string& name, const LLUUID& group_id)
:	LLPanelGroupTab(name, group_id),
	mHeader(NULL),
	mFooter(NULL),
	mSearchLineEditor(NULL),
	mSearchButton(NULL),
	mShowAllButton(NULL)
{
}

LLPanelGroupSubTab::~LLPanelGroupSubTab()
{
}

BOOL LLPanelGroupSubTab::postBuild()
{
	// Hook up the search widgets.
	bool recurse = true;
	mSearchLineEditor = (LLLineEditor*) getChildByName("search_text", recurse);

	if (!mSearchLineEditor) return FALSE;
	mSearchLineEditor->setKeystrokeCallback(onSearchKeystroke);
	mSearchLineEditor->setCallbackUserData(this);

	mSearchButton = (LLButton*) getChildByName("search_button", recurse);

	if (!mSearchButton) return FALSE;
	mSearchButton->setClickedCallback(onClickSearch);
	mSearchButton->setCallbackUserData(this);
	mSearchButton->setEnabled(FALSE);

	mShowAllButton = (LLButton*) getChildByName("show_all_button", recurse);

	if (!mShowAllButton) return FALSE;
	mShowAllButton->setClickedCallback(onClickShowAll);
	mShowAllButton->setCallbackUserData(this);
	mShowAllButton->setEnabled(FALSE);
	
	// Get icons for later use.
	mActionIcons.clear();

	bool no_recurse = false;

	LLIconCtrl* icon = (LLIconCtrl*) getChildByName("power_folder_icon",no_recurse);
	if (icon && icon->getImage().notNull())
	{
		mActionIcons["folder"] = icon->getImage();
		removeChild(icon);
	}

	icon = (LLIconCtrl*) getChildByName("power_all_have_icon",no_recurse);
	if (icon && icon->getImage().notNull())
	{
		mActionIcons["full"] = icon->getImage();
		removeChild(icon);
	}

	icon = (LLIconCtrl*) getChildByName("power_partial_icon",no_recurse);
	if (icon && icon->getImage().notNull())
	{
		mActionIcons["partial"] = icon->getImage();
		removeChild(icon);
	}

	return LLPanelGroupTab::postBuild();
}

// static
void LLPanelGroupSubTab::onSearchKeystroke(LLLineEditor* caller, void* user_data)
{
	LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
	self->handleSearchKeystroke(caller);
}

void LLPanelGroupSubTab::handleSearchKeystroke(LLLineEditor* caller)
{
	if (caller->getText().size())
	{
		setDefaultBtn( mSearchButton );
		mSearchButton->setEnabled(TRUE);
	}
	else
	{
		setDefaultBtn( NULL );
		mSearchButton->setEnabled(FALSE);
	}
}

// static 
void LLPanelGroupSubTab::onClickSearch(void* user_data)
{
	LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
	self->handleClickSearch();
}
 
void LLPanelGroupSubTab::handleClickSearch()
{
	lldebugs << "LLPanelGroupSubTab::handleClickSearch()" << llendl;

	if (0 == mSearchLineEditor->getText().size())
	{
		// No search text.  (This shouldn't happen... the search button should have been disabled).
		llwarns << "handleClickSearch with no search text!" << llendl;
		mSearchButton->setEnabled(FALSE);
		return;
	}

	setSearchFilter( mSearchLineEditor->getText() );
	mShowAllButton->setEnabled(TRUE);
}

// static
void LLPanelGroupSubTab::onClickShowAll(void* user_data)
{
	LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
	self->handleClickShowAll();
}

void LLPanelGroupSubTab::handleClickShowAll()
{
	lldebugs << "LLPanelGroupSubTab::handleClickShowAll()" << llendl;
	setSearchFilter( LLString::null );
	mShowAllButton->setEnabled(FALSE);
}

void LLPanelGroupSubTab::setSearchFilter(const LLString& filter)
{
	lldebugs << "LLPanelGroupSubTab::setSearchFilter() ==> '" << filter << "'" << llendl;
	mSearchFilter = filter;
	LLString::toLower(mSearchFilter);
	update(GC_ALL);
}

void LLPanelGroupSubTab::activate()
{
	lldebugs << "LLPanelGroupSubTab::activate()" << llendl;
	setOthersVisible(TRUE);
}

void LLPanelGroupSubTab::deactivate()
{
	lldebugs << "LLPanelGroupSubTab::deactivate()" << llendl;
	setOthersVisible(FALSE);
}

void LLPanelGroupSubTab::setOthersVisible(BOOL b)
{
	if (mHeader)
	{
		mHeader->setVisible( b );
	}
	else
	{
		llwarns << "LLPanelGroupSubTab missing header!" << llendl;
	}

	if (mFooter)
	{
		mFooter->setVisible( b );
	}
	else
	{
		llwarns << "LLPanelGroupSubTab missing footer!" << llendl;
	}
}

bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action)
{
	// If the search filter is empty, everything passes.
	if (mSearchFilter.empty()) return true;

	LLString::toLower(action);
	std::string::size_type match = action.find(mSearchFilter);

	if (std::string::npos == match)
	{
		// not found
		return false;
	}
	else
	{
		return true;
	}
}

void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, 
										  U64 allowed_by_some, 
										  U64 allowed_by_all,
										  icon_map_t& icons,
										  void (*commit_callback)(LLUICtrl*,void*),
										  BOOL show_all,
										  BOOL filter,
										  BOOL is_owner_role)
{
	if (gGroupMgr->mRoleActionSets.empty())
	{
		llwarns << "Can't build action list - no actions found." << llendl;
		return;
	}

	std::vector<LLRoleActionSet*>::iterator ras_it = gGroupMgr->mRoleActionSets.begin();
	std::vector<LLRoleActionSet*>::iterator ras_end = gGroupMgr->mRoleActionSets.end();

	for ( ; ras_it != ras_end; ++ras_it)
	{
		buildActionCategory(ctrl,
							allowed_by_some,
							allowed_by_all,
							(*ras_it),
							icons, 
							commit_callback,
							show_all,
							filter,
							is_owner_role);
	}
}

void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
											 U64 allowed_by_some,
											 U64 allowed_by_all,
											 LLRoleActionSet* action_set,
											 icon_map_t& icons,
											 void (*commit_callback)(LLUICtrl*,void*),
											 BOOL show_all,
											 BOOL filter,
											 BOOL is_owner_role)
{
	lldebugs << "Building role list for: " << action_set->mActionSetData->mName << llendl;
	// See if the allow mask matches anything in this category.
	if (show_all || (allowed_by_some & action_set->mActionSetData->mPowerBit))
	{
		// List all the actions in this category that at least some members have.
		LLSD row;

		row["columns"][0]["column"] = "icon";
		icon_map_t::iterator iter = icons.find("folder");
		if (iter != icons.end())
		{
			row["columns"][0]["type"] = "icon";
			row["columns"][0]["value"] = (*iter).second;
		}

		row["columns"][1]["column"] = "action";
		row["columns"][1]["value"] = action_set->mActionSetData->mName;
		row["columns"][1]["font-style"] = "BOLD";

		LLScrollListItem* title_row = ctrl->addElement(row, ADD_BOTTOM, action_set->mActionSetData);

		bool category_matches_filter = (filter) ? matchesActionSearchFilter(action_set->mActionSetData->mName) : true;

		std::vector<LLRoleAction*>::iterator ra_it = action_set->mActions.begin();
		std::vector<LLRoleAction*>::iterator ra_end = action_set->mActions.end();

		bool items_match_filter = false;
		BOOL can_change_actions = (!is_owner_role && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CHANGE_ACTIONS));

		for ( ; ra_it != ra_end; ++ra_it)
		{
			// See if anyone has these action.
			if (!show_all && !(allowed_by_some & (*ra_it)->mPowerBit))
			{
				continue;
			}

			// See if we are filtering out these actions
			// If we aren't using filters, category_matches_filter will be true.
			if (!category_matches_filter
				&& !matchesActionSearchFilter((*ra_it)->mDescription))
			{
				continue;										
			}

			items_match_filter = true;

			// See if everyone has these actions.
			bool show_full_strength = false;
			if ( (allowed_by_some & (*ra_it)->mPowerBit) == (allowed_by_all & (*ra_it)->mPowerBit) )
			{
				show_full_strength = true;
			}

			LLSD row;

			S32 column_index = 0;
			row["columns"][column_index]["column"] = "icon";
			++column_index;

			
			S32 check_box_index = -1;
			if (commit_callback)
			{
				row["columns"][column_index]["column"] = "checkbox";
				row["columns"][column_index]["type"] = "checkbox";
				row["columns"][column_index]["value"] = (*ra_it)->mName;
				check_box_index = column_index;
				++column_index;
			}
			else
			{
				if (show_full_strength)
				{
					icon_map_t::iterator iter = icons.find("full");
					if (iter != icons.end())
					{
						row["columns"][column_index]["column"] = "checkbox";
						row["columns"][column_index]["type"] = "icon";
						row["columns"][column_index]["value"] = (*iter).second;
						++column_index;
					}
				}
				else
				{
					icon_map_t::iterator iter = icons.find("partial");
					if (iter != icons.end())
					{
						row["columns"][column_index]["column"] = "checkbox";
						row["columns"][column_index]["type"] = "icon";
						row["columns"][column_index]["value"] = (*iter).second;
						++column_index;
					}
					row["enabled"] = false;
				}
			}

			row["columns"][column_index]["column"] = "action";
			row["columns"][column_index]["value"] = (*ra_it)->mDescription;
			row["columns"][column_index]["font"] = "SANSSERIFSMALL";

			LLScrollListItem* item = ctrl->addElement(row, ADD_BOTTOM, (*ra_it));

			if (-1 != check_box_index)
			{
				// Extract the checkbox that was created.
				LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(check_box_index);
				LLCheckBoxCtrl* check = check_cell->getCheckBox();
				check->setEnabled(can_change_actions);
				check->setCommitCallback(commit_callback);
				check->setCallbackUserData(ctrl->getCallbackUserData());
				check->setToolTip( check->getLabel() );

				if (show_all)
				{
					check->setTentative(FALSE);
					if (allowed_by_some & (*ra_it)->mPowerBit)
					{
						check->set(TRUE);
					}
					else
					{
						check->set(FALSE);
					}
				}
				else
				{
					check->set(TRUE);
					if (show_full_strength)
					{
						check->setTentative(FALSE);
					}
					else
					{
						check->setTentative(TRUE);
					}
				}
			}
		}

		if (!items_match_filter)
		{
			S32 title_index = ctrl->getItemIndex(title_row);
			ctrl->deleteSingleItem(title_index);
		}
	}
}

void LLPanelGroupSubTab::setFooterEnabled(BOOL enable)
{
	if (mFooter)
	{
		mFooter->setAllChildrenEnabled(enable);
	}
}

////////////////////////////
// LLPanelGroupMembersSubTab
////////////////////////////

// static
void* LLPanelGroupMembersSubTab::createTab(void* data)
{
	LLUUID* group_id = static_cast<LLUUID*>(data);
	return new LLPanelGroupMembersSubTab("panel group members sub tab", *group_id);
}

LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab(const std::string& name, const LLUUID& group_id)
: 	LLPanelGroupSubTab(name, group_id),
	mMembersList(NULL),
	mAssignedRolesList(NULL),
	mAllowedActionsList(NULL),
	mChanged(FALSE),
	mPendingMemberUpdate(FALSE),
	mHasMatch(FALSE),
	mNumOwnerAdditions(0)
{
}

LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
{
}

BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root)
{
	// Upcast parent so we can ask it for sibling controls.
	LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;

	// Look recursively from the parent to find all our widgets.
	bool recurse = true;
	mHeader = (LLPanel*) parent->getChildByName("members_header", recurse);
	mFooter = (LLPanel*) parent->getChildByName("members_footer", recurse);

	mMembersList 		= (LLNameListCtrl*) 	parent->getChildByName("member_list", recurse);
	mAssignedRolesList	= (LLScrollListCtrl*)	parent->getChildByName("member_assigned_roles", recurse);
	mAllowedActionsList	= (LLScrollListCtrl*)	parent->getChildByName("member_allowed_actions", recurse);

	if (!mMembersList || !mAssignedRolesList || !mAllowedActionsList) return FALSE;

	// We want to be notified whenever a member is selected.
	mMembersList->setCallbackUserData(this);
	mMembersList->setCommitOnSelectionChange(TRUE);
	mMembersList->setCommitCallback(onMemberSelect);
	// Show the member's profile on double click.
	mMembersList->setDoubleClickCallback(onMemberDoubleClick);

	LLButton* button = (LLButton*) parent->getChildByName("member_invite", recurse);
	if ( button )
	{
		button->setClickedCallback(onInviteMember);
		button->setCallbackUserData(this);
		button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));
	}

	mEjectBtn = (LLButton*) parent->getChildByName("member_eject", recurse);
	if ( mEjectBtn )
	{
		mEjectBtn->setClickedCallback(onEjectMembers);
		mEjectBtn->setCallbackUserData(this);
		mEjectBtn->setEnabled(FALSE);
	}

	return TRUE;
}


// static
void LLPanelGroupMembersSubTab::onMemberSelect(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
	self->handleMemberSelect();
}

void LLPanelGroupMembersSubTab::handleMemberSelect()
{
	lldebugs << "LLPanelGroupMembersSubTab::handleMemberSelect" << llendl;

	mAssignedRolesList->deleteAllItems();
	mAllowedActionsList->deleteAllItems();
	
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupMembersSubTab::handleMemberSelect() "
				<< "-- No group data!" << llendl;
		return;
	}

	// Check if there is anything selected.
	std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
	if (selection.empty()) return;

	// Build a vector of all selected members, and gather allowed actions.
	std::vector<LLUUID> selected_members;
	U64 allowed_by_all = 0xffffffffffffLL;
	U64 allowed_by_some = 0;

	std::vector<LLScrollListItem*>::iterator itor;
	for (itor = selection.begin();
		 itor != selection.end(); ++itor)
	{
		selected_members.push_back( (*itor)->getUUID() );
		// Get this member's power mask including any unsaved changes

		U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID());

		allowed_by_all &= powers;
		allowed_by_some |= powers;
	}
	std::sort(selected_members.begin(), selected_members.end());

	//////////////////////////////////
	// Build the allowed actions list.
	//////////////////////////////////
	buildActionsList(mAllowedActionsList,
					 allowed_by_some,
					 allowed_by_all,
					 mActionIcons,
					 NULL,
					 FALSE,
					 FALSE,
					 FALSE);

	//////////////////////////////////
	// Build the assigned roles list.
	//////////////////////////////////
	// Add each role to the assigned roles list.
	LLGroupMgrGroupData::role_iter iter = gdatap->mRoles.begin();
	LLGroupMgrGroupData::role_iter end  = gdatap->mRoles.end();

	BOOL can_eject_members = gAgent.hasPowerInGroup(mGroupID,
													GP_MEMBER_EJECT);
	BOOL member_is_owner = FALSE;
	
	for( ; iter != end; ++iter)
	{
		// Count how many selected users are in this role.
		const LLUUID& role_id = iter->first;
		LLGroupRoleData* group_role_data = iter->second;

		if (group_role_data)
		{
			const BOOL needs_sort = FALSE;
			S32 count = group_role_data->getMembersInRole(
											selected_members, needs_sort);
			//check if the user has permissions to assign/remove
			//members to/from the role (but the ability to add/remove
			//should only be based on the "saved" changes to the role
			//not in the temp/meta data. -jwolk
			BOOL cb_enable = ( (count > 0) ?
							   agentCanRemoveFromRole(mGroupID, role_id) :
							   agentCanAddToRole(mGroupID, role_id) );


			// Owner role has special enabling permissions for removal.
			if (cb_enable && (count > 0) && role_id == gdatap->mOwnerRole)
			{
				// Check if any owners besides this agent are selected.
				std::vector<LLUUID>::const_iterator member_iter;
				std::vector<LLUUID>::const_iterator member_end =
												selected_members.end();
				for (member_iter = selected_members.begin();
					 member_iter != member_end;	
					 ++member_iter)
				{
					// Don't count the agent.
					if ((*member_iter) == gAgent.getID()) continue;
					
					// Look up the member data.
					LLGroupMgrGroupData::member_iter mi = 
									gdatap->mMembers.find((*member_iter));
					if (mi == gdatap->mMembers.end()) continue;
					LLGroupMemberData* member_data = (*mi).second;
					// Is the member an owner?
					if ( member_data->isInRole(gdatap->mOwnerRole) )
					{
						// Can't remove other owners.
						cb_enable = FALSE;
						break;
					}
				}
			}

			//now see if there are any role changes for the selected
			//members and remember to include them
			std::vector<LLUUID>::iterator sel_mem_iter = selected_members.begin();
			for (; sel_mem_iter != selected_members.end(); sel_mem_iter++)
			{
				LLRoleMemberChangeType type;
				if ( getRoleChangeType(*sel_mem_iter, role_id, type) )
				{
					if ( type == RMC_ADD ) count++;
					else if ( type == RMC_REMOVE ) count--;
				}
			}
			
			// If anyone selected is in any role besides 'Everyone' then they can't be ejected.
			if (role_id.notNull() && (count > 0))
			{
				can_eject_members = FALSE;
				if (role_id == gdatap->mOwnerRole)
				{
					member_is_owner = TRUE;
				}
			}

			LLRoleData rd;
			if (gdatap->getRoleData(role_id,rd))
			{
				std::ostringstream label;
				label << rd.mRoleName;
				// Don't bother showing a count, if there is only 0 or 1.
				if (count > 1)
				{
					label << ": " << count ;
				}
	
				LLSD row;
				row["id"] = role_id;

				row["columns"][0]["column"] = "checkbox";
				row["columns"][0]["type"] = "checkbox";

				row["columns"][1]["column"] = "role";
				row["columns"][1]["value"] = label.str();

				if (row["id"].asUUID().isNull())
				{
					// This is the everyone role, you can't take people out of the everyone role!
					row["enabled"] = false;
				}

				LLScrollListItem* item = mAssignedRolesList->addElement(row);

				// Extract the checkbox that was created.
				LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(0);
				LLCheckBoxCtrl* check = check_cell->getCheckBox();
				check->setCommitCallback(onRoleCheck);
				check->setCallbackUserData(this);
				check->set( count > 0 );
				check->setTentative(0 != count && selected_members.size() != count);

				//NOTE: as of right now a user can break the group
				//by removing himself from a role if he is the
				//last owner.  We should check for this special case
				// -jwolk
				check->setEnabled(cb_enable);
			}
		}
		else
		{
			// This could happen if changes are not synced right on sub-panel change.
			llwarns << "No group role data for " << iter->second << llendl;
		}
	}
	mAssignedRolesList->setEnabled(TRUE);

	if (!can_eject_members && !member_is_owner)
	{
		// Maybe we can eject them because we are an owner...
		LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
		if (mi != gdatap->mMembers.end())
		{
			LLGroupMemberData* member_data = (*mi).second;

			if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
			{
				can_eject_members = TRUE;
			}
		}
	}

	mEjectBtn->setEnabled(can_eject_members);
}

// static
void LLPanelGroupMembersSubTab::onMemberDoubleClick(void* user_data)
{
	LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
	self->handleMemberDoubleClick();
}

//static
void LLPanelGroupMembersSubTab::onInviteMember(void *userdata)
{
	LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;

	if ( selfp )
	{
		selfp->handleInviteMember();
	}
}

void LLPanelGroupMembersSubTab::handleInviteMember()
{
	LLFloaterGroupInvite::showForGroup(mGroupID);
}

void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata)
{
	LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;

	if ( selfp )
	{
		selfp->handleEjectMembers();
	}
}

void LLPanelGroupMembersSubTab::handleEjectMembers()
{
	//send down an eject message
	std::vector<LLUUID> selected_members;

	std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
	if (selection.empty()) return;

	std::vector<LLScrollListItem*>::iterator itor;
	for (itor = selection.begin() ; 
		 itor != selection.end(); ++itor)
	{
		selected_members.push_back((*itor)->getUUID());
	}

	mMembersList->deleteSelectedItems();

	gGroupMgr->sendGroupMemberEjects(mGroupID,
									 selected_members);
}

void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
												LLRoleMemberChangeType type)
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) return;

	//add that the user is requesting to change the roles for selected
	//members
	U64 powers_all_have  = 0xffffffffffffLL;
	U64 powers_some_have = 0;

	BOOL   is_owner_role = ( gdatap->mOwnerRole == role_id );
	LLUUID member_id;
	

	member_role_change_iter           member_end = mMemberRoleChangeData.end();
	member_role_change_iter           member;
	role_change_data_map_t           *role_change_datap;
	role_change_data_map_t::iterator  role_end;
	role_change_data_map_t::iterator  role;

	std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
	if (selection.empty()) return;

	std::vector<LLScrollListItem*>::iterator itor;
	for (itor = selection.begin() ; 
		 itor != selection.end(); ++itor)
	{
		member_id = (*itor)->getUUID();

		//see if we requested a change for this member before
		member = mMemberRoleChangeData.find(member_id);
		if ( member != member_end )
		{
			//this member had previously had their role data changed
			//so grab it
			role_change_datap = (*member).second;
		}
		else
		{
			role_change_datap = new role_change_data_map_t;
			mMemberRoleChangeData[member_id] = role_change_datap;
		}

		//now check to see if the selected group member
		//had changed his association with the selected role before
		role_end = role_change_datap->end();
		role     = role_change_datap->find(role_id);

		if ( role != role_end )
		{
			//see if the new change type cancels out the previous change
			if (role->second != type)
			{
				role_change_datap->erase(role_id);
				if ( is_owner_role ) mNumOwnerAdditions--;
			}
			//else do nothing

			if ( role_change_datap->empty() )
			{
				//the current member now has no role changes
				//so erase the role change and erase the member's entry
				delete role_change_datap;
                role_change_datap = NULL;

				mMemberRoleChangeData.erase(member_id);
			}
		}
		else
		{
			//a previously unchanged role is being changed
			(*role_change_datap)[role_id] = type;
			if ( is_owner_role && type == RMC_ADD ) mNumOwnerAdditions++;
		}

		//we need to calculate what powers the selected members
		//have (including the role changes we're making)
		//so that we can rebuild the action list
		U64 new_powers = getAgentPowersBasedOnRoleChanges(member_id);

		powers_all_have  &= new_powers;
		powers_some_have |= new_powers;
	}

	
	mChanged = !mMemberRoleChangeData.empty();
	notifyObservers();

	//alrighty now we need to update the actions list
	//to reflect the changes
	mAllowedActionsList->deleteAllItems();
	buildActionsList(mAllowedActionsList,
					 powers_some_have,
					 powers_all_have,
					 mActionIcons,
					 NULL,
					 FALSE,
					 FALSE,
					 FALSE);
}


// static 
void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
	LLCheckBoxCtrl* check_box = static_cast<LLCheckBoxCtrl*>(ctrl);
	if (!check_box || !self) return;


	LLUUID role_id = self->mAssignedRolesList->getFirstSelected()->getUUID();
	LLRoleMemberChangeType change_type = (check_box->get() ? 
										  RMC_ADD : 
										  RMC_REMOVE);

	self->handleRoleCheck(role_id, change_type);
}

void LLPanelGroupMembersSubTab::handleMemberDoubleClick()
{
	LLScrollListItem* selected = mMembersList->getFirstSelected();
	if (selected)
	{
		LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() );
	}
}

void LLPanelGroupMembersSubTab::activate()
{
	LLPanelGroupSubTab::activate();

	update(GC_ALL);
}

void LLPanelGroupMembersSubTab::deactivate()
{
	LLPanelGroupSubTab::deactivate();
}

bool LLPanelGroupMembersSubTab::needsApply(LLString& mesg)
{
	return mChanged;
}

void LLPanelGroupMembersSubTab::cancel()
{
	if ( mChanged )
	{
		std::for_each(mMemberRoleChangeData.begin(),
					  mMemberRoleChangeData.end(),
					  DeletePairedPointer());
		mMemberRoleChangeData.clear();

		mChanged = FALSE;
		notifyObservers();
	}
}

bool LLPanelGroupMembersSubTab::apply(LLString& mesg)
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap)
	{
		llwarns << "Unable to get group data for group " << mGroupID << llendl;

		mesg.assign("Unable to save member data.  Try again later.");
		return false;
	}

	if (mChanged)
	{
		//figure out if we are somehow adding an owner or not and alert
		//the user...possibly make it ignorable
		if ( mNumOwnerAdditions > 0 )
		{
			LLRoleData rd;
			LLStringBase<char>::format_map_t args;

			if ( gdatap->getRoleData(gdatap->mOwnerRole, rd) )
			{
				mHasModal = TRUE;
				args["[ROLE_NAME]"] = rd.mRoleName;
				gViewerWindow->alertXml("AddGroupOwnerWarning",
										args,
										addOwnerCB,
										this);
			}
			else
			{
				llwarns << "Unable to get role information for the owner role in group " << mGroupID << llendl;

				mesg.assign("Unable to retried specific group information.  Try again later");
				return false;
			}
				 
		}
		else
		{
			applyMemberChanges();
		}
	}

	return true;
}

//static
void LLPanelGroupMembersSubTab::addOwnerCB(S32 option, void* data)
{
	LLPanelGroupMembersSubTab* self = (LLPanelGroupMembersSubTab*) data;

	if (!self) return;
	
	self->mHasModal = FALSE;

	if (0 == option)
	{
		// User clicked "Yes"
		self->applyMemberChanges();
	}
}

void LLPanelGroupMembersSubTab::applyMemberChanges()
{
	//sucks to do a find again here, but it is in constant time, so, could
	//be worse
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap)
	{
		llwarns << "Unable to get group data for group " << mGroupID << llendl;
		return;
	}

	//we need to add all of the changed roles data
	//for each member whose role changed
	member_role_change_iter member_end = mMemberRoleChangeData.end();
	member_role_change_iter member     = mMemberRoleChangeData.begin();

	for (; member != member_end; member++)
	{
		role_change_data_map_t::iterator role_end = member->second->end();
		role_change_data_map_t::iterator role     = member->second->begin();

		for (; role != role_end; role++)
		{
			gdatap->changeRoleMember(role->first, //role_id
									 member->first, //member_id
									 role->second); //add/remove
		}

		member->second->clear();
		delete member->second;
	}
	mMemberRoleChangeData.clear();

	gGroupMgr->sendGroupRoleMemberChanges(mGroupID);	
	//force a UI update
	handleMemberSelect();

	mChanged = FALSE;
	mNumOwnerAdditions = 0;
	notifyObservers();
}

bool LLPanelGroupMembersSubTab::matchesSearchFilter(char* first, char* last)
{
	// If the search filter is empty, everything passes.
	if (mSearchFilter.empty()) return true;

	// Create a full name, and compare it to the search filter.
	LLString fullname;
	fullname.assign(first);
	fullname.append(1, ' ');
	fullname.append(last);
	LLString::toLower(fullname);

	std::string::size_type match = fullname.find(mSearchFilter);

	if (std::string::npos == match)
	{
		// not found
		return false;
	}
	else
	{
		return true;
	}
}

U64 LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges(const LLUUID& agent_id)
{
	//we loop over all of the changes
	//if we are adding a role, then we simply add the role's powers
	//if we are removing a role, we store that role id away
	//and then we have to build the powers up bases on the roles the agent
	//is in
	member_role_change_iter          member_end = mMemberRoleChangeData.end();
	member_role_change_iter          member;
	role_change_data_map_t          *role_change_datap = NULL;
	role_change_data_map_t::iterator role_end;
	role_change_data_map_t::iterator role;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No group data!" << llendl;
		return GP_NO_POWERS;
	}

	LLGroupMemberData* member_data = gdatap->mMembers[agent_id];
	if ( !member_data )
	{
		llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << llendl;
		return GP_NO_POWERS;
	}

	//see if there are unsaved role changes for this agent
	member = mMemberRoleChangeData.find(agent_id);
	if ( member != member_end )
	{
		//this member has unsaved role changes
		//so grab them
		role_change_datap = (*member).second;
	}

	U64 new_powers = GP_NO_POWERS;

	if ( role_change_datap )
	{
		std::vector<LLUUID> roles_to_be_removed;

		role_end = role_change_datap->end();
		role     = role_change_datap->begin();

		for (; role != role_end; role++)
		{
			if ( role->second == RMC_ADD )
				new_powers |= gdatap->getRolePowers(role->first);
			else
			{
				roles_to_be_removed.push_back(role->first);
			}
		}

		//loop over the member's current roles, summing up
		//the powers (not including the role we are removing)
		std::map<LLUUID,LLGroupRoleData*>::iterator current_role =
			member_data->roleBegin();
		std::map<LLUUID,LLGroupRoleData*>::iterator end_role = 
			member_data->roleEnd();

		for (; current_role != end_role; current_role++)
		{
			bool role_in_remove_list =
				(std::find(roles_to_be_removed.begin(),
						   roles_to_be_removed.end(),
						   current_role->second->getID()) !=
				 roles_to_be_removed.end());

			if ( !role_in_remove_list )
			{
				new_powers |= 
					current_role->second->getRoleData().mRolePowers;
			}
		}
	}
	else
	{
		//there are no changes for this member
		//the member's powers are just the ones stored in the group
		//manager
		new_powers = member_data->getAgentPowers();
	}

	return new_powers;
}

//If there is no change, returns false be sure to verify
//that there is a role change before attempting to get it or else
//the data will make no sense.  Stores the role change type
bool LLPanelGroupMembersSubTab::getRoleChangeType(const LLUUID& member_id,
												  const LLUUID& role_id,
												  LLRoleMemberChangeType& type)
{
	member_role_change_iter           member_changes_iter;
	role_change_data_map_t::iterator  role_changes_iter;

	member_changes_iter = mMemberRoleChangeData.find(member_id);
	if ( member_changes_iter != mMemberRoleChangeData.end() )
	{
		role_changes_iter = member_changes_iter->second->find(role_id);
		if ( role_changes_iter != member_changes_iter->second->end() )
		{
			type = role_changes_iter->second;
			return true;
		}
	}

	return false;
}

void LLPanelGroupMembersSubTab::draw()
{
	LLPanelGroupSubTab::draw();

	if (mPendingMemberUpdate)
	{
		updateMembers();
	}
}

void LLPanelGroupMembersSubTab::update(LLGroupChange gc)
{
	if (mGroupID.isNull()) return;

	if ( GC_TITLES == gc || GC_PROPERTIES == gc )
	{
		// Don't care about title or general group properties updates.
		return;
	}

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupMembersSubTab::update() -- No group data!" << llendl;
		return;
	}

	// Rebuild the members list.
	mMembersList->deleteAllItems();

	// Wait for both all data to be retrieved before displaying anything.
	if (   gdatap->isMemberDataComplete() 
		&& gdatap->isRoleDataComplete()
		&& gdatap->isRoleMemberDataComplete())
	{
		mMemberProgress = gdatap->mMembers.begin();
		mPendingMemberUpdate = TRUE;
		mHasMatch = FALSE;
	}
	else
	{
		// Build a string with info on retrieval progress.
		std::ostringstream retrieved;
		if ( !gdatap->isMemberDataComplete() )
		{
			// Still busy retreiving member list.
			retrieved << "Retrieving member list (" << gdatap->mMembers.size()
					  << " / " << gdatap->mMemberCount << ")...";
		}
		else if( !gdatap->isRoleDataComplete() )
		{
			// Still busy retreiving role list.
			retrieved << "Retrieving role list (" << gdatap->mRoles.size()
					  << " / " << gdatap->mRoleCount << ")...";
		}
		else // (!gdatap->isRoleMemberDataComplete())
		{
			// Still busy retreiving role/member mappings.
			retrieved << "Retrieving role member mappings...";
		}
		mMembersList->setEnabled(FALSE);
		mMembersList->addSimpleItem(retrieved.str());
	}
}

void LLPanelGroupMembersSubTab::updateMembers()
{
	mPendingMemberUpdate = FALSE;

	lldebugs << "LLPanelGroupMembersSubTab::updateMembers()" << llendl;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << llendl;
		return;
	}

	// Make sure all data is still complete.  Incomplete data
	// may occur if we refresh.
	if (   !gdatap->isMemberDataComplete() 
		|| !gdatap->isRoleDataComplete()
		|| !gdatap->isRoleMemberDataComplete())
	{
		return;
	}
		
	LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();

	char first[DB_FIRST_NAME_BUF_SIZE];
	char last[DB_LAST_NAME_BUF_SIZE];
	S32 i = 0;
	for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME; 
			++mMemberProgress, ++i)
	{
		if (!mMemberProgress->second)
			continue;
		// Do filtering on name if it is already in the cache.
		bool add_member = true;

		if (gCacheName->getName(mMemberProgress->first, first, last))
		{
			if ( !matchesSearchFilter(first, last) )
			{
				add_member = false;
			}
		}

		if (add_member)
		{
			// Build the donated tier string.
			std::ostringstream donated;
			donated << mMemberProgress->second->getContribution() << " sq. m.";

			LLSD row;
			row["id"] = (*mMemberProgress).first;

			row["columns"][0]["column"] = "name";
			// value is filled in by name list control

			row["columns"][1]["column"] = "donated";
			row["columns"][1]["value"] = donated.str();

			row["columns"][2]["column"] = "online";
			row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus();
			row["columns"][2]["font"] = "SANSSERIFSMALL";

			mMembersList->addElement(row);//, ADD_SORTED);
			mHasMatch = TRUE;
		}
	}

	if (mMemberProgress == end)
	{
		if (mHasMatch)
		{
			mMembersList->setEnabled(TRUE);
		}
		else
		{
			mMembersList->setEnabled(FALSE);
			mMembersList->addSimpleItem("No match.");
		}
	}
	else
	{
		mPendingMemberUpdate = TRUE;
	}

	// This should clear the other two lists, since nothing is selected.
	handleMemberSelect();
}



////////////////////////////
// LLPanelGroupRolesSubTab
////////////////////////////

// static
void* LLPanelGroupRolesSubTab::createTab(void* data)
{
	LLUUID* group_id = static_cast<LLUUID*>(data);
	return new LLPanelGroupRolesSubTab("panel group roles sub tab", *group_id);
}

LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab(const std::string& name, const LLUUID& group_id)
: LLPanelGroupSubTab(name, group_id), mHasRoleChange(FALSE)
{
}

LLPanelGroupRolesSubTab::~LLPanelGroupRolesSubTab()
{
}

BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root)
{
	// Upcast parent so we can ask it for sibling controls.
	LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;

	// Look recursively from the parent to find all our widgets.
	bool recurse = true;
	mHeader = (LLPanel*) parent->getChildByName("roles_header", recurse);
	mFooter = (LLPanel*) parent->getChildByName("roles_footer", recurse);


	mRolesList 		= (LLScrollListCtrl*) 	parent->getChildByName("role_list", recurse);
	mAssignedMembersList	= (LLNameListCtrl*)	parent->getChildByName("role_assigned_members", recurse);
	mAllowedActionsList	= (LLScrollListCtrl*)	parent->getChildByName("role_allowed_actions", recurse);

	mRoleName = (LLLineEditor*) parent->getChildByName("role_name", recurse);
	mRoleTitle = (LLLineEditor*) parent->getChildByName("role_title", recurse);
	mRoleDescription = (LLTextEditor*) parent->getChildByName("role_description", recurse);

	mMemberVisibleCheck = (LLCheckBoxCtrl*) parent->getChildByName("role_visible_in_list", recurse);

	if (!mRolesList || !mAssignedMembersList || !mAllowedActionsList
		|| !mRoleName || !mRoleTitle || !mRoleDescription || !mMemberVisibleCheck)
	{
		llwarns << "ARG! element not found." << llendl;
		return FALSE;
	}

	LLTextBox* txt = (LLTextBox*) parent->getChildByName("cant_delete_role", FALSE);
	if (txt)
	{
		mRemoveEveryoneTxt = txt->getText();
		parent->removeChild(txt);
	}

	mCreateRoleButton = 
		(LLButton*) parent->getChildByName("role_create", recurse);
	if ( mCreateRoleButton )
	{
		mCreateRoleButton->setCallbackUserData(this);
		mCreateRoleButton->setClickedCallback(onCreateRole);
		mCreateRoleButton->setEnabled(FALSE);
	}
	
	mDeleteRoleButton =  
		(LLButton*) parent->getChildByName("role_delete", recurse);
	if ( mDeleteRoleButton )
	{
		mDeleteRoleButton->setCallbackUserData(this);
		mDeleteRoleButton->setClickedCallback(onDeleteRole);
		mDeleteRoleButton->setEnabled(FALSE);
	}

	mRolesList->setCommitOnSelectionChange(TRUE);
	mRolesList->setCallbackUserData(this);
	mRolesList->setCommitCallback(onRoleSelect);

	mMemberVisibleCheck->setCallbackUserData(this);
	mMemberVisibleCheck->setCommitCallback(onMemberVisibilityChange);

	mAllowedActionsList->setCommitOnSelectionChange(TRUE);
	mAllowedActionsList->setCallbackUserData(this);

	mRoleName->setCommitOnFocusLost(TRUE);
	mRoleName->setCallbackUserData(this);
	mRoleName->setKeystrokeCallback(onPropertiesKey);

	mRoleTitle->setCommitOnFocusLost(TRUE);
	mRoleTitle->setCallbackUserData(this);
	mRoleTitle->setKeystrokeCallback(onPropertiesKey);

	mRoleDescription->setCommitOnFocusLost(TRUE);
	mRoleDescription->setCallbackUserData(this);
	mRoleDescription->setCommitCallback(onDescriptionCommit);
	mRoleDescription->setFocusReceivedCallback(onDescriptionCommit);

	setFooterEnabled(FALSE);

	return TRUE;
}

void LLPanelGroupRolesSubTab::activate()
{
	LLPanelGroupSubTab::activate();

	mRolesList->deselectAllItems();
	mAssignedMembersList->deleteAllItems();
	mAllowedActionsList->deleteAllItems();
	mRoleName->clear();
	mRoleDescription->clear();
	mRoleTitle->clear();

	setFooterEnabled(FALSE);

	mHasRoleChange = FALSE;
	update(GC_ALL);
}

void LLPanelGroupRolesSubTab::deactivate()
{
	lldebugs << "LLPanelGroupRolesSubTab::deactivate()" << llendl;

	LLPanelGroupSubTab::deactivate();
}

bool LLPanelGroupRolesSubTab::needsApply(LLString& mesg)
{
	lldebugs << "LLPanelGroupRolesSubTab::needsApply()" << llendl;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	return (mHasRoleChange								// Text changed in current role
			|| (gdatap && gdatap->pendingRoleChanges()));	// Pending role changes in the group
}

bool LLPanelGroupRolesSubTab::apply(LLString& mesg)
{
	lldebugs << "LLPanelGroupRolesSubTab::apply()" << llendl;

	saveRoleChanges();
	gGroupMgr->sendGroupRoleChanges(mGroupID);

	notifyObservers();

	return true;
}

void LLPanelGroupRolesSubTab::cancel()
{
	mHasRoleChange = FALSE;
	gGroupMgr->cancelGroupRoleChanges(mGroupID);

	notifyObservers();
}

LLSD LLPanelGroupRolesSubTab::createRoleItem(const LLUUID& role_id, 
								 std::string name, 
								 std::string title, 
								 S32 members)
{
	LLSD row;
	row["id"] = role_id;

	row["columns"][0]["column"] = "name";
	row["columns"][0]["value"] = name;

	row["columns"][1]["column"] = "title";
	row["columns"][1]["value"] = title;

	row["columns"][2]["column"] = "members";
	row["columns"][2]["value"] = members;
	
	return row;
}

bool LLPanelGroupRolesSubTab::matchesSearchFilter(std::string rolename, std::string roletitle)
{
	// If the search filter is empty, everything passes.
	if (mSearchFilter.empty()) return true;

	LLString::toLower(rolename);
	LLString::toLower(roletitle);
	std::string::size_type match_name = rolename.find(mSearchFilter);
	std::string::size_type match_title = roletitle.find(mSearchFilter);

	if ( (std::string::npos == match_name)
		&& (std::string::npos == match_title))
	{
		// not found
		return false;
	}
	else
	{
		return true;
	}
}

void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
{
	lldebugs << "LLPanelGroupRolesSubTab::update()" << llendl;

	if (mGroupID.isNull()) return;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	if (!gdatap || !gdatap->isRoleDataComplete())
	{
		gGroupMgr->sendGroupRoleDataRequest(mGroupID);
	}
	else
	{
		bool had_selection = false;
		LLUUID last_selected;
		if (mRolesList->getFirstSelected())
		{
			last_selected = mRolesList->getFirstSelected()->getUUID();
			had_selection = true;
		}
		mRolesList->deleteAllItems();

		LLScrollListItem* item = NULL;

		LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.begin();
		LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();

		for ( ; rit != end; ++rit)
		{
			LLRoleData rd;
			if (gdatap->getRoleData((*rit).first,rd))
			{
				if (matchesSearchFilter(rd.mRoleName, rd.mRoleTitle))
				{
					// If this is the everyone role, then EVERYONE is in it.
					S32 members_in_role = (*rit).first.isNull() ? gdatap->mMembers.size() : (*rit).second->getTotalMembersInRole();
					LLSD row = createRoleItem((*rit).first,rd.mRoleName, rd.mRoleTitle, members_in_role);
					item = mRolesList->addElement(row, ((*rit).first.isNull()) ? ADD_TOP : ADD_BOTTOM, this);
					if (had_selection && ((*rit).first == last_selected))
					{
						item->setSelected(TRUE);
					}
				}
			}
			else
			{
				llwarns << "LLPanelGroupRolesSubTab::update() No role data for role " << (*rit).first << llendl;
			}
		}

		mRolesList->sortByColumn("name", TRUE);

		if ( (gdatap->mRoles.size() < MAX_ROLES)
			&& gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE) )
		{
			mCreateRoleButton->setEnabled(TRUE);
		}
		else
		{
			mCreateRoleButton->setEnabled(FALSE);
		}

		if (had_selection)
		{
			handleRoleSelect();
		}
		else
		{
			mAssignedMembersList->deleteAllItems();
			mAllowedActionsList->deleteAllItems();
			mRoleName->clear();
			mRoleDescription->clear();
			mRoleTitle->clear();
			setFooterEnabled(FALSE);
			mDeleteRoleButton->setEnabled(FALSE);
		}
	}

	if (!gdatap || !gdatap->isMemberDataComplete())
	{
		gGroupMgr->sendGroupMembersRequest(mGroupID);
	}
	
	if (!gdatap || !gdatap->isRoleMemberDataComplete())
	{
		gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
	}

	if ((GC_ROLE_MEMBER_DATA == gc || GC_MEMBER_DATA == gc)
		&& gdatap->isMemberDataComplete()
		&& gdatap->isRoleMemberDataComplete())
	{
		buildMembersList();
	}
}

// static
void LLPanelGroupRolesSubTab::onRoleSelect(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	self->handleRoleSelect();
}

void LLPanelGroupRolesSubTab::handleRoleSelect()
{
	BOOL can_delete = TRUE;
	lldebugs << "LLPanelGroupRolesSubTab::handleRoleSelect()" << llendl;

	mAssignedMembersList->deleteAllItems();
	mAllowedActionsList->deleteAllItems();
	
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
				<< "-- No group data!" << llendl;
		return;
	}

	saveRoleChanges();

	// Check if there is anything selected.
	LLScrollListItem* item = mRolesList->getFirstSelected();
	if (!item)
	{
		setFooterEnabled(FALSE);
		return;
	}

	setFooterEnabled(TRUE);

	LLRoleData rd;
	if (gdatap->getRoleData(item->getUUID(),rd))
	{
		BOOL is_owner_role = ( gdatap->mOwnerRole == item->getUUID() );
		mRoleName->setText(rd.mRoleName);
		mRoleTitle->setText(rd.mRoleTitle);
		mRoleDescription->setText(rd.mRoleDescription);

		mAllowedActionsList->setEnabled(gAgent.hasPowerInGroup(mGroupID,
									   GP_ROLE_CHANGE_ACTIONS));
		buildActionsList(mAllowedActionsList,
						 rd.mRolePowers,
						 0LL,
						 mActionIcons,
						 onActionCheck,
						 TRUE,
						 FALSE,
						 is_owner_role);


		mMemberVisibleCheck->set((rd.mRolePowers & GP_MEMBER_VISIBLE_IN_DIR) == GP_MEMBER_VISIBLE_IN_DIR);
		mRoleName->setEnabled(!is_owner_role &&
				gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
		mRoleTitle->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
		mRoleDescription->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
		mMemberVisibleCheck->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));

		if (item->getUUID().isNull())
		{
			// Everyone role, can't edit description or name or delete
			mRoleDescription->setEnabled(FALSE);
			mRoleName->setEnabled(FALSE);
			can_delete = FALSE;
		}
		//you can't delete the owner role
		if ( is_owner_role ) can_delete = FALSE;
	}
	else
	{
		mRolesList->deselectAllItems();
		mAssignedMembersList->deleteAllItems();
		mAllowedActionsList->deleteAllItems();
		mRoleName->clear();
		mRoleDescription->clear();
		mRoleTitle->clear();
		setFooterEnabled(FALSE);

		can_delete = FALSE;
	}
	mSelectedRole = item->getUUID();
	buildMembersList();

	can_delete = can_delete && gAgent.hasPowerInGroup(mGroupID,
													  GP_ROLE_DELETE);
	mDeleteRoleButton->setEnabled(can_delete);
}

void LLPanelGroupRolesSubTab::buildMembersList()
{
	mAssignedMembersList->deleteAllItems();

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
				<< "-- No group data!" << llendl;
		return;
	}

	// Check if there is anything selected.
	LLScrollListItem* item = mRolesList->getFirstSelected();
	if (!item) return;

	if (item->getUUID().isNull())
	{
		// Special cased 'Everyone' role
		LLGroupMgrGroupData::member_iter mit = gdatap->mMembers.begin();
		LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
		for ( ; mit != end; ++mit)
		{
			mAssignedMembersList->addNameItem((*mit).first);
		}
	}
	else
	{
		LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.find(item->getUUID());
		if (rit != gdatap->mRoles.end())
		{
			LLGroupRoleData* rdatap = (*rit).second;
			if (rdatap)
			{
				std::vector<LLUUID>::const_iterator mit = rdatap->getMembersBegin();
				std::vector<LLUUID>::const_iterator end = rdatap->getMembersEnd();
				for ( ; mit != end; ++mit)
				{
					mAssignedMembersList->addNameItem((*mit));
				}
			}
		}
	}
}

// static
void LLPanelGroupRolesSubTab::onActionCheck(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
	if (!check || !self) return;

	self->handleActionCheck(check);
}

struct ActionCBData
{
	LLPanelGroupRolesSubTab*	mSelf;
	LLCheckBoxCtrl* 			mCheck;
};

void LLPanelGroupRolesSubTab::handleActionCheck(LLCheckBoxCtrl* check, bool force)
{
	lldebugs << "LLPanelGroupRolesSubTab::handleActionSelect()" << llendl;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
				<< "-- No group data!" << llendl;
		return;
	}

	LLScrollListItem* action_item = mAllowedActionsList->getFirstSelected();
	if (!action_item)
	{
		return;
	}

	LLScrollListItem* role_item = mRolesList->getFirstSelected();
	if (!role_item)
	{
		return;
	}
	LLUUID role_id = role_item->getUUID();

	LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata();
	U64 power = rap->mPowerBit;

	if (check->get())
	{
		if (!force && (    (GP_ROLE_ASSIGN_MEMBER == power)
						|| (GP_ROLE_CHANGE_ACTIONS == power) ))
		{
			// Uncheck the item, for now.  It will be
			// checked if they click 'Yes', below.
			check->set(FALSE);

			LLRoleData rd;
			LLStringBase<char>::format_map_t args;

			if ( gdatap->getRoleData(role_id, rd) )
			{
				args["[ACTION_NAME]"] = rap->mDescription;
				args["[ROLE_NAME]"] = rd.mRoleName;
				struct ActionCBData* cb_data = new ActionCBData;
				cb_data->mSelf = this;
				cb_data->mCheck = check;
				mHasModal = TRUE;
				LLString warning = "AssignDangerousActionWarning";
				if (GP_ROLE_CHANGE_ACTIONS == power)
				{
					warning = "AssignDangerousAbilityWarning";
				}
				gViewerWindow->alertXml(warning, args, addActionCB, cb_data);
			}
			else
			{
				llwarns << "Unable to look up role information for role id: "
						<< role_id << llendl;
			}
		}
		else
		{
			gdatap->addRolePower(role_id,power);
		}
	}
	else
	{
		gdatap->removeRolePower(role_id,power);
	}

	mHasRoleChange = TRUE;
	notifyObservers();
}

//static
void LLPanelGroupRolesSubTab::addActionCB(S32 option, void* data)
{
	struct ActionCBData* cb_data = (struct ActionCBData*) data;

	if (!cb_data || !cb_data->mSelf || !cb_data->mCheck) return;

	cb_data->mSelf->mHasModal = FALSE;

	if (0 == option)
	{
		// User clicked "Yes"
		cb_data->mCheck->set(TRUE);
		const bool force_add = true;
		cb_data->mSelf->handleActionCheck(cb_data->mCheck, force_add);
	}
}


// static
void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	if (!self) return;

	self->mHasRoleChange = TRUE;
	self->notifyObservers();
}

// static 
void LLPanelGroupRolesSubTab::onDescriptionCommit(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	if (!self) return;

	self->mHasRoleChange = TRUE;
	self->notifyObservers();
}

// static 
void LLPanelGroupRolesSubTab::onMemberVisibilityChange(LLUICtrl* ctrl, void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
	if (!check || !self) return;

	self->handleMemberVisibilityChange(check->get());
}

void LLPanelGroupRolesSubTab::handleMemberVisibilityChange(bool value)
{
	lldebugs << "LLPanelGroupRolesSubTab::handleMemberVisibilityChange()" << llendl;

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
	if (!gdatap) 
	{
		llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
				<< "-- No group data!" << llendl;
		return;
	}

	LLScrollListItem* role_item = mRolesList->getFirstSelected();
	if (!role_item)
	{
		return;
	}

	if (value)
	{
		gdatap->addRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
	}
	else
	{
		gdatap->removeRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
	}
}

// static 
void LLPanelGroupRolesSubTab::onCreateRole(void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	if (!self) return;

	self->handleCreateRole();
}

void LLPanelGroupRolesSubTab::handleCreateRole()
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	if (!gdatap) return;

	LLUUID new_role_id;
	new_role_id.generate();

	LLRoleData rd;
	rd.mRoleName = "New Role";
	gdatap->createRole(new_role_id,rd);

	mRolesList->deselectAllItems(TRUE);
	LLSD row;
	row["id"] = new_role_id;
	row["columns"][0]["column"] = "name";
	row["columns"][0]["value"] = rd.mRoleName;
	mRolesList->addElement(row, ADD_BOTTOM, this);
	mRolesList->selectByID(new_role_id);

	// put focus on name field and select its contents
	if(mRoleName)
	{
		mRoleName->setFocus(TRUE);
		mRoleName->onTabInto();
		gFocusMgr.triggerFocusFlash();
	}

	notifyObservers();
}

// static 
void LLPanelGroupRolesSubTab::onDeleteRole(void* user_data)
{
	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
	if (!self) return;

	self->handleDeleteRole();
}

void LLPanelGroupRolesSubTab::handleDeleteRole()
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	if (!gdatap) return;

	LLScrollListItem* role_item = mRolesList->getFirstSelected();
	if (!role_item)
	{
		return;
	}

	if (role_item->getUUID().isNull() || role_item->getUUID() == gdatap->mOwnerRole)
	{
		LLString::format_map_t args;
		args["[MESSAGE]"] = mRemoveEveryoneTxt;
		LLNotifyBox::showXml("GenericNotify", args);
		return;
	}

	gdatap->deleteRole(role_item->getUUID());
	mRolesList->deleteSingleItem(mRolesList->getFirstSelectedIndex());
	mRolesList->selectFirstItem();

	notifyObservers();
}

void LLPanelGroupRolesSubTab::saveRoleChanges()
{
	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	if (!gdatap) return;

	if (mHasRoleChange)
	{
		LLRoleData rd;
		if (!gdatap->getRoleData(mSelectedRole,rd)) return;

		rd.mRoleName = mRoleName->getText();
		rd.mRoleDescription = mRoleDescription->getText();
		rd.mRoleTitle = mRoleTitle->getText();

		gdatap->setRoleData(mSelectedRole,rd);

		mRolesList->deleteSingleItem(mRolesList->getItemIndex(mSelectedRole));
		
		LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,0);
		LLScrollListItem* item = mRolesList->addElement(row, ADD_BOTTOM, this);
		item->setSelected(TRUE);

		mHasRoleChange = FALSE;
	}
}
////////////////////////////
// LLPanelGroupActionsSubTab
////////////////////////////

// static
void* LLPanelGroupActionsSubTab::createTab(void* data)
{
	LLUUID* group_id = static_cast<LLUUID*>(data);
	return new LLPanelGroupActionsSubTab("panel group actions sub tab", *group_id);
}

LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab(const std::string& name, const LLUUID& group_id)
: LLPanelGroupSubTab(name, group_id)
{
}

LLPanelGroupActionsSubTab::~LLPanelGroupActionsSubTab()
{
}

BOOL LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root)
{
	// Upcast parent so we can ask it for sibling controls.
	LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;

	// Look recursively from the parent to find all our widgets.
	bool recurse = true;
	mHeader = (LLPanel*) parent->getChildByName("actions_header", recurse);
	mFooter = (LLPanel*) parent->getChildByName("actions_footer", recurse);

	mActionDescription = (LLTextEditor*) parent->getChildByName("action_description", recurse);

	mActionList = (LLScrollListCtrl*) parent->getChildByName("action_list",recurse);
	mActionRoles = (LLScrollListCtrl*) parent->getChildByName("action_roles",recurse);
	mActionMembers  = (LLNameListCtrl*) parent->getChildByName("action_members",recurse);

	if (!mActionList || !mActionDescription || !mActionRoles || !mActionMembers) return FALSE;

	mActionList->setCallbackUserData(this);
	mActionList->setCommitOnSelectionChange(TRUE);
	mActionList->setCommitCallback(onActionSelect);

	mActionMembers->setCallbackUserData(this);
	mActionRoles->setCallbackUserData(this);

	update(GC_ALL);

	return TRUE;
}

void LLPanelGroupActionsSubTab::activate()
{
	LLPanelGroupSubTab::activate();
	lldebugs << "LLPanelGroupActionsSubTab::activate()" << llendl;

	mActionList->deselectAllItems();
	mActionMembers->deleteAllItems();
	mActionRoles->deleteAllItems();
	mActionDescription->clear();
}

void LLPanelGroupActionsSubTab::deactivate()
{
	lldebugs << "LLPanelGroupActionsSubTab::deactivate()" << llendl;

	LLPanelGroupSubTab::deactivate();
}

bool LLPanelGroupActionsSubTab::needsApply(LLString& mesg)
{
	lldebugs << "LLPanelGroupActionsSubTab::needsApply()" << llendl;

	return false;
}

bool LLPanelGroupActionsSubTab::apply(LLString& mesg)
{
	lldebugs << "LLPanelGroupActionsSubTab::apply()" << llendl;
	return true;
}

void LLPanelGroupActionsSubTab::update(LLGroupChange gc)
{
	lldebugs << "LLPanelGroupActionsSubTab::update()" << llendl;

	if (mGroupID.isNull()) return;

	mActionList->deselectAllItems();
	mActionMembers->deleteAllItems();
	mActionRoles->deleteAllItems();
	mActionDescription->clear();

	mActionList->deleteAllItems();
	buildActionsList(mActionList,
					 GP_ALL_POWERS,
					 GP_ALL_POWERS,
					 mActionIcons,
					 NULL,
					 FALSE,
					 TRUE,
					 FALSE);
}

// static 
void LLPanelGroupActionsSubTab::onActionSelect(LLUICtrl* scroll, void* data)
{
	LLPanelGroupActionsSubTab* self = static_cast<LLPanelGroupActionsSubTab*>(data);
	self->handleActionSelect();
}

void LLPanelGroupActionsSubTab::handleActionSelect()
{
	mActionMembers->deleteAllItems();
	mActionRoles->deleteAllItems();

	U64 power_mask = GP_NO_POWERS;
	std::vector<LLScrollListItem*> selection = 
							mActionList->getAllSelected();
	if (selection.empty()) return;

	LLRoleAction* rap;

	std::vector<LLScrollListItem*>::iterator itor;
	for (itor = selection.begin() ; 
		 itor != selection.end(); ++itor)
	{
		rap = (LLRoleAction*)( (*itor)->getUserdata() );
		power_mask |= rap->mPowerBit;
	}

	if (selection.size() == 1)
	{
		LLScrollListItem* item = selection[0];
		rap = (LLRoleAction*)(item->getUserdata());

		if (rap->mLongDescription.empty())
		{
			mActionDescription->setText(rap->mDescription);
		}
		else
		{
			mActionDescription->setText(rap->mLongDescription);
		}
	}
	else
	{
		mActionDescription->clear();
	}

	LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);

	if (!gdatap) return;

	if (gdatap->isMemberDataComplete())
	{
		LLGroupMgrGroupData::member_iter it = gdatap->mMembers.begin();
		LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
		LLGroupMemberData* gmd;

		for ( ; it != end; ++it)
		{
			gmd = (*it).second;
			if (!gmd) continue;
			if ((gmd->getAgentPowers() & power_mask) == power_mask)
			{
				mActionMembers->addNameItem(gmd->getID());
			}
		}
	}
	else
	{
		gGroupMgr->sendGroupMembersRequest(mGroupID);
	}

	if (gdatap->isRoleDataComplete())
	{
		LLGroupMgrGroupData::role_iter it = gdatap->mRoles.begin();
		LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
		LLGroupRoleData* rmd;

		for ( ; it != end; ++it)
		{
			rmd = (*it).second;
			if (!rmd) continue;
			if ((rmd->getRoleData().mRolePowers & power_mask) == power_mask)
			{
				mActionRoles->addSimpleItem(rmd->getRoleData().mRoleName);
			}
		}
	}
	else
	{
		gGroupMgr->sendGroupRoleDataRequest(mGroupID);
	}
}
