/**
 * @file llpanelgrouproles.cpp
 * @brief Panel for roles information about a particular group.
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llcheckboxctrl.h"

#include "llagent.h"
#include "llavatarnamecache.h"
#include "llbutton.h"
#include "llfiltereditor.h"
#include "llfloatergroupbulkban.h"
#include "llfloatergroupinvite.h"
#include "llavataractions.h"
#include "lliconctrl.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llpanelgrouproles.h"
#include "llscrolllistctrl.h"
#include "llscrolllistitem.h"
#include "llscrolllistcell.h"
#include "llslurl.h"
#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltexteditor.h"
#include "lltrans.h"
#include "llviewertexturelist.h"
#include "llviewerwindow.h"
#include "llfocusmgr.h"
#include "llviewercontrol.h"

#include "roles_constants.h"

static LLPanelInjector<LLPanelGroupRoles> t_panel_group_roles("panel_group_roles");

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)
{
    if (gAgent.isGodlike())
        return true;

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
    if (!gdatap)
    {
        LL_WARNS() << "agentCanAddToRole "
                << "-- No group data!" << LL_ENDL;
        return false;
    }

    //make sure the agent is in the group
    LLGroupMgrGroupData::member_list_t::iterator 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;
}


// LLPanelGroupRoles /////////////////////////////////////////////////////

// static
LLPanelGroupRoles::LLPanelGroupRoles()
:   LLPanelGroupTab(),
    mCurrentTab(NULL),
    mRequestedTab( NULL ),
    mSubTabContainer( NULL ),
    mFirstUse( true )
{
}

LLPanelGroupRoles::~LLPanelGroupRoles()
{
}

bool LLPanelGroupRoles::postBuild()
{
    LL_DEBUGS() << "LLPanelGroupRoles::postBuild()" << LL_ENDL;

    mSubTabContainer = getChild<LLTabContainer>("roles_tab_container");

    if (!mSubTabContainer) return false;

    // Hook up each sub-tabs callback and widgets.
    for (S32 i = 0; i < mSubTabContainer->getTabCount(); ++i)
    {
        LLPanel* panel = mSubTabContainer->getPanelByIndex(i);
        LLPanelGroupSubTab* subtabp = dynamic_cast<LLPanelGroupSubTab*>(panel);
        if (!subtabp)
        {
            LL_WARNS() << "Invalid subtab panel: " << panel->getName() << LL_ENDL;
            return false;
        }

        // 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);
    }
    // Add click callbacks to tab switching.
    mSubTabContainer->setValidateBeforeCommit(boost::bind(&LLPanelGroupRoles::handleSubTabSwitch, this, _1));

    // 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.
    mDefaultNeedsApplyMesg = getString("default_needs_apply_text");
    mWantApplyMesg = getString("want_apply_text");

    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);

}

bool LLPanelGroupRoles::handleSubTabSwitch(const LLSD& data)
{
    std::string panel_name = data.asString();

    if(mRequestedTab != NULL)//we already have tab change request
    {
        return false;
    }

    mRequestedTab = static_cast<LLPanelGroupTab*>(mSubTabContainer->getPanelByName(panel_name));

    std::string 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.
        LLSD args;
        args["NEEDS_APPLY_MESSAGE"] = mesg;
        args["WANT_APPLY_MESSAGE"] = mWantApplyMesg;
        LLNotificationsUtil::add("PanelGroupApply", args, LLSD(),
            boost::bind(&LLPanelGroupRoles::handleNotifyCallback, this, _1, _2));
        mHasModal = true;

        // Returning false will block a close action from finishing until
        // we get a response back from the user.
        return false;
    }

    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();
        mRequestedTab = 0;
    }
}

bool LLPanelGroupRoles::handleNotifyCallback(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    mHasModal = false;
    LLPanelGroupTab* transition_tab = mRequestedTab;
    switch (option)
    {
    case 0: // "Apply Changes"
    {
        // Try to apply changes, and switch to the requested tab.
        std::string apply_mesg;
        if ( !apply( apply_mesg ) )
        {
            // There was a problem doing the apply.
            if ( !apply_mesg.empty() )
            {
                mHasModal = true;
                LLSD args;
                args["MESSAGE"] = apply_mesg;
                LLNotificationsUtil::add("GenericAlert", args, LLSD(), boost::bind(&LLPanelGroupRoles::onModalClose, this, _1, _2));
            }
            // Skip switching tabs.
            break;
        }
        transitionToTab();
        mSubTabContainer->selectTabPanel( transition_tab );

        break;
    }
    case 1: // "Ignore Changes"
        // Switch to the requested panel without applying changes
        cancel();
        transitionToTab();
        mSubTabContainer->selectTabPanel( transition_tab );
        break;
    case 2: // "Cancel"
    default:
        mRequestedTab = NULL;
        // Do nothing.  The user is canceling the action.
        break;
    }
    return false;
}

bool LLPanelGroupRoles::onModalClose(const LLSD& notification, const LLSD& response)
{
    mHasModal = false;
    return false;
}

bool LLPanelGroupRoles::apply(std::string& 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.
    std::string 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();
}

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

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

}

void LLPanelGroupRoles::activate()
{
    if (!gAgent.isInGroup(mGroupID)) return;

    // Start requesting member and role data if needed.
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

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

        LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID);
    }

    // Need this to get base group member powers
    if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
    {
        LLGroupMgr::getInstance()->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(std::string& 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();
}

void LLPanelGroupRoles::setGroupID(const LLUUID& id)
{
    LLPanelGroupTab::setGroupID(id);

    LLPanelGroupMembersSubTab* group_members_tab = findChild<LLPanelGroupMembersSubTab>("members_sub_tab");
    LLPanelGroupRolesSubTab*  group_roles_tab = findChild<LLPanelGroupRolesSubTab>("roles_sub_tab");
    LLPanelGroupActionsSubTab* group_actions_tab = findChild<LLPanelGroupActionsSubTab>("actions_sub_tab");
    LLPanelGroupBanListSubTab* group_ban_tab = findChild<LLPanelGroupBanListSubTab>("banlist_sub_tab");

    if(group_members_tab) group_members_tab->setGroupID(id);
    if(group_roles_tab) group_roles_tab->setGroupID(id);
    if(group_actions_tab) group_actions_tab->setGroupID(id);
    if(group_ban_tab) group_ban_tab->setGroupID(id);

    LLButton* button = getChild<LLButton>("member_invite");
    if ( button )
        button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));

    if(mSubTabContainer)
        mSubTabContainer->selectTab(1);
    group_roles_tab->mFirstOpen = true;
    activate();
}


// LLPanelGroupSubTab ////////////////////////////////////////////////////
LLPanelGroupSubTab::LLPanelGroupSubTab()
:   LLPanelGroupTab(),
    mHeader(NULL),
    mFooter(NULL),
    mActivated(false),
    mHasGroupBanPower(false),
    mSearchEditor(NULL)
{
}

LLPanelGroupSubTab::~LLPanelGroupSubTab()
{
    mSearchCommitConnection.disconnect();
}

bool LLPanelGroupSubTab::postBuildSubTab(LLView* root)
{
    // Get icons for later use.
    mActionIcons.clear();

    if (hasString("power_folder_icon"))
    {
        mActionIcons["folder"] = getString("power_folder_icon");
    }

    if (hasString("power_all_have_icon"))
    {
        mActionIcons["full"] = getString("power_all_have_icon");
    }

    if (hasString("power_partial_icon"))
    {
        mActionIcons["partial"] = getString("power_partial_icon");
    }
    return true;
}

bool LLPanelGroupSubTab::postBuild()
{
    // Hook up the search widgets.
    constexpr bool recurse = true;

    mSearchEditor = findChild<LLFilterEditor>("filter_input", recurse);
    if (mSearchEditor) // SubTab doesn't implement this, only some of derived classes
    {
        // panel
        mSearchCommitConnection = mSearchEditor->setCommitCallback(boost::bind(&LLPanelGroupSubTab::setSearchFilter, this, _2));
    }

    return LLPanelGroupTab::postBuild();
}

void LLPanelGroupSubTab::setGroupID(const LLUUID& id)
{
    LLPanelGroupTab::setGroupID(id);
    if(mSearchEditor)
    {
        mSearchEditor->clear();
        setSearchFilter("");
    }

    mActivated = false;
}

void LLPanelGroupSubTab::setSearchFilter(const std::string& filter)
{
    if(mSearchFilter == filter)
        return;
    mSearchFilter = filter;
    LLStringUtil::toLower(mSearchFilter);
    update(GC_ALL);
    onFilterChanged();
}

void LLPanelGroupSubTab::activate()
{
    setOthersVisible(true);
}

void LLPanelGroupSubTab::deactivate()
{
    setOthersVisible(false);
}

void LLPanelGroupSubTab::setOthersVisible(bool b)
{
    if (mHeader)
    {
        mHeader->setVisible( b );
    }

    if (mFooter)
    {
        mFooter->setVisible( b );
    }
}

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

    LLStringUtil::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,
                                          LLUICtrl::commit_callback_t commit_callback,
                                          bool show_all,
                                          bool filter,
                                          bool is_owner_role)
{
    if (LLGroupMgr::getInstance()->mRoleActionSets.empty())
    {
        LL_WARNS() << "Can't build action list - no actions found." << LL_ENDL;
        return;
    }

    mHasGroupBanPower = false;

    std::vector<LLRoleActionSet*>::iterator ras_it = LLGroupMgr::getInstance()->mRoleActionSets.begin();
    std::vector<LLRoleActionSet*>::iterator ras_end = LLGroupMgr::getInstance()->mRoleActionSets.end();
    for ( ; ras_it != ras_end; ++ras_it)
    {
        buildActionCategory(ctrl,
                            allowed_by_some,
                            allowed_by_all,
                            (*ras_it),
                            commit_callback,
                            show_all,
                            filter,
                            is_owner_role);
    }
}

void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
                                             U64 allowed_by_some,
                                             U64 allowed_by_all,
                                             LLRoleActionSet* action_set,
                                             LLUICtrl::commit_callback_t commit_callback,
                                             bool show_all,
                                             bool filter,
                                             bool is_owner_role)
{
    LL_DEBUGS() << "Building role list for: " << action_set->mActionSetData->mName << LL_ENDL;
    // 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";
        row["columns"][0]["type"] = "icon";

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

        row["columns"][1]["column"] = "action";
        row["columns"][1]["type"] = "text";
        row["columns"][1]["value"] = LLTrans::getString(action_set->mActionSetData->mName);
        row["columns"][1]["font"]["name"] = "SANSSERIF_SMALL";


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

        LLScrollListText* name_textp = dynamic_cast<LLScrollListText*>(title_row->getColumn(2)); //?? I have no idea fix getColumn(1) return column spacer...
        if (name_textp)
            name_textp->setFontStyle(LLFontGL::BOLD);

        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";
                check_box_index = column_index;
                ++column_index;
            }
            else
            {
                if (show_full_strength)
                {
                    icon_map_t::iterator iter = mActionIcons.find("full");
                    if (iter != mActionIcons.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 = mActionIcons.find("partial");
                    if (iter != mActionIcons.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"] = "SANSSERIF_SMALL";

            if(mHasGroupBanPower)
            {
                // The ban ability is being set. Prevent these abilities from being manipulated
                if((*ra_it)->mPowerBit == GP_MEMBER_EJECT)
                {
                    row["enabled"] = false;
                }
                else if((*ra_it)->mPowerBit == GP_ROLE_REMOVE_MEMBER)
                {
                    row["enabled"] = false;
                }
            }
            else
            {
                // The ban ability is not set. Allow these abilities to be manipulated
                if((*ra_it)->mPowerBit == GP_MEMBER_EJECT)
                {
                    row["enabled"] = true;
                }
                else if((*ra_it)->mPowerBit == GP_ROLE_REMOVE_MEMBER)
                {
                    row["enabled"] = true;
                }
            }

            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->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);
                    }
                }

                // Regardless of whether or not this ability is allowed by all or some, we want to prevent
                // the group managers from accidentally disabling either of the two additional abilities
                // tied with GP_GROUP_BAN_ACCESS.
                if( (allowed_by_all & GP_GROUP_BAN_ACCESS) == GP_GROUP_BAN_ACCESS ||
                    (allowed_by_some & GP_GROUP_BAN_ACCESS) == GP_GROUP_BAN_ACCESS)
                {
                    mHasGroupBanPower = 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 LLPanelInjector<LLPanelGroupMembersSubTab> t_panel_group_members_subtab("panel_group_members_subtab");

LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab()
:   LLPanelGroupSubTab(),
    mMembersList(NULL),
    mAssignedRolesList(NULL),
    mAllowedActionsList(NULL),
    mChanged(false),
    mPendingMemberUpdate(false),
    mHasMatch(false),
    mNumOwnerAdditions(0)
{
}

LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
{
    for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
    {
        if (it->second.connected())
        {
            it->second.disconnect();
        }
    }
    mAvatarNameCacheConnections.clear();
    if (mMembersList)
    {
        gSavedSettings.setString("GroupMembersSortOrder", mMembersList->getSortColumnName());
    }
}

bool LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root)
{
    LLPanelGroupSubTab::postBuildSubTab(root);

    // Look recursively from the parent to find all our widgets.
    mHeader = root->findChild<LLPanel>("members_header");
    mFooter = root->findChild<LLPanel>("members_footer");

    mMembersList = root->getChild<LLNameListCtrl>("member_list");
    mAssignedRolesList = root->getChild<LLScrollListCtrl>("member_assigned_roles");
    mAllowedActionsList = root->getChild<LLScrollListCtrl>("member_allowed_actions");
    mActionDescription = root->getChild<LLTextEditor>("member_action_description");

    mAllowedActionsList->setCommitOnSelectionChange(true);
    mAllowedActionsList->setCommitCallback(boost::bind(&LLPanelGroupMembersSubTab::updateActionDescription, this));

    // We want to be notified whenever a member is selected.
    mMembersList->setCommitOnSelectionChange(true);
    mMembersList->setCommitCallback(onMemberSelect, this);
    // Show the member's profile on double click.
    mMembersList->setDoubleClickCallback(onMemberDoubleClick, this);
    mMembersList->setContextMenu(LLScrollListCtrl::MENU_AVATAR);
    mMembersList->setIsFriendCallback(LLAvatarActions::isFriend);

    LLSD row;
    row["columns"][0]["column"] = "name";
    row["columns"][1]["column"] = "donated";
    row["columns"][2]["column"] = "online";
    mMembersList->addElement(row);
    std::string order_by = gSavedSettings.getString("GroupMembersSortOrder");
    if (!order_by.empty())
    {
        mMembersList->sortByColumn(order_by, true);
    }

    LLButton* button = root->getChild<LLButton>("member_invite");
    button->setClickedCallback(onInviteMember, this);
    button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));

    mEjectBtn = root->getChild<LLButton>("member_eject");
    mEjectBtn->setClickedCallback(onEjectMembers, this);
    mEjectBtn->setEnabled(false);

    mBanBtn = root->getChild<LLButton>("member_ban");
    mBanBtn->setClickedCallback(onBanMember, this);
    mBanBtn->setEnabled(false);

    return true;
}

void LLPanelGroupMembersSubTab::setGroupID(const LLUUID& id)
{
    //clear members list
    if(mMembersList) mMembersList->deleteAllItems();
    if(mAssignedRolesList) mAssignedRolesList->deleteAllItems();
    if(mAllowedActionsList) mAllowedActionsList->deleteAllItems();

    LLPanelGroupSubTab::setGroupID(id);
}

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

void LLPanelGroupMembersSubTab::handleMemberSelect()
{
    LL_DEBUGS() << "LLPanelGroupMembersSubTab::handleMemberSelect" << LL_ENDL;

    mAssignedRolesList->deleteAllItems();
    mAllowedActionsList->deleteAllItems();
    mActionDescription->clear();

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::handleMemberSelect() "
                << "-- No group data!" << LL_ENDL;
        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.
    uuid_vec_t selected_members;
    U64 allowed_by_all = GP_ALL_POWERS; //0xFFFFffffFFFFffffLL;
    U64 allowed_by_some = 0;

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

        selected_members.push_back( member_id );
        // Get this member's power mask including any unsaved changes

        U64 powers = getAgentPowersBasedOnRoleChanges( member_id );

        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,
                     NULL,
                     false,
                     false,
                     false);

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

    bool can_ban_members = gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS);
    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.
                uuid_vec_t::const_iterator member_iter;
                uuid_vec_t::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_list_t::iterator 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 && member_data->isInRole(gdatap->mOwnerRole) )
                    {
                        // Can't remove other owners.
                        cb_enable = false;
                        can_ban_members = false;
                        break;
                    }
                }
            }

            //now see if there are any role changes for the selected
            //members and remember to include them
            uuid_vec_t::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, this);
                check->set( count > 0 );
                check->setTentative(
                    (0 != count)
                    && (selected_members.size() !=
                        (uuid_vec_t::size_type)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);
                item->setEnabled(cb_enable);
            }
        }
        else
        {
            // This could happen if changes are not synced right on sub-panel change.
            LL_WARNS() << "No group role data for " << iter->second << LL_ENDL;
        }
    }
    mAssignedRolesList->setEnabled(true);

    if (gAgent.isGodlike())
    {
        can_eject_members = true;
    //  can_ban_members = true;
    }

    if (!can_eject_members && !member_is_owner)
    {
        // Maybe we can eject them because we are an owner...
        LLGroupMgrGroupData::member_list_t::iterator 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;
                //can_ban_members = true;
            }
        }

    }

    // ... or we can eject them because we have all the requisite powers...
    if( gAgent.hasPowerInGroup(mGroupID, GP_ROLE_REMOVE_MEMBER) &&
        !member_is_owner)
    {
        if( gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_EJECT))
        {
            can_eject_members = true;
        }

        if( gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
        {
            can_ban_members = true;
        }
    }


    uuid_vec_t::const_iterator member_iter = selected_members.begin();
    uuid_vec_t::const_iterator member_end = selected_members.end();
    for ( ; member_iter != member_end; ++member_iter)
    {
        // Don't count the agent.
        if ((*member_iter) == gAgent.getID())
        {
            can_eject_members = false;
            can_ban_members = false;
        }
    }

    mBanBtn->setEnabled(can_ban_members);
    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, NULL, false);
}

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

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

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

    auto selection_count = selection.size();
    if (selection_count == 1)
    {
        LLSD args;
        LLAvatarName av_name;
        LLAvatarNameCache::get(mMembersList->getValue(), &av_name);
        args["AVATAR_NAME"] = av_name.getUserName();
        LLSD payload;
        LLNotificationsUtil::add("EjectGroupMemberWarning",
                                 args,
                                 payload,
                                 boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
    }
    else
    {
        LLSD args;
        args["COUNT"] = llformat("%d", selection_count);
        LLSD payload;
        LLNotificationsUtil::add("EjectGroupMembersWarning",
                                 args,
                                 payload,
                                 boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
    }
}

void LLPanelGroupMembersSubTab::handleEjectMembers()
{
    //send down an eject message
    uuid_vec_t 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)
    {
        LLUUID member_id = (*itor)->getUUID();
        selected_members.push_back( member_id );
    }

    mMembersList->deleteSelectedItems();

    sendEjectNotifications(mGroupID, selected_members);

    LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID, selected_members);
}

bool LLPanelGroupMembersSubTab::handleEjectCallback(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    if (0 == option) // Eject button
    {
        handleEjectMembers();
    }
    return false;
}

void LLPanelGroupMembersSubTab::sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members)
{
    LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id);

    if (group_data)
    {
        for (uuid_vec_t::const_iterator i = selected_members.begin(); i != selected_members.end(); ++i)
        {
            LLSD args;
            args["AVATAR_NAME"] = LLSLURL("agent", *i, "completename").getSLURLString();
            args["GROUP_NAME"] = group_data->mName;

            LLNotifications::instance().add(LLNotification::Params("EjectAvatarFromGroup").substitutions(args));
        }
    }
}

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

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

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

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

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

        //see if we requested a change for this member before
        if ( mMemberRoleChangeData.find(member_id) == mMemberRoleChangeData.end() )
        {
            mMemberRoleChangeData[member_id] = new role_change_data_map_t;
        }
        role_change_data_map_t* role_change_datap = mMemberRoleChangeData[member_id];

        //now check to see if the selected group member
        //had changed his association with the selected role before

        role_change_data_map_t::iterator  role = role_change_datap->find(role_id);
        if ( role != role_change_datap->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,
                     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;

    LLScrollListItem* first_selected =
        self->mAssignedRolesList->getFirstSelected();
    if (first_selected)
    {
        LLUUID role_id = first_selected->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)
    {
        LLUUID member_id = selected->getUUID();
        LLAvatarActions::showProfile( member_id );
    }
}

void LLPanelGroupMembersSubTab::activate()
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

    LLPanelGroupSubTab::activate();
    if (!mActivated)
    {
        if (!gdatap || !gdatap->isMemberDataComplete())
        {
            const U32 page_size = 50;
            std::string sort_column_name = mMembersList->getSortColumnName();
            bool sort_descending = !mMembersList->getSortAscending();
            LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID, page_size, 0, sort_column_name, sort_descending);
        }

        if (!gdatap || !gdatap->isRoleMemberDataComplete())
        {
            LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID);
        }

        update(GC_ALL);
        mActivated = true;
    }
    else
    {
        // Members can be removed outside of this tab, checking changes
        if (!gdatap || (gdatap->isMemberDataComplete() && gdatap->mMembers.size() != mMembersList->getItemCount()))
        {
            update(GC_MEMBER_DATA);
        }
    }
    mActionDescription->clear();
}

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

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

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

        mChanged = false;
        notifyObservers();
    }
}

bool LLPanelGroupMembersSubTab::apply(std::string& mesg)
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "Unable to get group data for group " << mGroupID << LL_ENDL;

        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;
            LLSD args;

            if ( gdatap->getRoleData(gdatap->mOwnerRole, rd) )
            {
                mHasModal = true;
                args["ROLE_NAME"] = rd.mRoleName;
                LLNotificationsUtil::add("AddGroupOwnerWarning",
                                        args,
                                        LLSD(),
                                        boost::bind(&LLPanelGroupMembersSubTab::addOwnerCB, this, _1, _2));
            }
            else
            {
                LL_WARNS() << "Unable to get role information for the owner role in group " << mGroupID << LL_ENDL;

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

        }
        else
        {
            applyMemberChanges();
        }
    }

    return true;
}

bool LLPanelGroupMembersSubTab::addOwnerCB(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    mHasModal = false;

    if (0 == option)
    {
        // User clicked "Yes"
        applyMemberChanges();
    }
    return false;
}

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

    // We need to add all of the changed roles data for each member whose role changed
    for (member_role_changes_map_t::iterator member = mMemberRoleChangeData.begin();
         member != mMemberRoleChangeData.end(); ++member)
    {
        for (role_change_data_map_t::iterator role = member->second->begin();
             role != member->second->end(); ++role)
        {
            gdatap->changeRoleMember(role->first, //role_id
                                     member->first, //member_id
                                     role->second); //add/remove
        }

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

    LLGroupMgr::getInstance()->sendGroupRoleMemberChanges(mGroupID);
    // Force an UI update
    handleMemberSelect();

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

bool LLPanelGroupMembersSubTab::matchesSearchFilter(const std::string& fullname)
{
    // If the search filter is empty, everything passes.
    if (mSearchFilter.empty())
        return true;

    // Create a full name, and compare it to the search filter.
    std::string fullname_lc(fullname);
    LLStringUtil::toLower(fullname_lc);

    std::string::size_type match = fullname_lc.find(mSearchFilter);
    return match != std::string::npos;
}

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

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No group data!" << LL_ENDL;
        return GP_NO_POWERS;
    }

    LLGroupMgrGroupData::member_list_t::iterator iter = gdatap->mMembers.find(agent_id);
    if (iter == gdatap->mMembers.end())
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << LL_ENDL;
        return GP_NO_POWERS;
    }

    LLGroupMemberData* member_data = (*iter).second;
    if (!member_data)
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- Null member data for member with UUID " << agent_id << LL_ENDL;
        return GP_NO_POWERS;
    }

    // See if there are unsaved role changes for this agent
    role_change_data_map_t* role_change_datap = NULL;
    member_role_changes_map_t::iterator member = mMemberRoleChangeData.find(agent_id);
    if (member != mMemberRoleChangeData.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)
    {
        uuid_vec_t roles_to_be_removed;

        for (const auto& role : *role_change_datap)
        {
            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)
        for (LLGroupMemberData::role_list_t::iterator current_role = member_data->roleBegin();
             current_role != member_data->roleEnd(); ++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_changes_map_t::iterator member_changes_iter = mMemberRoleChangeData.find(member_id);
    if ( member_changes_iter != mMemberRoleChangeData.end() )
    {
        role_change_data_map_t::iterator 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 = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::update() -- No group data!" << LL_ENDL;
        return;
    }

    // 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->isRoleDataComplete() && gdatap->isMemberDataComplete() && !gdatap->mMembers.size())
        {
            // MAINT-5237
            retrieved << "Member list not available.";
        }
        else 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->setCommentText(retrieved.str());
    }
}

void LLPanelGroupMembersSubTab::addMemberToList(LLGroupMemberData* data)
{
    if (!data)
        return;

    LLUIString donated = getString("donation_area");
    donated.setArg("[AREA]", llformat("%d", data->getContribution()));

    LLNameListCtrl::NameItem item_params;
    item_params.value = data->getID();

    item_params.columns.add().column("name").font.name("SANSSERIF_SMALL").style("NORMAL");

    item_params.columns.add().column("donated").value(donated.getString())
            .font.name("SANSSERIF_SMALL").style("NORMAL");

    item_params.columns.add().column("online").value(data->getOnlineStatus())
            .font.name("SANSSERIF_SMALL").style("NORMAL");

    item_params.columns.add().column("title").value(data->getTitle()).font.name("SANSSERIF_SMALL").style("NORMAL");;

    mMembersList->addNameItemRow(item_params);

    mHasMatch = true;
}

void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name, const LLUUID& av_id)
{
    avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(av_id);
    if (it != mAvatarNameCacheConnections.end())
    {
        if (it->second.connected())
        {
            it->second.disconnect();
        }
        mAvatarNameCacheConnections.erase(it);
    }

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap
        || gdatap->getMemberVersion() != update_id
        || !member)
    {
        return;
    }

    // trying to avoid unnecessary hash lookups
    if (matchesSearchFilter(av_name.getAccountName()))
    {
        addMemberToList(member);
        if (!mMembersList->getEnabled())
        {
            mMembersList->setEnabled(true);
        }
    }

}

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

    // Rebuild the members list.

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << LL_ENDL;
        return;
    }

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

    // Cleanup list only for first iteration
    if (mMemberProgress == gdatap->mMembers.begin())
    {
        mMembersList->deleteAllItems();
    }

    LLGroupMgrGroupData::member_list_t::iterator end = gdatap->mMembers.end();

    LLTimer update_time;
    update_time.setTimerExpirySec(UPDATE_MEMBERS_SECONDS_PER_FRAME);

    for (; mMemberProgress != end && !update_time.hasExpired(); ++mMemberProgress)
    {
        if (!mMemberProgress->second)
            continue;

        // Do filtering on name if it is already in the cache.
        LLAvatarName av_name;
        if (LLAvatarNameCache::get(mMemberProgress->first, &av_name))
        {
            if (matchesSearchFilter(av_name.getAccountName()))
            {
                addMemberToList(mMemberProgress->second);
            }
        }
        else
        {
            // If name is not cached, onNameCache() should be called when it is cached and add this member to list.
            avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(mMemberProgress->first);
            if (it != mAvatarNameCacheConnections.end())
            {
                if (it->second.connected())
                {
                    it->second.disconnect();
                }
                mAvatarNameCacheConnections.erase(it);
            }
            mAvatarNameCacheConnections[mMemberProgress->first] = LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupMembersSubTab::onNameCache, this, gdatap->getMemberVersion(), mMemberProgress->second, _2, _1));
        }
    }

    if (mMemberProgress == end)
    {
        if (mHasMatch)
        {
            mMembersList->setEnabled(true);
        }
        else if (gdatap->mMembers.size())
        {
            mMembersList->setEnabled(false);
            mMembersList->setCommentText(std::string("No match."));
        }
    }
    else
    {
        mPendingMemberUpdate = true;
    }

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

void LLPanelGroupMembersSubTab::onBanMember(void* user_data)
{
    LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
    self->confirmBanMembers();
}

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

    auto selection_count = selection.size();
    if (selection_count == 1)
    {
        LLSD args;
        LLAvatarName av_name;
        LLAvatarNameCache::get(mMembersList->getValue(), &av_name);
        args["AVATAR_NAME"] = av_name.getUserName();
        LLSD payload;
        LLNotificationsUtil::add("BanGroupMemberWarning",
                                 args,
                                 payload,
                                 boost::bind(&LLPanelGroupMembersSubTab::handleBanCallback, this, _1, _2));
    }
    else
    {
        LLSD args;
        args["COUNT"] = llformat("%d", selection_count);
        LLSD payload;
        LLNotificationsUtil::add("BanGroupMembersWarning",
                                 args,
                                 payload,
                                 boost::bind(&LLPanelGroupMembersSubTab::handleBanCallback, this, _1, _2));
    }
}

bool LLPanelGroupMembersSubTab::handleBanCallback(const LLSD& notification, const LLSD& response)
{
    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    if (0 == option) // Eject button
    {
        handleBanMember();
    }
    return false;
}

void LLPanelGroupMembersSubTab::updateActionDescription()
{
    mActionDescription->setText(std::string());
    LLScrollListItem* action_item = mAllowedActionsList->getFirstSelected();
    if (!action_item || !mAllowedActionsList->getCanSelect())
    {
        return;
    }

    if (LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata())
    {
        std::string desc = rap->mLongDescription.empty() ? rap->mDescription : rap->mLongDescription;
        mActionDescription->setText(desc);
    }
}

void LLPanelGroupMembersSubTab::handleBanMember()
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
        return;
    }

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

    uuid_vec_t ban_ids;
    for (const LLScrollListItem* item : selection)
    {
        LLUUID ban_id = item->getUUID();
        ban_ids.push_back(ban_id);

        LLGroupBanData ban_data;
        gdatap->createBanEntry(ban_id, ban_data);
    }

    LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mGroupID, LLGroupMgr::BAN_CREATE, ban_ids);
    handleEjectMembers();
}


// LLPanelGroupRolesSubTab ///////////////////////////////////////////////
static LLPanelInjector<LLPanelGroupRolesSubTab> t_panel_group_roles_subtab("panel_group_roles_subtab");

LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab()
  : LLPanelGroupSubTab(),
    mRolesList(NULL),
    mAssignedMembersList(NULL),
    mAllowedActionsList(NULL),
    mRoleName(NULL),
    mRoleTitle(NULL),
    mRoleDescription(NULL),
    mMemberVisibleCheck(NULL),
    mDeleteRoleButton(NULL),
    mCopyRoleButton(NULL),
    mCreateRoleButton(NULL),
    mFirstOpen(true),
    mHasRoleChange(false)
{
}

LLPanelGroupRolesSubTab::~LLPanelGroupRolesSubTab()
{
}

bool LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root)
{
    LLPanelGroupSubTab::postBuildSubTab(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.
    mHeader = parent->findChild<LLPanel>("roles_header");
    mFooter = parent->findChild<LLPanel>("roles_footer");

    mRolesList = parent->getChild<LLScrollListCtrl>("role_list");
    mAssignedMembersList = parent->getChild<LLNameListCtrl>("role_assigned_members");
    mAllowedActionsList = parent->getChild<LLScrollListCtrl>("role_allowed_actions");
    mActionDescription  = parent->getChild<LLTextEditor>("role_action_description");

    mRoleName = parent->getChild<LLLineEditor>("role_name");
    mRoleTitle = parent->getChild<LLLineEditor>("role_title");
    mRoleDescription = parent->getChild<LLTextEditor>("role_description");

    mMemberVisibleCheck = parent->getChild<LLCheckBoxCtrl>("role_visible_in_list");

    mRemoveEveryoneTxt = getString("cant_delete_role");

    mCreateRoleButton = parent->getChild<LLButton>("role_create");
    mCreateRoleButton->setClickedCallback(onCreateRole, this);
    mCreateRoleButton->setEnabled(false);

    mCopyRoleButton = parent->getChild<LLButton>("role_copy");
    mCopyRoleButton->setClickedCallback(onCopyRole, this);
    mCopyRoleButton->setEnabled(false);

    mDeleteRoleButton = parent->getChild<LLButton>("role_delete");
    mDeleteRoleButton->setClickedCallback(onDeleteRole, this);
    mDeleteRoleButton->setEnabled(false);

    mRolesList->setCommitOnSelectionChange(true);
    mRolesList->setCommitCallback(onRoleSelect, this);

    mAssignedMembersList->setContextMenu(LLScrollListCtrl::MENU_AVATAR);

    mMemberVisibleCheck->setCommitCallback(onMemberVisibilityChange, this);

    mAllowedActionsList->setCommitOnSelectionChange(true);
    mAllowedActionsList->setCommitCallback(boost::bind(&LLPanelGroupRolesSubTab::updateActionDescription, this));

    mRoleName->setCommitOnFocusLost(true);
    mRoleName->setKeystrokeCallback(onPropertiesKey, this);

    mRoleTitle->setCommitOnFocusLost(true);
    mRoleTitle->setKeystrokeCallback(onPropertiesKey, this);

    mRoleDescription->setCommitOnFocusLost(true);
    mRoleDescription->setKeystrokeCallback(boost::bind(&LLPanelGroupRolesSubTab::onDescriptionKeyStroke, this, _1));

    setFooterEnabled(false);

    return true;
}

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

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

    setFooterEnabled(false);

    mHasRoleChange = false;
    update(GC_ALL);
}

void LLPanelGroupRolesSubTab::deactivate()
{
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::deactivate()" << LL_ENDL;

    LLPanelGroupSubTab::deactivate();
    mFirstOpen = false;
}

bool LLPanelGroupRolesSubTab::needsApply(std::string& mesg)
{
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::needsApply()" << LL_ENDL;

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if(!gdatap)
    {
        LL_WARNS() << "Unable to get group data for group " << mGroupID << LL_ENDL;
        return false;
    }


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

bool LLPanelGroupRolesSubTab::apply(std::string& mesg)
{
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::apply()" << LL_ENDL;

    saveRoleChanges(true);
    mFirstOpen = false;
    LLGroupMgr::getInstance()->sendGroupRoleChanges(mGroupID);

    notifyObservers();

    return true;
}

void LLPanelGroupRolesSubTab::cancel()
{
    mHasRoleChange = false;
    LLGroupMgr::getInstance()->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;

    LLStringUtil::toLower(rolename);
    LLStringUtil::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)
{
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::update()" << LL_ENDL;

    if (mGroupID.isNull()) return;

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

    if (!gdatap || !gdatap->isRoleDataComplete())
    {
        LLGroupMgr::getInstance()->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_list_t::iterator rit = gdatap->mRoles.begin();
        LLGroupMgrGroupData::role_list_t::iterator 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() ? static_cast<S32>(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
            {
                LL_WARNS() << "LLPanelGroupRolesSubTab::update() No role data for role " << (*rit).first << LL_ENDL;
            }
        }

        mRolesList->sortByColumn(std::string("name"), true);

        if ( (gdatap->mRoles.size() < (U32)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);
            mCopyRoleButton->setEnabled(false);
        }
    }

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

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

    self->handleRoleSelect();
}

void LLPanelGroupRolesSubTab::handleRoleSelect()
{
    bool can_delete = true;
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::handleRoleSelect()" << LL_ENDL;

    mAssignedMembersList->deleteAllItems();
    mAllowedActionsList->deleteAllItems();

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupRolesSubTab::handleRoleSelect() "
                << "-- No group data!" << LL_ENDL;
        return;
    }

    saveRoleChanges(false);

    // 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,
                         boost::bind(&LLPanelGroupRolesSubTab::handleActionCheck, this, _1, false),
                         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));

        if ( is_owner_role )
            {
                // you can't delete the owner role
                can_delete = false;
                // ... or hide members with this role
                mMemberVisibleCheck->setEnabled(false);
            }
        else
            {
                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;
        }
    }
    else
    {
        mRolesList->deselectAllItems();
        mAssignedMembersList->deleteAllItems();
        mAllowedActionsList->deleteAllItems();
        mRoleName->clear();
        mRoleDescription->clear();
        mRoleTitle->clear();
        setFooterEnabled(false);

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

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

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

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupRolesSubTab::handleRoleSelect() "
                << "-- No group data!" << LL_ENDL;
        return;
    }

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

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

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

void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force)
{
    LLCheckBoxCtrl* check = dynamic_cast<LLCheckBoxCtrl*>(ctrl);
    if (!check)
        return;

    LL_DEBUGS() << "LLPanelGroupRolesSubTab::handleActionSelect()" << LL_ENDL;

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupRolesSubTab::handleRoleSelect() "
                << "-- No group data!" << LL_ENDL;
        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;

    bool isEnablingAbility = check->get();
    LLRoleData rd;
    LLSD args;

    if (isEnablingAbility &&
        !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;
        LLSD args;

        if ( gdatap->getRoleData(role_id, rd) )
        {
            args["ACTION_NAME"] = rap->mDescription;
            args["ROLE_NAME"] = rd.mRoleName;
            mHasModal = true;
            std::string warning = "AssignDangerousActionWarning";
            if (GP_ROLE_CHANGE_ACTIONS == power)
            {
                warning = "AssignDangerousAbilityWarning";
            }
            LLNotificationsUtil::add(warning, args, LLSD(), boost::bind(&LLPanelGroupRolesSubTab::addActionCB, this, _1, _2, check));
        }
        else
        {
                LL_WARNS() << "Unable to look up role information for role id: "
                        << role_id << LL_ENDL;
        }
    }

    if(GP_GROUP_BAN_ACCESS == power)
    {
        std::string warning = isEnablingAbility ? "AssignBanAbilityWarning" : "RemoveBanAbilityWarning";

        //////////////////////////////////////////////////////////////////////////
        // Get role data for both GP_ROLE_REMOVE_MEMBER and GP_MEMBER_EJECT
        // Add description and role name to LLSD
        // Pop up dialog saying "Yo, you also granted these other abilities when you did this!"
        if ( gdatap->getRoleData(role_id, rd) )
        {
            args["ACTION_NAME"] = rap->mDescription;
            args["ROLE_NAME"] = rd.mRoleName;
            mHasModal = true;

            std::vector<LLScrollListItem*> all_data = mAllowedActionsList->getAllData();
            std::vector<LLScrollListItem*>::iterator ad_it = all_data.begin();
            std::vector<LLScrollListItem*>::iterator ad_end = all_data.end();
            LLRoleAction* adp;
            for( ; ad_it != ad_end; ++ad_it)
            {
                adp = (LLRoleAction*)(*ad_it)->getUserdata();
                if(adp->mPowerBit == GP_MEMBER_EJECT)
                {
                    args["ACTION_NAME_2"] = adp->mDescription;
                }
                else if(adp->mPowerBit == GP_ROLE_REMOVE_MEMBER)
                {
                    args["ACTION_NAME_3"] = adp->mDescription;
                }
            }

            LLNotificationsUtil::add(warning, args);
        }
        else
        {
            LL_WARNS() << "Unable to look up role information for role id: "
                << role_id << LL_ENDL;
        }

        //////////////////////////////////////////////////////////////////////////

        U64 current_role_powers = gdatap->getRolePowers(role_id);

        if(isEnablingAbility)
        {
            power |= (GP_ROLE_REMOVE_MEMBER | GP_MEMBER_EJECT);
            current_role_powers |= power;
        }
        else
        {
            current_role_powers &= ~GP_GROUP_BAN_ACCESS;
        }

        mAllowedActionsList->deleteAllItems();
        buildActionsList(   mAllowedActionsList,
            current_role_powers,
            current_role_powers,
            boost::bind(&LLPanelGroupRolesSubTab::handleActionCheck, this, _1, false),
            true,
            false,
            false);

    }

    //////////////////////////////////////////////////////////////////////////
    // Adding non-specific ability to role
    //////////////////////////////////////////////////////////////////////////
    if(isEnablingAbility)
    {
        gdatap->addRolePower(role_id, power);
    }
    else
    {
        gdatap->removeRolePower(role_id,power);
    }

    mHasRoleChange = true;
    notifyObservers();

}

bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD& response, LLCheckBoxCtrl* check)
{
    if (!check) return false;

    mHasModal = false;

    S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
    if (0 == option)
    {
        // User clicked "Yes"
        check->set(true);
        const bool force_add = true;
        handleActionCheck(check, force_add);
    }
    return false;
}

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

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

void LLPanelGroupRolesSubTab::onDescriptionKeyStroke(LLTextEditor* caller)
{
    mHasRoleChange = true;
    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)
{
    LL_DEBUGS() << "LLPanelGroupRolesSubTab::handleMemberVisibilityChange()" << LL_ENDL;

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (!gdatap)
    {
        LL_WARNS() << "LLPanelGroupRolesSubTab::handleRoleSelect() "
                << "-- No group data!" << LL_ENDL;
        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 = LLGroupMgr::getInstance()->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::onCopyRole(void* user_data)
{
    LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
    if (!self) return;

    self->handleCopyRole();
}

void LLPanelGroupRolesSubTab::handleCopyRole()
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

    if (!gdatap) return;

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

    LLRoleData rd;
    if (!gdatap->getRoleData(role_item->getUUID(), rd))
    {
        return;
    }

    LLUUID new_role_id;
    new_role_id.generate();
    rd.mRoleName += "(Copy)";
    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 = LLGroupMgr::getInstance()->getGroupData(mGroupID);

    if (!gdatap) return;

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

    if (role_item->getUUID().isNull() || role_item->getUUID() == gdatap->mOwnerRole)
    {
        LLSD args;
        args["MESSAGE"] = mRemoveEveryoneTxt;
        LLNotificationsUtil::add("GenericAlert", args);
        return;
    }

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

    notifyObservers();
}

void LLPanelGroupRolesSubTab::saveRoleChanges(bool select_saved_role)
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->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();

        S32 role_members_count = 0;
        if (mSelectedRole.isNull())
        {
            role_members_count = gdatap->mMemberCount;
        }
        else if(LLGroupRoleData* grd = get_ptr_in_map(gdatap->mRoles, mSelectedRole))
        {
            role_members_count = grd->getTotalMembersInRole();
        }

        gdatap->setRoleData(mSelectedRole,rd);

        mRolesList->deleteSingleItem(mRolesList->getItemIndex(mSelectedRole));

        LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,role_members_count);
        LLScrollListItem* item = mRolesList->addElement(row, ADD_BOTTOM, this);
        item->setSelected(select_saved_role);

        mHasRoleChange = false;
    }
}

void LLPanelGroupRolesSubTab::updateActionDescription()
{
    mActionDescription->setText(std::string());
    LLScrollListItem* action_item = mAllowedActionsList->getFirstSelected();
    if (!action_item || !mAllowedActionsList->getCanSelect())
    {
        return;
    }

    LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata();
    if (rap)
    {
        std::string desc = rap->mLongDescription.empty() ? rap->mDescription : rap->mLongDescription;
        mActionDescription->setText(desc);
    }
}

void LLPanelGroupRolesSubTab::setGroupID(const LLUUID& id)
{
    if(mRolesList) mRolesList->deleteAllItems();
    if(mAssignedMembersList) mAssignedMembersList->deleteAllItems();
    if(mAllowedActionsList) mAllowedActionsList->deleteAllItems();

    if(mRoleName) mRoleName->clear();
    if(mRoleDescription) mRoleDescription->clear();
    if(mRoleTitle) mRoleTitle->clear();

    mHasRoleChange = false;

    setFooterEnabled(false);

    LLPanelGroupSubTab::setGroupID(id);
}


// LLPanelGroupActionsSubTab /////////////////////////////////////////////
static LLPanelInjector<LLPanelGroupActionsSubTab> t_panel_group_actions_subtab("panel_group_actions_subtab");

LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab()
: LLPanelGroupSubTab()
{
}

LLPanelGroupActionsSubTab::~LLPanelGroupActionsSubTab()
{
}

bool LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root)
{
    LLPanelGroupSubTab::postBuildSubTab(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 = parent->findChild<LLPanel>("actions_header", recurse);
    mFooter = parent->findChild<LLPanel>("actions_footer", recurse);

    mActionDescription = parent->getChild<LLTextEditor>("action_description", recurse);

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

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

    mActionList->setCommitOnSelectionChange(true);
    mActionList->setCommitCallback(boost::bind(&LLPanelGroupActionsSubTab::handleActionSelect, this));
    mActionList->setContextMenu(LLScrollListCtrl::MENU_AVATAR);

    update(GC_ALL);

    return true;
}

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

    update(GC_ALL);
    mActionDescription->clear();
    mActionList->deselectAllItems();
    mActionList->deleteAllItems();
    buildActionsList(mActionList,
                     GP_ALL_POWERS,
                     GP_ALL_POWERS,
                     NULL,
                     false,
                     true,
                     false);
}

void LLPanelGroupActionsSubTab::deactivate()
{
    LL_DEBUGS() << "LLPanelGroupActionsSubTab::deactivate()" << LL_ENDL;

    LLPanelGroupSubTab::deactivate();
}

bool LLPanelGroupActionsSubTab::needsApply(std::string& mesg)
{
    LL_DEBUGS() << "LLPanelGroupActionsSubTab::needsApply()" << LL_ENDL;

    return false;
}

bool LLPanelGroupActionsSubTab::apply(std::string& mesg)
{
    LL_DEBUGS() << "LLPanelGroupActionsSubTab::apply()" << LL_ENDL;
    return true;
}

void LLPanelGroupActionsSubTab::update(LLGroupChange gc)
{
    LL_DEBUGS() << "LLPanelGroupActionsSubTab::update()" << LL_ENDL;

    if (mGroupID.isNull()) return;

    mActionMembers->deleteAllItems();
    mActionRoles->deleteAllItems();

    if(mActionList->hasSelectedItem())
    {
        LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
        if (gdatap && gdatap->isMemberDataComplete() && gdatap->isRoleDataComplete())
        {
            handleActionSelect();
        }
    }
}

void LLPanelGroupActionsSubTab::onFilterChanged()
{
    mActionDescription->clear();
    mActionList->deselectAllItems();
    mActionList->deleteAllItems();
    buildActionsList(mActionList,
        GP_ALL_POWERS,
        GP_ALL_POWERS,
        NULL,
        false,
        true,
        false);
}

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

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

    U64 power_mask = GP_NO_POWERS;
    for (const LLScrollListItem* item : selection)
    {
        power_mask |= ((LLRoleAction*)item->getUserdata())->mPowerBit;
    }

    if (selection.size() == 1)
    {
        LLRoleAction* rap = (LLRoleAction*)selection.front()->getUserdata();
        if (rap->mLongDescription.empty())
        {
            mActionDescription->setText(rap->mDescription);
        }
        else
        {
            mActionDescription->setText(rap->mLongDescription);
        }
    }
    else
    {
        mActionDescription->clear();
    }

    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

    if (!gdatap)
        return;

    if (gdatap->isMemberDataComplete())
    {
        for (const auto& it : gdatap->mMembers)
        {
            if (LLGroupMemberData* gmd = it.second)
            {
                if ((gmd->getAgentPowers() & power_mask) == power_mask)
                {
                    mActionMembers->addNameItem(gmd->getID());
                }
            }
        }
    }
    else
    {
        LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID);
    }

    if (gdatap->isRoleDataComplete())
    {
        LLGroupMgrGroupData::role_list_t::iterator it = gdatap->mRoles.begin();
        LLGroupMgrGroupData::role_list_t::iterator 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->addSimpleElement(rmd->getRoleData().mRoleName);
            }
        }
    }
    else
    {
        LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID);
    }
}

void LLPanelGroupActionsSubTab::setGroupID(const LLUUID& id)
{
    if(mActionList) mActionList->deleteAllItems();
    if(mActionRoles) mActionRoles->deleteAllItems();
    if(mActionMembers) mActionMembers->deleteAllItems();

    if(mActionDescription) mActionDescription->clear();

    LLPanelGroupSubTab::setGroupID(id);
}


// LLPanelGroupBanListSubTab /////////////////////////////////////////////
static LLPanelInjector<LLPanelGroupBanListSubTab> t_panel_group_ban_subtab("panel_group_banlist_subtab");

LLPanelGroupBanListSubTab::LLPanelGroupBanListSubTab()
    : LLPanelGroupSubTab(),
      mBanList(NULL),
      mCreateBanButton(NULL),
      mDeleteBanButton(NULL)
{}

bool LLPanelGroupBanListSubTab::postBuildSubTab(LLView* root)
{
    LLPanelGroupSubTab::postBuildSubTab(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 = parent->findChild<LLPanel>("banlist_header", recurse);
    mFooter = parent->findChild<LLPanel>("banlist_footer", recurse);

    mBanList = parent->getChild<LLNameListCtrl>("ban_list", recurse);

    mCreateBanButton        = parent->getChild<LLButton>("ban_create", recurse);
    mDeleteBanButton        = parent->getChild<LLButton>("ban_delete", recurse);
    mRefreshBanListButton   = parent->getChild<LLButton>("ban_refresh", recurse);
    mBanCountText           = parent->getChild<LLTextBase>("ban_count", recurse);

    if(!mBanList || !mCreateBanButton || !mDeleteBanButton || !mRefreshBanListButton || !mBanCountText)
        return false;

    mBanList->setCommitOnSelectionChange(true);
    mBanList->setCommitCallback(onBanEntrySelect, this);

    mCreateBanButton->setClickedCallback(onCreateBanEntry, this);
    mCreateBanButton->setEnabled(false);

    mDeleteBanButton->setClickedCallback(onDeleteBanEntry, this);
    mDeleteBanButton->setEnabled(false);

    mRefreshBanListButton->setClickedCallback(onRefreshBanList, this);
    mRefreshBanListButton->setEnabled(false);

    setBanCount(0);

    mBanList->setOnNameListCompleteCallback(boost::bind(&LLPanelGroupBanListSubTab::onBanListCompleted, this, _1));

    populateBanList();

    setFooterEnabled(false);
    return true;
}

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

    mBanList->deselectAllItems();
    mDeleteBanButton->setEnabled(false);

    LLGroupMgrGroupData * group_datap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if (group_datap)
    {
        mCreateBanButton->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS) &&
                                     group_datap->mBanList.size() < GB_MAX_BANNED_AGENTS);
        setBanCount(static_cast<U32>(group_datap->mBanList.size()));
    }
    else
    {
        mCreateBanButton->setEnabled(false);
        setBanCount(0);
    }

    // BAKER: Should I really request everytime activate() is called?
    //        Perhaps I should only do it on a force refresh, or if an action on the list happens...
    //        Because it's not going to live-update the list anyway... You'd have to refresh if you
    //        wanted to see someone else's additions anyway...
    //
    LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);

    setFooterEnabled(false);
    update(GC_ALL);
}

void LLPanelGroupBanListSubTab::update(LLGroupChange gc)
{
    populateBanList();
}

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

    // BAKER: Might be good to put it here instead of update, maybe.. See how often draw gets hit.
    //if(
    //  populateBanList();
}

void LLPanelGroupBanListSubTab::onBanEntrySelect(LLUICtrl* ctrl, void* user_data)
{
    LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
    if (!self)
        return;

    self->handleBanEntrySelect();
}

void LLPanelGroupBanListSubTab::handleBanEntrySelect()
{
    if (gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
    {
        mDeleteBanButton->setEnabled(true);
    }
}

void LLPanelGroupBanListSubTab::onCreateBanEntry(void* user_data)
{
    LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
    if (!self)
        return;

    self->handleCreateBanEntry();
}

void LLPanelGroupBanListSubTab::handleCreateBanEntry()
{
    LLFloaterGroupBulkBan::showForGroup(mGroupID);
    //populateBanList();
}

void LLPanelGroupBanListSubTab::onDeleteBanEntry(void* user_data)
{
    LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
    if (!self)
        return;

    self->handleDeleteBanEntry();
}

void LLPanelGroupBanListSubTab::handleDeleteBanEntry()
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if(!gdatap)
    {
        LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
        return;
    }

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

    bool can_ban_members = false;
    if (gAgent.isGodlike() ||
        gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
    {
        can_ban_members = true;
    }

    // Owners can ban anyone in the group.
    LLGroupMgrGroupData::member_list_t::iterator 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_ban_members = true;
        }
    }

    if(!can_ban_members)
        return;

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

        gdatap->removeBanEntry(ban_id);
        mBanList->removeNameItem(ban_id);

        // Removing an item removes the selection, we shouldn't be able to click
        // the button anymore until we reselect another entry.
        mDeleteBanButton->setEnabled(false);
    }

    // update ban-count related elements
    mCreateBanButton->setEnabled(true);
    setBanCount(static_cast<U32>(gdatap->mBanList.size()));

    LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mGroupID, LLGroupMgr::BAN_DELETE, ban_ids);
}

void LLPanelGroupBanListSubTab::onRefreshBanList(void* user_data)
{
    LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
    if (!self)
        return;

    self->handleRefreshBanList();
}

void LLPanelGroupBanListSubTab::handleRefreshBanList()
{
    mRefreshBanListButton->setEnabled(false);
    LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
}

void LLPanelGroupBanListSubTab::onBanListCompleted(bool isComplete)
{
    if(isComplete)
    {
        mRefreshBanListButton->setEnabled(true);
        populateBanList();
    }
}

void LLPanelGroupBanListSubTab::setBanCount(U32 ban_count)
{
    LLStringUtil::format_map_t args;
    args["[COUNT]"] = llformat("%d", ban_count);
    args["[LIMIT]"] = llformat("%d", GB_MAX_BANNED_AGENTS);
    mBanCountText->setText(getString("ban_count_template", args));
}

void LLPanelGroupBanListSubTab::populateBanList()
{
    LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
    if(!gdatap)
    {
        LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
        return;
    }

    mBanList->deleteAllItems();
    std::map<LLUUID,LLGroupBanData>::const_iterator entry = gdatap->mBanList.begin();
    for(; entry != gdatap->mBanList.end(); entry++)
    {
        LLNameListCtrl::NameItem ban_entry;
        ban_entry.value = entry->first;
        LLGroupBanData bd = entry->second;

        ban_entry.columns.add().column("name").font.name("SANSSERIF_SMALL").style("NORMAL");

        // Baker TODO: MAINT-
        // Check out utc_to_pacific_time()

        std::string ban_date_str = bd.mBanDate.toHTTPDateString("%Y/%m/%d");
//      time_t utc_time;
//      utc_time = time_corrected();
//      LLSD substitution;
//      substitution["datetime"] = (S32) utc_time;
//      LLStringUtil::format (ban_date_str, substitution);

        //LL_INFOS("BAKER") << "[BAKER] BAN_DATE: " << bd.mBanDate.toHTTPDateString("%Y/%m/%d") << LL_ENDL;
        //LL_INFOS("BAKER") << "[BAKER] BAN_DATE_MODIFIED: " << ban_date_str << LL_ENDL;

        //ban_entry.columns.add().column("ban_date").value(ban_date_str.font.name("SANSSERIF_SMALL").style("NORMAL");
        ban_entry.columns.add().column("ban_date").value(bd.mBanDate.toHTTPDateString("%Y/%m/%d")).font.name("SANSSERIF_SMALL").style("NORMAL");

        mBanList->addNameItemRow(ban_entry);
    }

    mRefreshBanListButton->setEnabled(true);
    mCreateBanButton->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS) &&
                                 gdatap->mBanList.size() < GB_MAX_BANNED_AGENTS);
    setBanCount(static_cast<U32>(gdatap->mBanList.size()));
}

void LLPanelGroupBanListSubTab::setGroupID(const LLUUID& id)
{
    if(mBanList)
        mBanList->deleteAllItems();

    setFooterEnabled(false);
    LLPanelGroupSubTab::setGroupID(id);
}