/indra/newview/llparticipantlist.cpp
C++ | 1042 lines | 747 code | 144 blank | 151 comment | 168 complexity | 1bf64e9c0055814d8ec4d7254617f031 MD5 | raw file
Possible License(s): LGPL-2.1
1/** 2 * @file llparticipantlist.cpp 3 * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages 4 * 5 * $LicenseInfo:firstyear=2009&license=viewerlgpl$ 6 * Second Life Viewer Source Code 7 * Copyright (C) 2010, Linden Research, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; 12 * version 2.1 of the License only. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 24 * $/LicenseInfo$ 25 */ 26 27#include "llviewerprecompiledheaders.h" 28 29// common includes 30#include "lltrans.h" 31#include "llavataractions.h" 32#include "llagent.h" 33 34#include "llimview.h" 35#include "llnotificationsutil.h" 36#include "llparticipantlist.h" 37#include "llspeakers.h" 38#include "llviewercontrol.h" 39#include "llviewermenu.h" 40#include "llvoiceclient.h" 41 42//LLParticipantList retrieves add, clear and remove events and updates view accordingly 43#if LL_MSVC 44#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally 45#endif 46 47static const LLAvatarItemAgentOnTopComparator AGENT_ON_TOP_NAME_COMPARATOR; 48 49// helper function to update AvatarList Item's indicator in the voice participant list 50static void update_speaker_indicator(const LLAvatarList* const avatar_list, const LLUUID& avatar_uuid, bool is_muted) 51{ 52 LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(avatar_list->getItemByValue(avatar_uuid)); 53 if (item) 54 { 55 LLOutputMonitorCtrl* indicator = item->getChild<LLOutputMonitorCtrl>("speaking_indicator"); 56 indicator->setIsMuted(is_muted); 57 } 58} 59 60 61// See EXT-4301. 62/** 63 * class LLAvalineUpdater - observe the list of voice participants in session and check 64 * presence of Avaline Callers among them. 65 * 66 * LLAvalineUpdater is a LLVoiceClientParticipantObserver. It provides two kinds of validation: 67 * - whether Avaline caller presence among participants; 68 * - whether watched Avaline caller still exists in voice channel. 69 * Both validations have callbacks which will notify subscriber if any of event occur. 70 * 71 * @see findAvalineCaller() 72 * @see checkIfAvalineCallersExist() 73 */ 74class LLAvalineUpdater : public LLVoiceClientParticipantObserver 75{ 76public: 77 typedef boost::function<void(const LLUUID& speaker_id)> process_avaline_callback_t; 78 79 LLAvalineUpdater(process_avaline_callback_t found_cb, process_avaline_callback_t removed_cb) 80 : mAvalineFoundCallback(found_cb) 81 , mAvalineRemovedCallback(removed_cb) 82 { 83 LLVoiceClient::getInstance()->addObserver(this); 84 } 85 ~LLAvalineUpdater() 86 { 87 if (LLVoiceClient::instanceExists()) 88 { 89 LLVoiceClient::getInstance()->removeObserver(this); 90 } 91 } 92 93 /** 94 * Adds UUID of Avaline caller to watch. 95 * 96 * @see checkIfAvalineCallersExist(). 97 */ 98 void watchAvalineCaller(const LLUUID& avaline_caller_id) 99 { 100 mAvalineCallers.insert(avaline_caller_id); 101 } 102 103 void onParticipantsChanged() 104 { 105 uuid_set_t participant_uuids; 106 LLVoiceClient::getInstance()->getParticipantList(participant_uuids); 107 108 109 // check whether Avaline caller exists among voice participants 110 // and notify Participant List 111 findAvalineCaller(participant_uuids); 112 113 // check whether watched Avaline callers still present among voice participant 114 // and remove if absents. 115 checkIfAvalineCallersExist(participant_uuids); 116 } 117 118private: 119 typedef std::set<LLUUID> uuid_set_t; 120 121 /** 122 * Finds Avaline callers among voice participants and calls mAvalineFoundCallback. 123 * 124 * When Avatar is in group call with Avaline caller and then ends call Avaline caller stays 125 * in Group Chat floater (exists in LLSpeakerMgr). If Avatar starts call with that group again 126 * Avaline caller is added to voice channel AFTER Avatar is connected to group call. 127 * But Voice Control Panel (VCP) is filled from session LLSpeakerMgr and there is no information 128 * if a speaker is Avaline caller. 129 * 130 * In this case this speaker is created as avatar and will be recreated when it appears in 131 * Avatar's Voice session. 132 * 133 * @see LLParticipantList::onAvalineCallerFound() 134 */ 135 void findAvalineCaller(const uuid_set_t& participant_uuids) 136 { 137 uuid_set_t::const_iterator it = participant_uuids.begin(), it_end = participant_uuids.end(); 138 139 for(; it != it_end; ++it) 140 { 141 const LLUUID& participant_id = *it; 142 if (!LLVoiceClient::getInstance()->isParticipantAvatar(participant_id)) 143 { 144 LL_DEBUGS("Avaline") << "Avaline caller found among voice participants: " << participant_id << LL_ENDL; 145 146 if (mAvalineFoundCallback) 147 { 148 mAvalineFoundCallback(participant_id); 149 } 150 } 151 } 152 } 153 154 /** 155 * Finds Avaline callers which are not anymore among voice participants and calls mAvalineRemovedCallback. 156 * 157 * The problem is when Avaline caller ends a call it is removed from Voice Client session but 158 * still exists in LLSpeakerMgr. Server does not send such information. 159 * This method implements a HUCK to notify subscribers that watched Avaline callers by class 160 * are not anymore in the call. 161 * 162 * @see LLParticipantList::onAvalineCallerRemoved() 163 */ 164 void checkIfAvalineCallersExist(const uuid_set_t& participant_uuids) 165 { 166 uuid_set_t::iterator it = mAvalineCallers.begin(); 167 uuid_set_t::const_iterator participants_it_end = participant_uuids.end(); 168 169 while (it != mAvalineCallers.end()) 170 { 171 const LLUUID participant_id = *it; 172 LL_DEBUGS("Avaline") << "Check avaline caller: " << participant_id << LL_ENDL; 173 bool not_found = participant_uuids.find(participant_id) == participants_it_end; 174 if (not_found) 175 { 176 LL_DEBUGS("Avaline") << "Watched Avaline caller is not found among voice participants: " << participant_id << LL_ENDL; 177 178 // notify Participant List 179 if (mAvalineRemovedCallback) 180 { 181 mAvalineRemovedCallback(participant_id); 182 } 183 184 // remove from the watch list 185 mAvalineCallers.erase(it++); 186 } 187 else 188 { 189 ++it; 190 } 191 } 192 } 193 194 process_avaline_callback_t mAvalineFoundCallback; 195 process_avaline_callback_t mAvalineRemovedCallback; 196 197 uuid_set_t mAvalineCallers; 198}; 199 200LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, 201 LLAvatarList* avatar_list, 202 bool use_context_menu/* = true*/, 203 bool exclude_agent /*= true*/, 204 bool can_toggle_icons /*= true*/) : 205 mSpeakerMgr(data_source), 206 mAvatarList(avatar_list), 207 mParticipantListMenu(NULL), 208 mExcludeAgent(exclude_agent), 209 mValidateSpeakerCallback(NULL) 210{ 211 212 mAvalineUpdater = new LLAvalineUpdater(boost::bind(&LLParticipantList::onAvalineCallerFound, this, _1), 213 boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1)); 214 215 mSpeakerAddListener = new SpeakerAddListener(*this); 216 mSpeakerRemoveListener = new SpeakerRemoveListener(*this); 217 mSpeakerClearListener = new SpeakerClearListener(*this); 218 mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); 219 mSpeakerMuteListener = new SpeakerMuteListener(*this); 220 221 mSpeakerMgr->addListener(mSpeakerAddListener, "add"); 222 mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); 223 mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); 224 mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); 225 226 mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData")); 227 LL_DEBUGS("SpeakingIndicator") << "Set session for speaking indicators: " << mSpeakerMgr->getSessionID() << LL_ENDL; 228 mAvatarList->setSessionID(mSpeakerMgr->getSessionID()); 229 mAvatarListDoubleClickConnection = mAvatarList->setItemDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, _1)); 230 mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2)); 231 // Set onAvatarListDoubleClicked as default on_return action. 232 mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); 233 234 if (use_context_menu) 235 { 236 mParticipantListMenu = new LLParticipantListMenu(*this); 237 mAvatarList->setContextMenu(mParticipantListMenu); 238 } 239 else 240 { 241 mAvatarList->setContextMenu(NULL); 242 } 243 244 if (use_context_menu && can_toggle_icons) 245 { 246 mAvatarList->setShowIcons("ParticipantListShowIcons"); 247 mAvatarListToggleIconsConnection = gSavedSettings.getControl("ParticipantListShowIcons")->getSignal()->connect(boost::bind(&LLAvatarList::toggleIcons, mAvatarList)); 248 } 249 250 //Lets fill avatarList with existing speakers 251 LLSpeakerMgr::speaker_list_t speaker_list; 252 mSpeakerMgr->getSpeakerList(&speaker_list, true); 253 for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) 254 { 255 const LLPointer<LLSpeaker>& speakerp = *it; 256 257 addAvatarIDExceptAgent(speakerp->mID); 258 if ( speakerp->mIsModerator ) 259 { 260 mModeratorList.insert(speakerp->mID); 261 } 262 else 263 { 264 mModeratorToRemoveList.insert(speakerp->mID); 265 } 266 } 267 // we need to exclude agent id for non group chat 268 sort(); 269} 270 271LLParticipantList::~LLParticipantList() 272{ 273 mAvatarListDoubleClickConnection.disconnect(); 274 mAvatarListRefreshConnection.disconnect(); 275 mAvatarListReturnConnection.disconnect(); 276 mAvatarListToggleIconsConnection.disconnect(); 277 278 // It is possible Participant List will be re-created from LLCallFloater::onCurrentChannelChanged() 279 // See ticket EXT-3427 280 // hide menu before deleting it to stop enable and check handlers from triggering. 281 if(mParticipantListMenu && !LLApp::isExiting()) 282 { 283 mParticipantListMenu->hide(); 284 } 285 286 if (mParticipantListMenu) 287 { 288 delete mParticipantListMenu; 289 mParticipantListMenu = NULL; 290 } 291 292 mAvatarList->setContextMenu(NULL); 293 mAvatarList->setComparator(NULL); 294 295 delete mAvalineUpdater; 296} 297 298void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) 299{ 300 mAvatarList->setSpeakingIndicatorsVisible(visible); 301}; 302 303void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl) 304{ 305 LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl); 306 if(!item) 307 { 308 return; 309 } 310 311 LLUUID clicked_id = item->getAvatarId(); 312 313 if (clicked_id.isNull() || clicked_id == gAgent.getID()) 314 return; 315 316 LLAvatarActions::startIM(clicked_id); 317} 318 319void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) 320{ 321 LLAvatarList* list = dynamic_cast<LLAvatarList*>(ctrl); 322 if (list) 323 { 324 const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); 325 const std::size_t moderator_indicator_len = moderator_indicator.length(); 326 327 // Firstly remove moderators indicator 328 std::set<LLUUID>::const_iterator 329 moderator_list_it = mModeratorToRemoveList.begin(), 330 moderator_list_end = mModeratorToRemoveList.end(); 331 for (;moderator_list_it != moderator_list_end; ++moderator_list_it) 332 { 333 LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)); 334 if ( item ) 335 { 336 std::string name = item->getAvatarName(); 337 std::string tooltip = item->getAvatarToolTip(); 338 size_t found = name.find(moderator_indicator); 339 if (found != std::string::npos) 340 { 341 name.erase(found, moderator_indicator_len); 342 item->setAvatarName(name); 343 } 344 found = tooltip.find(moderator_indicator); 345 if (found != tooltip.npos) 346 { 347 tooltip.erase(found, moderator_indicator_len); 348 item->setAvatarToolTip(tooltip); 349 } 350 } 351 } 352 353 mModeratorToRemoveList.clear(); 354 355 // Add moderators indicator 356 moderator_list_it = mModeratorList.begin(); 357 moderator_list_end = mModeratorList.end(); 358 for (;moderator_list_it != moderator_list_end; ++moderator_list_it) 359 { 360 LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)); 361 if ( item ) 362 { 363 std::string name = item->getAvatarName(); 364 std::string tooltip = item->getAvatarToolTip(); 365 size_t found = name.find(moderator_indicator); 366 if (found == std::string::npos) 367 { 368 name += " "; 369 name += moderator_indicator; 370 item->setAvatarName(name); 371 } 372 found = tooltip.find(moderator_indicator); 373 if (found == std::string::npos) 374 { 375 tooltip += " "; 376 tooltip += moderator_indicator; 377 item->setAvatarToolTip(tooltip); 378 } 379 } 380 } 381 382 // update voice mute state of all items. See EXT-7235 383 LLSpeakerMgr::speaker_list_t speaker_list; 384 385 // Use also participants which are not in voice session now (the second arg is TRUE). 386 // They can already have mModeratorMutedVoice set from the previous voice session 387 // and LLSpeakerVoiceModerationEvent will not be sent when speaker manager is updated next time. 388 mSpeakerMgr->getSpeakerList(&speaker_list, TRUE); 389 for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) 390 { 391 const LLPointer<LLSpeaker>& speakerp = *it; 392 393 update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice); 394 } 395 } 396} 397 398/* 399 Seems this method is not necessary after onAvalineCallerRemoved was implemented; 400 401 It does nothing because list item is always created with correct class type for Avaline caller. 402 For now Avaline Caller is removed from the LLSpeakerMgr List when it is removed from the Voice Client 403 session. 404 This happens in two cases: if Avaline Caller ends call itself or if Resident ends group call. 405 406 Probably Avaline caller should be removed from the LLSpeakerMgr list ONLY if it ends call itself. 407 Asked in EXT-4301. 408*/ 409void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id) 410{ 411 LLPanel* item = mAvatarList->getItemByValue(participant_id); 412 413 if (NULL == item) 414 { 415 LL_WARNS("Avaline") << "Something wrong. Unable to find item for: " << participant_id << LL_ENDL; 416 return; 417 } 418 419 if (typeid(*item) == typeid(LLAvalineListItem)) 420 { 421 LL_DEBUGS("Avaline") << "Avaline caller has already correct class type for: " << participant_id << LL_ENDL; 422 // item representing an Avaline caller has a correct type already. 423 return; 424 } 425 426 LL_DEBUGS("Avaline") << "remove item from the list and re-add it: " << participant_id << LL_ENDL; 427 428 // remove UUID from LLAvatarList::mIDs to be able add it again. 429 uuid_vec_t& ids = mAvatarList->getIDs(); 430 uuid_vec_t::iterator pos = std::find(ids.begin(), ids.end(), participant_id); 431 ids.erase(pos); 432 433 // remove item directly 434 mAvatarList->removeItem(item); 435 436 // re-add avaline caller with a correct class instance. 437 addAvatarIDExceptAgent(participant_id); 438} 439 440void LLParticipantList::onAvalineCallerRemoved(const LLUUID& participant_id) 441{ 442 LL_DEBUGS("Avaline") << "Removing avaline caller from the list: " << participant_id << LL_ENDL; 443 444 mSpeakerMgr->removeAvalineSpeaker(participant_id); 445} 446 447void LLParticipantList::setSortOrder(EParticipantSortOrder order) 448{ 449 const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); 450 451 if ( speaker_sort_order != order ) 452 { 453 gSavedSettings.setU32("SpeakerParticipantDefaultOrder", (U32)order); 454 sort(); 455 } 456} 457 458const LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() const 459{ 460 const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); 461 return EParticipantSortOrder(speaker_sort_order); 462} 463 464void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb) 465{ 466 mValidateSpeakerCallback = cb; 467} 468 469void LLParticipantList::updateRecentSpeakersOrder() 470{ 471 if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered()) 472 { 473 // Need to update speakers to sort list correctly 474 mSpeakerMgr->update(true); 475 // Resort avatar list 476 sort(); 477 } 478} 479 480bool LLParticipantList::isHovered() 481{ 482 S32 x, y; 483 LLUI::getMousePositionScreen(&x, &y); 484 return mAvatarList->calcScreenRect().pointInRect(x, y); 485} 486 487bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 488{ 489 LLUUID uu_id = event->getValue().asUUID(); 490 491 if (mValidateSpeakerCallback && !mValidateSpeakerCallback(uu_id)) 492 { 493 return true; 494 } 495 496 addAvatarIDExceptAgent(uu_id); 497 sort(); 498 return true; 499} 500 501bool LLParticipantList::onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 502{ 503 uuid_vec_t& group_members = mAvatarList->getIDs(); 504 uuid_vec_t::iterator pos = std::find(group_members.begin(), group_members.end(), event->getValue().asUUID()); 505 if(pos != group_members.end()) 506 { 507 group_members.erase(pos); 508 mAvatarList->setDirty(); 509 } 510 return true; 511} 512 513bool LLParticipantList::onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 514{ 515 uuid_vec_t& group_members = mAvatarList->getIDs(); 516 group_members.clear(); 517 mAvatarList->setDirty(); 518 return true; 519} 520 521bool LLParticipantList::onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 522{ 523 const LLSD& evt_data = event->getValue(); 524 if ( evt_data.has("id") && evt_data.has("is_moderator") ) 525 { 526 LLUUID id = evt_data["id"]; 527 bool is_moderator = evt_data["is_moderator"]; 528 if ( id.notNull() ) 529 { 530 if ( is_moderator ) 531 mModeratorList.insert(id); 532 else 533 { 534 std::set<LLUUID>::iterator it = mModeratorList.find (id); 535 if ( it != mModeratorList.end () ) 536 { 537 mModeratorToRemoveList.insert(id); 538 mModeratorList.erase(id); 539 } 540 } 541 542 // apply changes immediately 543 onAvatarListRefreshed(mAvatarList, LLSD()); 544 } 545 } 546 return true; 547} 548 549bool LLParticipantList::onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 550{ 551 LLPointer<LLSpeaker> speakerp = (LLSpeaker*)event->getSource(); 552 if (speakerp.isNull()) return false; 553 554 // update UI on confirmation of moderator mutes 555 if (event->getValue().asString() == "voice") 556 { 557 update_speaker_indicator(mAvatarList, speakerp->mID, speakerp->mModeratorMutedVoice); 558 } 559 return true; 560} 561 562void LLParticipantList::sort() 563{ 564 if ( !mAvatarList ) 565 return; 566 567 switch ( getSortOrder() ) 568 { 569 case E_SORT_BY_NAME : 570 // if mExcludeAgent == true , then no need to keep agent on top of the list 571 if(mExcludeAgent) 572 { 573 mAvatarList->sortByName(); 574 } 575 else 576 { 577 mAvatarList->setComparator(&AGENT_ON_TOP_NAME_COMPARATOR); 578 mAvatarList->sort(); 579 } 580 break; 581 case E_SORT_BY_RECENT_SPEAKERS: 582 if (mSortByRecentSpeakers.isNull()) 583 mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this); 584 mAvatarList->setComparator(mSortByRecentSpeakers.get()); 585 mAvatarList->sort(); 586 break; 587 default : 588 llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl; 589 return; 590 } 591} 592 593void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) 594{ 595 if (mExcludeAgent && gAgent.getID() == avatar_id) return; 596 if (mAvatarList->contains(avatar_id)) return; 597 598 bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(avatar_id); 599 600 if (is_avatar) 601 { 602 mAvatarList->getIDs().push_back(avatar_id); 603 mAvatarList->setDirty(); 604 } 605 else 606 { 607 std::string display_name = LLVoiceClient::getInstance()->getDisplayName(avatar_id); 608 mAvatarList->addAvalineItem(avatar_id, mSpeakerMgr->getSessionID(), display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name); 609 mAvalineUpdater->watchAvalineCaller(avatar_id); 610 } 611 adjustParticipant(avatar_id); 612} 613 614void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) 615{ 616 LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); 617 if (speakerp.isNull()) return; 618 619 // add listener to process moderation changes 620 speakerp->addListener(mSpeakerMuteListener); 621} 622 623// 624// LLParticipantList::SpeakerAddListener 625// 626bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 627{ 628 /** 629 * We need to filter speaking objects. These objects shouldn't appear in the list 630 * @see LLFloaterChat::addChat() in llviewermessage.cpp to get detailed call hierarchy 631 */ 632 const LLUUID& speaker_id = event->getValue().asUUID(); 633 LLPointer<LLSpeaker> speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id); 634 if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT) 635 { 636 return false; 637 } 638 return mParent.onAddItemEvent(event, userdata); 639} 640 641// 642// LLParticipantList::SpeakerRemoveListener 643// 644bool LLParticipantList::SpeakerRemoveListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 645{ 646 return mParent.onRemoveItemEvent(event, userdata); 647} 648 649// 650// LLParticipantList::SpeakerClearListener 651// 652bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 653{ 654 return mParent.onClearListEvent(event, userdata); 655} 656 657// 658// LLParticipantList::SpeakerModeratorListener 659// 660bool LLParticipantList::SpeakerModeratorUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 661{ 662 return mParent.onModeratorUpdateEvent(event, userdata); 663} 664 665bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) 666{ 667 return mParent.onSpeakerMuteEvent(event, userdata); 668} 669 670LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() 671{ 672 // set up the callbacks for all of the avatar menu items 673 LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; 674 LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; 675 676 registrar.add("ParticipantList.Sort", boost::bind(&LLParticipantList::LLParticipantListMenu::sortParticipantList, this, _2)); 677 registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2)); 678 registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2)); 679 680 registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, mUUIDs.front())); 681 registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, mUUIDs.front())); 682 registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs.front())); 683 registrar.add("Avatar.BlockUnblock", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteVoice, this, _2)); 684 registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, mUUIDs.front())); 685 registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mUUIDs.front())); 686 registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startCall, mUUIDs.front())); 687 688 registrar.add("ParticipantList.ModerateVoice", boost::bind(&LLParticipantList::LLParticipantListMenu::moderateVoice, this, _2)); 689 690 enable_registrar.add("ParticipantList.EnableItem", boost::bind(&LLParticipantList::LLParticipantListMenu::enableContextMenuItem, this, _2)); 691 enable_registrar.add("ParticipantList.EnableItem.Moderate", boost::bind(&LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem, this, _2)); 692 enable_registrar.add("ParticipantList.CheckItem", boost::bind(&LLParticipantList::LLParticipantListMenu::checkContextMenuItem, this, _2)); 693 694 // create the context menu from the XUI 695 LLContextMenu* main_menu = createFromFile("menu_participant_list.xml"); 696 697 // Don't show sort options for P2P chat 698 bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); 699 main_menu->setItemVisible("SortByName", is_sort_visible); 700 main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible); 701 main_menu->setItemVisible("Moderator Options Separator", isGroupModerator()); 702 main_menu->setItemVisible("Moderator Options", isGroupModerator()); 703 main_menu->setItemVisible("View Icons Separator", mParent.mAvatarListToggleIconsConnection.connected()); 704 main_menu->setItemVisible("View Icons", mParent.mAvatarListToggleIconsConnection.connected()); 705 main_menu->arrangeAndClear(); 706 707 return main_menu; 708} 709 710void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) 711{ 712 if (uuids.size() == 0) return; 713 714 LLListContextMenu::show(spawning_view, uuids, x, y); 715 716 const LLUUID& speaker_id = mUUIDs.front(); 717 BOOL is_muted = isMuted(speaker_id); 718 719 if (is_muted) 720 { 721 LLMenuGL::sMenuContainer->getChildView("ModerateVoiceMuteSelected")->setVisible( false); 722 } 723 else 724 { 725 LLMenuGL::sMenuContainer->getChildView("ModerateVoiceUnMuteSelected")->setVisible( false); 726 } 727} 728 729void LLParticipantList::LLParticipantListMenu::sortParticipantList(const LLSD& userdata) 730{ 731 std::string param = userdata.asString(); 732 if ("sort_by_name" == param) 733 { 734 mParent.setSortOrder(E_SORT_BY_NAME); 735 } 736 else if ("sort_by_recent_speakers" == param) 737 { 738 mParent.setSortOrder(E_SORT_BY_RECENT_SPEAKERS); 739 } 740} 741 742void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata) 743{ 744 745 LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); 746 if (mgr) 747 { 748 const LLUUID speaker_id = mUUIDs.front(); 749 mgr->toggleAllowTextChat(speaker_id); 750 } 751} 752 753void LLParticipantList::LLParticipantListMenu::toggleMute(const LLSD& userdata, U32 flags) 754{ 755 const LLUUID speaker_id = mUUIDs.front(); 756 BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, flags); 757 std::string name; 758 759 //fill in name using voice client's copy of name cache 760 LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(speaker_id); 761 if (speakerp.isNull()) 762 { 763 return; 764 } 765 LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(mParent.mAvatarList->getItemByValue(speaker_id)); 766 if (NULL == item) return; 767 768 name = item->getAvatarName(); 769 770 LLMute::EType mute_type; 771 switch (speakerp->mType) 772 { 773 case LLSpeaker::SPEAKER_AGENT: 774 mute_type = LLMute::AGENT; 775 break; 776 case LLSpeaker::SPEAKER_OBJECT: 777 mute_type = LLMute::OBJECT; 778 break; 779 case LLSpeaker::SPEAKER_EXTERNAL: 780 default: 781 mute_type = LLMute::EXTERNAL; 782 break; 783 } 784 LLMute mute(speaker_id, name, mute_type); 785 786 if (!is_muted) 787 { 788 LLMuteList::getInstance()->add(mute, flags); 789 } 790 else 791 { 792 LLMuteList::getInstance()->remove(mute, flags); 793 } 794} 795 796void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userdata) 797{ 798 toggleMute(userdata, LLMute::flagTextChat); 799} 800 801void LLParticipantList::LLParticipantListMenu::toggleMuteVoice(const LLSD& userdata) 802{ 803 toggleMute(userdata, LLMute::flagVoiceChat); 804} 805 806bool LLParticipantList::LLParticipantListMenu::isGroupModerator() 807{ 808 if (!mParent.mSpeakerMgr) 809 { 810 llwarns << "Speaker manager is missing" << llendl; 811 return false; 812 } 813 814 // Is session a group call/chat? 815 if(gAgent.isInGroup(mParent.mSpeakerMgr->getSessionID())) 816 { 817 LLSpeaker* speaker = mParent.mSpeakerMgr->findSpeaker(gAgentID).get(); 818 819 // Is agent a moderator? 820 return speaker && speaker->mIsModerator; 821 } 822 return false; 823} 824 825bool LLParticipantList::LLParticipantListMenu::isMuted(const LLUUID& avatar_id) 826{ 827 LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(avatar_id); 828 if (!selected_speakerp) return true; 829 830 return selected_speakerp->mStatus == LLSpeaker::STATUS_MUTED; 831} 832 833void LLParticipantList::LLParticipantListMenu::moderateVoice(const LLSD& userdata) 834{ 835 if (!gAgent.getRegion()) return; 836 837 bool moderate_selected = userdata.asString() == "selected"; 838 839 if (moderate_selected) 840 { 841 const LLUUID& selected_avatar_id = mUUIDs.front(); 842 bool is_muted = isMuted(selected_avatar_id); 843 moderateVoiceParticipant(selected_avatar_id, is_muted); 844 } 845 else 846 { 847 bool unmute_all = userdata.asString() == "unmute_all"; 848 moderateVoiceAllParticipants(unmute_all); 849 } 850} 851 852void LLParticipantList::LLParticipantListMenu::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) 853{ 854 LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); 855 if (mgr) 856 { 857 mgr->moderateVoiceParticipant(avatar_id, unmute); 858 } 859} 860 861void LLParticipantList::LLParticipantListMenu::moderateVoiceAllParticipants(bool unmute) 862{ 863 LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); 864 if (mgr) 865 { 866 if (!unmute) 867 { 868 LLSD payload; 869 payload["session_id"] = mgr->getSessionID(); 870 LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); 871 return; 872 } 873 874 mgr->moderateVoiceAllParticipants(unmute); 875 } 876} 877 878// static 879void LLParticipantList::LLParticipantListMenu::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) 880{ 881 S32 option = LLNotificationsUtil::getSelectedOption(notification, response); 882 // if Cancel pressed 883 if (option == 1) 884 { 885 return; 886 } 887 888 const LLSD& payload = notification["payload"]; 889 const LLUUID& session_id = payload["session_id"]; 890 891 LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> ( 892 LLIMModel::getInstance()->getSpeakerManager(session_id)); 893 if (speaker_manager) 894 { 895 speaker_manager->moderateVoiceAllParticipants(false); 896 } 897 898 return; 899} 900 901 902bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata) 903{ 904 std::string item = userdata.asString(); 905 const LLUUID& participant_id = mUUIDs.front(); 906 907 // For now non of "can_view_profile" action and menu actions listed below except "can_block" 908 // can be performed for Avaline callers. 909 bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id); 910 if (!is_participant_avatar && "can_block" != item) return false; 911 912 if (item == "can_mute_text" || "can_block" == item || "can_share" == item || "can_im" == item 913 || "can_pay" == item) 914 { 915 return mUUIDs.front() != gAgentID; 916 } 917 else if (item == std::string("can_add")) 918 { 919 // We can add friends if: 920 // - there are selected people 921 // - and there are no friends among selection yet. 922 923 bool result = (mUUIDs.size() > 0); 924 925 uuid_vec_t::const_iterator 926 id = mUUIDs.begin(), 927 uuids_end = mUUIDs.end(); 928 929 for (;id != uuids_end; ++id) 930 { 931 if ( *id == gAgentID || LLAvatarActions::isFriend(*id) ) 932 { 933 result = false; 934 break; 935 } 936 } 937 return result; 938 } 939 else if (item == "can_call") 940 { 941 bool not_agent = mUUIDs.front() != gAgentID; 942 bool can_call = not_agent && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); 943 return can_call; 944 } 945 946 return true; 947} 948 949/* 950 Processed menu items with such parameters: 951 can_allow_text_chat 952 can_moderate_voice 953*/ 954bool LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem(const LLSD& userdata) 955{ 956 // only group moderators can perform actions related to this "enable callback" 957 if (!isGroupModerator()) return false; 958 959 const LLUUID& participant_id = mUUIDs.front(); 960 LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(participant_id); 961 962 // not in voice participants can not be moderated 963 bool speaker_in_voice = speakerp.notNull() && speakerp->isInVoiceChannel(); 964 965 const std::string& item = userdata.asString(); 966 967 if ("can_moderate_voice" == item) 968 { 969 return speaker_in_voice; 970 } 971 972 // For now non of menu actions except "can_moderate_voice" can be performed for Avaline callers. 973 bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id); 974 if (!is_participant_avatar) return false; 975 976 return true; 977} 978 979bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD& userdata) 980{ 981 std::string item = userdata.asString(); 982 const LLUUID& id = mUUIDs.front(); 983 984 if (item == "is_muted") 985 { 986 return LLMuteList::getInstance()->isMuted(id, LLMute::flagTextChat); 987 } 988 else if (item == "is_allowed_text_chat") 989 { 990 LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(id); 991 992 if (selected_speakerp.notNull()) 993 { 994 return !selected_speakerp->mModeratorMutedText; 995 } 996 } 997 else if(item == "is_blocked") 998 { 999 return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); 1000 } 1001 else if(item == "is_sorted_by_name") 1002 { 1003 return E_SORT_BY_NAME == mParent.getSortOrder(); 1004 } 1005 else if(item == "is_sorted_by_recent_speakers") 1006 { 1007 return E_SORT_BY_RECENT_SPEAKERS == mParent.getSortOrder(); 1008 } 1009 1010 return false; 1011} 1012 1013bool LLParticipantList::LLAvatarItemRecentSpeakerComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const 1014{ 1015 if (mParent.mSpeakerMgr) 1016 { 1017 LLPointer<LLSpeaker> lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId()); 1018 LLPointer<LLSpeaker> rhs = mParent.mSpeakerMgr->findSpeaker(avatar_item2->getAvatarId()); 1019 if ( lhs.notNull() && rhs.notNull() ) 1020 { 1021 // Compare by last speaking time 1022 if( lhs->mLastSpokeTime != rhs->mLastSpokeTime ) 1023 return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime ); 1024 else if ( lhs->mSortIndex != rhs->mSortIndex ) 1025 return ( lhs->mSortIndex < rhs->mSortIndex ); 1026 } 1027 else if ( lhs.notNull() ) 1028 { 1029 // True if only avatar_item1 speaker info available 1030 return true; 1031 } 1032 else if ( rhs.notNull() ) 1033 { 1034 // False if only avatar_item2 speaker info available 1035 return false; 1036 } 1037 } 1038 // By default compare by name. 1039 return LLAvatarItemNameComparator::doCompare(avatar_item1, avatar_item2); 1040} 1041 1042//EOF