diff options
36 files changed, 805 insertions, 135 deletions
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 09124c3013..7b7a3139a4 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -560,6 +560,8 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() ) { + ensureSelectedVisible(); + /* LLRect visible_rc = getVisibleContentRect(); LLRect selected_rc = getLastSelectedItemRect(); @@ -572,7 +574,8 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) // In case we are in accordion tab notify parent to show selected rectangle LLRect screen_rc; localRectToScreen(selected_rc, &screen_rc); - notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); + notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/ + handled = TRUE; } @@ -694,11 +697,30 @@ LLRect LLFlatListView::getSelectedItemsRect() void LLFlatListView::selectFirstItem () { selectItemPair(mItemPairs.front(), true); + ensureSelectedVisible(); } void LLFlatListView::selectLastItem () { selectItemPair(mItemPairs.back(), true); + ensureSelectedVisible(); +} + +void LLFlatListView::ensureSelectedVisible() +{ + LLRect visible_rc = getVisibleContentRect(); + LLRect selected_rc = getLastSelectedItemRect(); + + if ( !visible_rc.contains (selected_rc) ) + { + // But scroll in Items panel coordinates + scrollToShowRect(selected_rc); + } + + // In case we are in accordion tab notify parent to show selected rectangle + LLRect screen_rc; + localRectToScreen(selected_rc, &screen_rc); + notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); } diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index ba824ff2df..26e84a6fe1 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -359,6 +359,8 @@ protected: LLRect getSelectedItemsRect(); + void ensureSelectedVisible(); + private: void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;} diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 71b23e1383..5317cf2cd0 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -33,7 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "llavatarlist.h" -#include "llagent.h" // for comparator +#include "llagentdata.h" // for comparator // newview #include "llcallingcard.h" // for LLAvatarTracker @@ -322,7 +322,6 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is item->setAvatarId(id, mIgnoreOnlineStatus); item->setOnline(mIgnoreOnlineStatus ? true : is_online); item->showLastInteractionTime(mShowLastInteractionTime); - item->setContextMenu(mContextMenu); item->childSetVisible("info_btn", false); item->setAvatarIconVisible(mShowIcons); @@ -425,11 +424,11 @@ bool LLAvatarItemAgentOnTopComparator::doCompare(const LLAvatarListItem* avatar_ { //keep agent on top, if first is agent, //then we need to return true to elevate this id, otherwise false. - if(avatar_item1->getAvatarId() == gAgent.getID()) + if(avatar_item1->getAvatarId() == gAgentID) { return true; } - else if (avatar_item2->getAvatarId() == gAgent.getID()) + else if (avatar_item2->getAvatarId() == gAgentID) { return false; } diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 072eebdf2d..c8544bc3fb 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -43,6 +43,12 @@ #include "lltextutil.h" #include "llbutton.h" +bool LLAvatarListItem::sStaticInitialized = false; +S32 LLAvatarListItem::sIconWidth = 0; +S32 LLAvatarListItem::sInfoBtnWidth = 0; +S32 LLAvatarListItem::sProfileBtnWidth = 0; +S32 LLAvatarListItem::sSpeakingIndicatorWidth = 0; + LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) : LLPanel(), mAvatarIcon(NULL), @@ -51,7 +57,6 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) mSpeakingIndicator(NULL), mInfoBtn(NULL), mProfileBtn(NULL), - mContextMenu(NULL), mOnlineStatus(E_UNKNOWN), mShowInfoBtn(true), mShowProfileBtn(true) @@ -88,10 +93,15 @@ BOOL LLAvatarListItem::postBuild() // Remember avatar icon width including its padding from the name text box, // so that we can hide and show the icon again later. - mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; - mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; - mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; - mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight; + if (!sStaticInitialized) + { + sIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; + sInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; + sProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; + sSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight; + + sStaticInitialized = true; + } /* if(!p.buttons.profile) @@ -173,6 +183,28 @@ void LLAvatarListItem::setHighlight(const std::string& highlight) setNameInternal(mAvatarName->getText(), mHighlihtSubstring = highlight); } +void LLAvatarListItem::setStyle(const LLStyle::Params& new_style) +{ +// LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle = new_style); + + // Active group should be bold. + LLFontDescriptor new_desc(mAvatarName->getDefaultFont()->getFontDesc()); + + new_desc.setStyle(new_style.font()->getFontDesc().getStyle()); + // *NOTE dzaporozhan + // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font + // is predefined as bold (SansSerifSmallBold, for example) +// new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); + LLFontGL* new_font = LLFontGL::getFont(new_desc); + +// + mAvatarNameStyle.font = new_font; + + // *NOTE: You cannot set the style on a text box anymore, you must + // rebuild the text. This will cause problems if the text contains + // hyperlinks, as their styles will be wrong. + mAvatarName->setText(mAvatarName->getText(), mAvatarNameStyle/* = new_style*/); +} void LLAvatarListItem::setAvatarId(const LLUUID& id, bool ignore_status_changes) { if (mAvatarId.notNull()) @@ -214,7 +246,7 @@ void LLAvatarListItem::setShowInfoBtn(bool show) if(mShowInfoBtn == show) return; mShowInfoBtn = show; - S32 width_delta = show ? - mInfoBtnWidth : mInfoBtnWidth; + S32 width_delta = show ? - sInfoBtnWidth : sInfoBtnWidth; //Translating speaking indicator mSpeakingIndicator->translate(width_delta, 0); @@ -228,7 +260,7 @@ void LLAvatarListItem::setShowProfileBtn(bool show) if(mShowProfileBtn == show) return; mShowProfileBtn = show; - S32 width_delta = show ? - mProfileBtnWidth : mProfileBtnWidth; + S32 width_delta = show ? - sProfileBtnWidth : sProfileBtnWidth; //Translating speaking indicator mSpeakingIndicator->translate(width_delta, 0); @@ -242,7 +274,7 @@ void LLAvatarListItem::setSpeakingIndicatorVisible(bool visible) if (mSpeakingIndicator->getVisible() == (BOOL)visible) return; mSpeakingIndicator->setVisible(visible); - S32 width_delta = visible ? - mSpeakingIndicatorWidth : mSpeakingIndicatorWidth; + S32 width_delta = visible ? - sSpeakingIndicatorWidth : sSpeakingIndicatorWidth; //Reshaping avatar name mAvatarName->reshape(mAvatarName->getRect().getWidth() + width_delta, mAvatarName->getRect().getHeight()); @@ -259,7 +291,7 @@ void LLAvatarListItem::setAvatarIconVisible(bool visible) // Move the avatar name horizontally by icon size + its distance from the avatar name. LLRect name_rect = mAvatarName->getRect(); - name_rect.mLeft += visible ? mIconWidth : -mIconWidth; + name_rect.mLeft += visible ? sIconWidth : -sIconWidth; mAvatarName->setRect(name_rect); } @@ -327,10 +359,10 @@ void LLAvatarListItem::onNameCache(const std::string& first_name, const std::str void LLAvatarListItem::reshapeAvatarName() { S32 width_delta = 0; - width_delta += mShowProfileBtn ? mProfileBtnWidth : 0; - width_delta += mSpeakingIndicator->getVisible() ? mSpeakingIndicatorWidth : 0; - width_delta += mAvatarIcon->getVisible() ? mIconWidth : 0; - width_delta += mShowInfoBtn ? mInfoBtnWidth : 0; + width_delta += mShowProfileBtn ? sProfileBtnWidth : 0; + width_delta += mSpeakingIndicator->getVisible() ? sSpeakingIndicatorWidth : 0; + width_delta += mAvatarIcon->getVisible() ? sIconWidth : 0; + width_delta += mShowInfoBtn ? sInfoBtnWidth : 0; width_delta += mLastInteractionTime->getVisible() ? mLastInteractionTime->getRect().getWidth() : 0; S32 height = mAvatarName->getRect().getHeight(); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index aa1b7593f5..0e058f75db 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -73,6 +73,7 @@ public: void setOnline(bool online); void setName(const std::string& name); void setHighlight(const std::string& highlight); + void setStyle(const LLStyle::Params& new_style); void setAvatarId(const LLUUID& id, bool ignore_status_changes = false); void setLastInteractionTime(U32 secs_since); //Show/hide profile/info btn, translating speaker indicator and avatar name coordinates accordingly @@ -91,8 +92,6 @@ public: void showInfoBtn(bool show_info_btn) {mInfoBtn->setVisible(show_info_btn); } void showLastInteractionTime(bool show); - void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } - /** * This method was added to fix EXT-2364 (Items in group/ad-hoc IM participant list (avatar names) should be reshaped when adding/removing the "(Moderator)" label) * But this is a *HACK. The real reason of it was in incorrect logic while hiding profile/info/speaker buttons @@ -126,7 +125,6 @@ private: LLButton* mInfoBtn; LLButton* mProfileBtn; - ContextMenu* mContextMenu; LLUUID mAvatarId; std::string mHighlihtSubstring; // substring to highlight @@ -135,10 +133,12 @@ private: //Speaker indicator and avatar name coords are translated accordingly bool mShowInfoBtn; bool mShowProfileBtn; - S32 mIconWidth; // icon width + padding - S32 mInfoBtnWidth; //info btn width + padding - S32 mProfileBtnWidth; //profile btn width + padding - S32 mSpeakingIndicatorWidth; //speaking indicator width + padding + + static bool sStaticInitialized; // this variable is introduced to improve code readability + static S32 sIconWidth; // icon width + padding + static S32 sInfoBtnWidth; //info btn width + padding + static S32 sProfileBtnWidth; //profile btn width + padding + static S32 sSpeakingIndicatorWidth; //speaking indicator width + padding }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index ab657e0e99..4d5d416907 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -81,9 +81,6 @@ LLBottomTray::LLBottomTray(const LLSD&) LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraPresets, _2)); - //managing chiclets for voice calls - LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLBottomTray::onNewIM, this, _1)); - //this is to fix a crash that occurs because LLBottomTray is a singleton //and thus is deleted at the end of the viewers lifetime, but to be cleanly //destroyed LLBottomTray requires some subsystems that are long gone @@ -153,9 +150,6 @@ void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& nam if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; - // For im sessions started as voice call chiclet gets created on the first incoming message - if (gIMMgr->isVoiceCall(session_id)) return; - LLIMChiclet* chiclet = createIMChiclet(session_id); if(chiclet) { @@ -197,27 +191,6 @@ void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& } } -void LLBottomTray::onNewIM(const LLSD& data) -{ - LLUUID from_id = data["from_id"]; - if (from_id.isNull() || gAgentID == from_id) return; - - LLUUID session_id = data["session_id"]; - if (session_id.isNull()) return; - - if (!gIMMgr->isVoiceCall(session_id)) return; - - if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; - - //first real message, time to create chiclet - LLIMChiclet* chiclet = createIMChiclet(session_id); - if(chiclet) - { - chiclet->setIMSessionName(LLIMModel::getInstance()->getName(session_id)); - chiclet->setOtherParticipantId(LLIMModel::getInstance()->getOtherParticipantID(session_id)); - } -} - S32 LLBottomTray::getTotalUnreadIMCount() { return getChicletPanel()->getTotalUnreadIMCount(); diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 5cd3f15746..9be0e5810f 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -75,8 +75,6 @@ public: virtual void sessionRemoved(const LLUUID& session_id); void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); - void onNewIM(const LLSD& data); - S32 getTotalUnreadIMCount(); virtual void reshape(S32 width, S32 height, BOOL called_from_parent); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 20739d2401..fe4f0c5525 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -89,6 +89,7 @@ LLCallFloater::LLCallFloater(const LLSD& key) , mAgentPanel(NULL) , mSpeakingIndicator(NULL) , mIsModeratorMutedVoice(false) +, mInitParticipantsVoiceState(false) { mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL); LLVoiceClient::getInstance()->addObserver(this); @@ -100,6 +101,8 @@ LLCallFloater::~LLCallFloater() delete mPaticipants; mPaticipants = NULL; + mAvatarListRefreshConnection.disconnect(); + // Don't use LLVoiceClient::getInstance() here // singleton MAY have already been destroyed. if(gVoiceClient) @@ -114,6 +117,8 @@ BOOL LLCallFloater::postBuild() { LLDockableFloater::postBuild(); mAvatarList = getChild<LLAvatarList>("speakers_list"); + mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this)); + childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this)); mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller"); @@ -153,6 +158,10 @@ void LLCallFloater::draw() setModeratorMutedVoice(is_moderator_muted); } + // Need to resort the participant list if it's in sort by recent speaker order. + if (mPaticipants) + mPaticipants->updateRecentSpeakersOrder(); + LLDockableFloater::draw(); } @@ -161,7 +170,7 @@ void LLCallFloater::onChange() { if (NULL == mPaticipants) return; - mPaticipants->refreshVoiceState(); + updateParticipantsVoiceState(); } @@ -243,7 +252,7 @@ void LLCallFloater::updateSession() childSetVisible("leave_call_btn", !is_local_chat); refreshPartisipantList(); - updateModeratorState(); + updateAgentModeratorState(); //show floater for voice calls if (!is_local_chat) @@ -280,13 +289,29 @@ void LLCallFloater::refreshPartisipantList() if (!non_avatar_caller) { - mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList); + mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT); if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager) { mAvatarList->setNoItemsCommentText(getString("no_one_near")); } - mPaticipants->refreshVoiceState(); + + // we have to made delayed initialization of voice state of participant list. + // it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed(). + mInitParticipantsVoiceState = true; + } +} + +void LLCallFloater::onAvatarListRefreshed() +{ + if (mInitParticipantsVoiceState) + { + initParticipantsVoiceState(); + mInitParticipantsVoiceState = false; + } + else + { + updateParticipantsVoiceState(); } } @@ -366,7 +391,7 @@ void LLCallFloater::setModeratorMutedVoice(bool moderator_muted) mSpeakingIndicator->setIsMuted(moderator_muted); } -void LLCallFloater::updateModeratorState() +void LLCallFloater::updateAgentModeratorState() { std::string name; gCacheName->getFullName(gAgentID, name); @@ -388,4 +413,213 @@ void LLCallFloater::updateModeratorState() } mAgentPanel->childSetValue("user_text", name); } + +void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids) +{ + // Get a list of participants from VoiceClient + LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList(); + if (voice_map) + { + for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin(); + iter != voice_map->end(); ++iter) + { + LLUUID id = (*iter).second->mAvatarID; + speakers_uuids.push_back(id); + } + } +} + +void LLCallFloater::initParticipantsVoiceState() +{ + // Set initial status for each participant in the list. + std::vector<LLPanel*> items; + mAvatarList->getItems(items); + std::vector<LLPanel*>::const_iterator + it = items.begin(), + it_end = items.end(); + + + std::vector<LLUUID> speakers_uuids; + get_voice_participants_uuids(speakers_uuids); + + for(; it != it_end; ++it) + { + LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it); + + if (!item) continue; + + LLUUID speaker_id = item->getAvatarId(); + + std::vector<LLUUID>::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id); + + // If an avatarID assigned to a panel is found in a speakers list + // obtained from VoiceClient we assign the JOINED status to the owner + // of this avatarID. + if (speaker_iter != speakers_uuids.end()) + { + setState(item, STATE_JOINED); + } + else + { + LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id); + // If someone has already left the call before, we create his + // avatar row panel with HAS_LEFT status and remove it after + // the timeout, otherwise we create a panel with INVITED status + if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall) + { + setState(item, STATE_LEFT); + } + else + { + setState(item, STATE_INVITED); + } + } + } +} + +void LLCallFloater::updateParticipantsVoiceState() +{ + std::vector<LLUUID> speakers_list; + + // Get a list of participants from VoiceClient + LLVoiceClient::participantMap *map = gVoiceClient->getParticipantList(); + if (!map) return; + + for (LLVoiceClient::participantMap::const_iterator iter = map->begin(); + iter != map->end(); ++iter) + { + LLUUID id = (*iter).second->mAvatarID; +// if ( id != gAgent.getID() ) + { + speakers_list.push_back(id); +/* + LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(id)); + if (item) + { + setState(item, STATE_JOINED); + } +*/ + + } + } + + // Updating the status for each participant. + std::vector<LLPanel*> items; + mAvatarList->getItems(items); + std::vector<LLPanel*>::const_iterator + it = items.begin(), + it_end = items.end(); + + for(; it != it_end; ++it) + { + LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it); + if (!item) continue; + + const LLUUID participant_id = item->getAvatarId(); + bool found = false; + + std::vector<LLUUID>::iterator speakers_iter = std::find(speakers_list.begin(), speakers_list.end(), participant_id); + + lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl; + + // If an avatarID assigned to a panel is found in a speakers list + // obtained from VoiceClient we assign the JOINED status to the owner + // of this avatarID. + if (speakers_iter != speakers_list.end()) + { + setState(item, STATE_JOINED); + + LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id); + if (speaker.isNull()) + continue; + speaker->mHasLeftCurrentCall = FALSE; + + speakers_list.erase(speakers_iter); + found = true; + } + + // If an avatarID is not found in a speakers list from VoiceClient and + // a panel with this ID has a JOINED status this means that this person + // HAS LEFT the call. + if (!found) + { + if ((getState(participant_id) == STATE_JOINED)) + { + setState(item, STATE_LEFT); + + LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(item->getAvatarId()); + if (speaker.isNull()) + continue; + + speaker->mHasLeftCurrentCall = TRUE; + } + else if ((getState(participant_id) != STATE_LEFT)) + { + setState(item, STATE_INVITED); + } + +/* + // If there is already a started timer for the current panel don't do anything. + bool no_timer_for_current_panel = true; + if (mTimersMap.size() > 0) + { + timers_map::iterator found_it = mTimersMap.find(participant_id); + if (found_it != mTimersMap.end()) + { + no_timer_for_current_panel = false; + } + } + + if (no_timer_for_current_panel) + { + // Starting a timer to remove an avatar row panel after timeout + // *TODO Make the timeout period adjustable + mTimersMap.insert(timer_pair(participant_id, new LLAvatarRowRemoveTimer(this->getHandle(), 10, participant_id))); + } +*/ + } + } + +} + +void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state) +{ + setState(item->getAvatarId(), state); + + LLStyle::Params speaker_style; + LLFontDescriptor new_desc(speaker_style.font()->getFontDesc()); + + switch (state) + { + case STATE_INVITED: +// status_str = "INVITED"; // *TODO: localize + new_desc.setStyle(LLFontGL::NORMAL); + break; + case STATE_JOINED: +// status_str = "JOINED"; // *TODO: localize + new_desc.setStyle(LLFontGL::NORMAL); + break; + case STATE_LEFT: + { + // status_str = "HAS LEFT CALL"; // *TODO: localize + new_desc.setStyle(LLFontGL::ITALIC); + + } + break; + default: + llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl; + break; + } + + LLFontGL* new_font = LLFontGL::getFont(new_desc); + speaker_style.font = new_font; + item->setStyle(speaker_style); + +// if () + { + // found speaker is in voice, mark him as online + item->setOnline(STATE_JOINED == state); + } +} + //EOF diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index f9c9149085..21fba433c6 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -38,6 +38,7 @@ #include "llvoiceclient.h" class LLAvatarList; +class LLAvatarListItem; class LLNonAvatarCaller; class LLOutputMonitorCtrl; class LLParticipantList; @@ -81,6 +82,16 @@ private: VC_PEER_TO_PEER }EVoiceControls; + typedef enum e_speaker_state + { + STATE_UNKNOWN, + STATE_INVITED, + STATE_JOINED, + STATE_LEFT, + } ESpeakerState; + + typedef std::map<LLUUID, ESpeakerState> speaker_state_map_t; + void leaveCall(); /** @@ -95,15 +106,32 @@ private: * Refreshes participant list according to current Voice Channel */ void refreshPartisipantList(); - + void onAvatarListRefreshed(); void updateTitle(); void initAgentData(); void setModeratorMutedVoice(bool moderator_muted); - void updateModeratorState(); + void updateAgentModeratorState(); + + void initParticipantsVoiceState(); + void updateParticipantsVoiceState(); + + void setState(LLAvatarListItem* item, ESpeakerState state); + void setState(const LLUUID& speaker_id, ESpeakerState state) + { + lldebugs << "Storing state: " << speaker_id << ", " << state << llendl; + mSpeakerStateMap[speaker_id] = state; + } + + ESpeakerState getState(const LLUUID& speaker_id) + { + lldebugs << "Getting state: " << speaker_id << ", " << mSpeakerStateMap[speaker_id] << llendl; + return mSpeakerStateMap[speaker_id]; + } private: + speaker_state_map_t mSpeakerStateMap; LLSpeakerMgr* mSpeakerManager; LLParticipantList* mPaticipants; LLAvatarList* mAvatarList; @@ -112,6 +140,11 @@ private: LLPanel* mAgentPanel; LLOutputMonitorCtrl* mSpeakingIndicator; bool mIsModeratorMutedVoice; + + bool mInitParticipantsVoiceState; + + boost::signals2::connection mAvatarListRefreshConnection; + }; diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 21a0381495..17ef1f41a4 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -68,7 +68,8 @@ static const LLRect CHICLET_RECT(0, 25, 25, 0); static const LLRect CHICLET_ICON_RECT(0, 22, 22, 0); static const LLRect VOICE_INDICATOR_RECT(50, 25, 70, 0); static const LLRect COUNTER_RECT(25, 25, 50, 0); -static const S32 OVERLAY_ICON_SHIFT = 2; // used for shifting of an overlay icon for new massages in a chiclet +static const S32 OVERLAY_ICON_SHIFT = 2; // used for shifting of an overlay icon for new massages in a chiclet +static const S32 SCROLL_BUTTON_PAD = 5; // static const S32 LLChicletPanel::s_scroll_ratio = 10; @@ -140,7 +141,7 @@ private: LLSysWellChiclet::Params::Params() : button("button") , unread_notifications("unread_notifications") -, max_displayed_count("max_displayed_count", 9) +, max_displayed_count("max_displayed_count", 99) , flash_to_lit_count("flash_to_lit_count", 3) , flash_period("flash_period", 0.5F) { @@ -186,9 +187,9 @@ void LLSysWellChiclet::setCounter(S32 counter) mButton->setLabel(s_count); - setNewMessagesState(counter > 0); + setNewMessagesState(counter > mCounter); - // we have to flash to 'Lit' state each time new unread message is comming. + // we have to flash to 'Lit' state each time new unread message is coming. if (counter > mCounter) { mFlashToLitTimer->flash(); @@ -1311,7 +1312,6 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index) chiclet->setChicletSizeChangedCallback(boost::bind(&LLChicletPanel::onChicletSizeChanged, this, _1, index)); arrange(); - showScrollButtonsIfNeeded(); return true; } @@ -1322,8 +1322,6 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index) void LLChicletPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param) { arrange(); - trimChiclets(); - showScrollButtonsIfNeeded(); } void LLChicletPanel::onChicletClick(LLUICtrl*ctrl,const LLSD¶m) @@ -1340,8 +1338,6 @@ void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it) mChicletList.erase(it); arrange(); - trimChiclets(); - showScrollButtonsIfNeeded(); } void LLChicletPanel::removeChiclet(S32 index) @@ -1434,8 +1430,6 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) { LLPanel::reshape(width,height,called_from_parent); - static const S32 SCROLL_BUTTON_PAD = 5; - //Needed once- to avoid error at first call of reshape() before postBuild() if(!mLeftScrollButton||!mRightScrollButton) return; @@ -1446,9 +1440,21 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) scroll_button_rect = mRightScrollButton->getRect(); mRightScrollButton->setRect(LLRect(width - scroll_button_rect.getWidth(),scroll_button_rect.mTop, width, scroll_button_rect.mBottom)); - mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, - height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); + + + bool need_show_scroll = needShowScroll(); + if(need_show_scroll) + { + mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, + height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); + } + else + { + mScrollArea->setRect(LLRect(0,height, width, 0)); + } + mShowControls = width >= mMinWidth; + mScrollArea->setVisible(mShowControls); trimChiclets(); @@ -1461,8 +1467,8 @@ void LLChicletPanel::arrange() if(mChicletList.empty()) return; + //initial arrange of chicklets positions S32 chiclet_left = getChiclet(0)->getRect().mLeft; - S32 size = getChicletCount(); for( int n = 0; n < size; ++n) { @@ -1476,6 +1482,24 @@ void LLChicletPanel::arrange() chiclet_left += chiclet_width + getChicletPadding(); } + + //reset size and pos on mScrollArea + LLRect rect = getRect(); + LLRect scroll_button_rect = mLeftScrollButton->getRect(); + + bool need_show_scroll = needShowScroll(); + if(need_show_scroll) + { + mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, + rect.getHeight(), rect.getWidth() - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); + } + else + { + mScrollArea->setRect(LLRect(0,rect.getHeight(), rect.getWidth(), 0)); + } + + trimChiclets(); + showScrollButtonsIfNeeded(); } void LLChicletPanel::trimChiclets() @@ -1493,6 +1517,17 @@ void LLChicletPanel::trimChiclets() } } +bool LLChicletPanel::needShowScroll() +{ + if(mChicletList.empty()) + return false; + + S32 chicklet_width = (*mChicletList.rbegin())->getRect().mRight - (*mChicletList.begin())->getRect().mLeft; + + return chicklet_width>getRect().getWidth(); +} + + void LLChicletPanel::showScrollButtonsIfNeeded() { bool can_scroll_left = canScrollLeft(); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 259476c2ad..2ab6abfb5b 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -1050,6 +1050,11 @@ protected: bool canScrollRight(); /** + * Returns true if we need to show scroll buttons + */ + bool needShowScroll(); + + /** * Returns true if chiclets can be scrolled left. */ bool canScrollLeft(); diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index ff75d461df..22658b4d65 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -119,6 +119,36 @@ void LLGroupActions::search() } // static +void LLGroupActions::startCall(const LLUUID& group_id) +{ + // create a new group voice session + LLGroupData gdata; + + if (!gAgent.getGroupData(group_id, gdata)) + { + llwarns << "Error getting group data" << llendl; + return; + } + + LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, true); + if (session_id == LLUUID::null) + { + llwarns << "Error adding session" << llendl; + return; + } + + // start the call + // *TODO: move this to LLIMMgr? + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); + if (session && session->mSessionInitialized) + gIMMgr->startCall(session_id); + else + gIMMgr->autoStartCallOnStartup(session_id); + + make_ui_sound("UISndStartIM"); +} + +// static void LLGroupActions::join(const LLUUID& group_id) { LLGroupMgrGroupData* gdatap = diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h index 9750b3e3cb..e99df86cd9 100644 --- a/indra/newview/llgroupactions.h +++ b/indra/newview/llgroupactions.h @@ -99,6 +99,11 @@ public: static bool isInGroup(const LLUUID& group_id); /** + * Start a group voice call. + */ + static void startCall(const LLUUID& group_id); + + /** * Returns true if avatar is in group. * * Note that data about group members is loaded from server. diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index ab9db10f38..3ca459a403 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -37,6 +37,7 @@ // libs #include "llbutton.h" #include "lliconctrl.h" +#include "llmenugl.h" #include "lltextbox.h" #include "lltrans.h" @@ -46,6 +47,7 @@ #include "llfloaterreg.h" #include "lltextutil.h" #include "llviewercontrol.h" // for gSavedSettings +#include "llviewermenu.h" // for gMenuHolder static LLDefaultChildRegistry::Register<LLGroupList> r("group_list"); S32 LLGroupListItem::sIconWidth = 0; @@ -88,11 +90,24 @@ LLGroupList::LLGroupList(const Params& p) // Set default sort order. setComparator(&GROUP_COMPARATOR); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2)); + enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2)); + + LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml", + gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + mContextMenuHandle = context_menu->getHandle(); } LLGroupList::~LLGroupList() { gAgent.removeListener(this); + LLView::deleteViewByHandle(mContextMenuHandle); } // virtual @@ -104,6 +119,22 @@ void LLGroupList::draw() LLFlatListView::draw(); } +// virtual +BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); + if (context_menu) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + void LLGroupList::setNameFilter(const std::string& filter) { if (mNameFilter != filter) @@ -203,6 +234,46 @@ bool LLGroupList::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& return false; } +bool LLGroupList::onContextMenuItemClick(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LLUUID selected_group = getSelectedUUID(); + + if (action == "view_info") + { + LLGroupActions::show(selected_group); + } + else if (action == "chat") + { + LLGroupActions::startIM(selected_group); + } + else if (action == "call") + { + LLGroupActions::startCall(selected_group); + } + else if (action == "activate") + { + LLGroupActions::activate(selected_group); + } + else if (action == "leave") + { + LLGroupActions::leave(selected_group); + } + + return true; +} + +bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) +{ + LLUUID selected_group_id = getSelectedUUID(); + bool real_group_selected = selected_group_id.notNull(); // a "real" (not "none") group is selected + + if (userdata.asString() == "activate") + return real_group_selected && gAgent.getGroupID() != selected_group_id; + + return real_group_selected; +} + /************************************************************************/ /* LLGroupListItem implementation */ /************************************************************************/ diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 33cfe005b9..f7afe0c0b2 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -60,6 +60,7 @@ public: virtual ~LLGroupList(); virtual void draw(); // from LLView + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); // from LLView void setNameFilter(const std::string& filter); void toggleIcons(); @@ -71,6 +72,11 @@ private: void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM); bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); // called on agent group list changes + bool onContextMenuItemClick(const LLSD& userdata); + bool onContextMenuItemEnable(const LLSD& userdata); + + LLHandle<LLView> mContextMenuHandle; + bool mShowIcons; bool mDirty; std::string mNameFilter; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index f5362acbfe..8917cc11e1 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1227,15 +1227,15 @@ LLIMMgr::showSessionEventError( const std::string& error_string, const LLUUID session_id) { - const LLFloater* floater = getFloaterBySessionID (session_id); - if (!floater) return; - LLSD args; + LLStringUtil::format_map_t event_args; + + event_args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id); + args["REASON"] = LLTrans::getString(error_string); args["EVENT"] = - LLTrans::getString(event_string); - args["RECIPIENT"] = floater->getTitle(); + LLTrans::getString(event_string, event_args); LLNotificationsUtil::add( "ChatterBoxSessionEventError", diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 22201aecb2..818e7e0db1 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -571,7 +571,7 @@ BOOL LLPanelStandStopFlying::postBuild() mStandButton->setVisible(FALSE); mStopFlyingButton = getChild<LLButton>("stop_fly_btn"); - mStopFlyingButton->setCommitCallback(boost::bind(&LLFloaterMove::setFlyingMode, FALSE)); + //mStopFlyingButton->setCommitCallback(boost::bind(&LLFloaterMove::setFlyingMode, FALSE)); mStopFlyingButton->setCommitCallback(boost::bind(&LLPanelStandStopFlying::onStopFlyingButtonClick, this)); mStopFlyingButton->setVisible(FALSE); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 3a1ae5bf46..1a0183a8ba 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -178,6 +178,8 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive) if (!chat.mMuted) { + tmp_chat.mFromName = chat.mFromID != gAgentID ? chat.mFromName : LLTrans::getString("You"); + if (chat.mChatStyle == CHAT_STYLE_IRC) { LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 0dae667e7f..e29320ffc2 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -1557,6 +1557,11 @@ void LLPanelClassifiedEdit::resetControls() childSetValue("price_for_listing", MINIMUM_PRICE_FOR_LISTING); } +bool LLPanelClassifiedEdit::canClose() +{ + return isValidName(); +} + void LLPanelClassifiedEdit::sendUpdate() { LLAvatarClassifiedInfo c_data; @@ -1671,6 +1676,12 @@ void LLPanelClassifiedEdit::onChange() void LLPanelClassifiedEdit::onSaveClick() { + if(!isValidName()) + { + notifyInvalidName(); + return; + } + sendUpdate(); resetDirty(); } @@ -1681,6 +1692,34 @@ std::string LLPanelClassifiedEdit::getLocationNotice() return location_notice; } +bool LLPanelClassifiedEdit::isValidName() +{ + std::string name = getClassifiedName(); + if (name.empty()) + { + return false; + } + if (!isalnum(name[0])) + { + return false; + } + + return true; +} + +void LLPanelClassifiedEdit::notifyInvalidName() +{ + std::string name = getClassifiedName(); + if (name.empty()) + { + LLNotificationsUtil::add("BlankClassifiedName"); + } + else if (!isalnum(name[0])) + { + LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric"); + } +} + void LLPanelClassifiedEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl) { ctrl->setVisible(TRUE); diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index 8b32495854..10fdf60bbe 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -305,6 +305,8 @@ public: bool isNew() { return mIsNew; } + bool canClose(); + protected: LLPanelClassifiedEdit(); @@ -325,6 +327,10 @@ protected: std::string getLocationNotice(); + bool isValidName(); + + void notifyInvalidName(); + void onSetLocationClick(); void onChange(); void onSaveClick(); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 70e4798079..3f309b3bf5 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -247,6 +247,9 @@ void LLPanelGroupControlPanel::draw() //Remove event does not raised until speakerp->mActivityTimer.hasExpired() is false, see LLSpeakerManager::update() //so we need update it to raise needed event mSpeakerManager->update(true); + // Need to resort the participant list if it's in sort by recent speaker order. + if (mParticipantList) + mParticipantList->updateRecentSpeakersOrder(); LLPanelChatControlPanel::draw(); } @@ -282,8 +285,9 @@ void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) mGroupID = LLIMModel::getInstance()->getOtherParticipantID(session_id); + // for group and Ad-hoc chat we need to include agent into list if(!mParticipantList) - mParticipantList = new LLParticipantList(mSpeakerManager, getChild<LLAvatarList>("speakers_list")); + mParticipantList = new LLParticipantList(mSpeakerManager, getChild<LLAvatarList>("speakers_list"), true,false); } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index e134840153..e5846c7318 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -564,6 +564,7 @@ BOOL LLPanelPeople::postBuild() buttonSetAction("chat_btn", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); buttonSetAction("im_btn", boost::bind(&LLPanelPeople::onImButtonClicked, this)); buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this)); + buttonSetAction("group_call_btn", boost::bind(&LLPanelPeople::onGroupCallButtonClicked, this)); buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); @@ -733,6 +734,7 @@ void LLPanelPeople::updateButtons() buttonSetVisible("view_profile_btn", !group_tab_active); buttonSetVisible("im_btn", !group_tab_active); buttonSetVisible("call_btn", !group_tab_active); + buttonSetVisible("group_call_btn", group_tab_active); buttonSetVisible("teleport_btn", friends_tab_active); buttonSetVisible("share_btn", nearby_tab_active || friends_tab_active); @@ -781,6 +783,7 @@ void LLPanelPeople::updateButtons() bool none_group_selected = item_selected && selected_id.isNull(); buttonSetEnabled("group_info_btn", !none_group_selected); + buttonSetEnabled("group_call_btn", !none_group_selected); buttonSetEnabled("chat_btn", !none_group_selected); } @@ -1272,6 +1275,11 @@ void LLPanelPeople::onCallButtonClicked() } } +void LLPanelPeople::onGroupCallButtonClicked() +{ + LLGroupActions::startCall(getCurrentItemID()); +} + void LLPanelPeople::onTeleportButtonClicked() { LLAvatarActions::offerTeleport(getCurrentItemID()); diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index f5cdc0935c..0d2bae1baf 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -100,6 +100,7 @@ private: void onChatButtonClicked(); void onImButtonClicked(); void onCallButtonClicked(); + void onGroupCallButtonClicked(); void onTeleportButtonClicked(); void onShareButtonClicked(); void onMoreButtonClicked(); diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 0314642d9e..c1c10e6022 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -184,8 +184,6 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) else if (item == std::string("can_call")) { bool result = false; - int size = mUUIDs.size(); - std::cout << size << std::endl; std::vector<LLUUID>::const_iterator id = mUUIDs.begin(), uuids_end = mUUIDs.end(); diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 69fa110d11..751705dd57 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -781,6 +781,11 @@ void LLPanelPicks::onPanelPickSave(LLPanel* panel) void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel) { + if(!panel->canClose()) + { + return; + } + if(panel->isNew()) { LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), panel->getClassifiedId()); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index afb9892d12..5941487c7d 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -39,7 +39,6 @@ #include "llimview.h" #include "llparticipantlist.h" -#include "llavatarlist.h" #include "llspeakers.h" #include "llviewermenu.h" #include "llvoiceclient.h" @@ -51,12 +50,13 @@ static const LLAvatarItemAgentOnTopComparator AGENT_ON_TOP_NAME_COMPARATOR; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu/* = true*/): +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu/* = true*/, + bool exclude_agent /*= true*/): mSpeakerMgr(data_source), mAvatarList(avatar_list), mSortOrder(E_SORT_BY_NAME) , mParticipantListMenu(NULL) -, mExcludeAgent(true) +, mExcludeAgent(exclude_agent) { mSpeakerAddListener = new SpeakerAddListener(*this); mSpeakerRemoveListener = new SpeakerRemoveListener(*this); @@ -101,7 +101,6 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av } } // we need to exclude agent id for non group chat - mExcludeAgent = !gAgent.isInGroup(mSpeakerMgr->getSessionID()); mAvatarList->setDirty(true); sort(); } @@ -204,24 +203,18 @@ void LLParticipantList::setSortOrder(EParticipantSortOrder order) } } -void LLParticipantList::refreshVoiceState() +LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() { - LLSpeakerMgr::speaker_list_t speakers; - mSpeakerMgr->getSpeakerList(&speakers, TRUE); + return mSortOrder; +} - for (LLSpeakerMgr::speaker_list_t::iterator iter = speakers.begin(); - iter != speakers.end(); ++iter) +void LLParticipantList::updateRecentSpeakersOrder() +{ + if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder()) { - LLSpeaker* speakerp = (*iter).get(); - const LLUUID& speaker_id = speakerp->mID; - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(speaker_id)); - if ( item ) - { - // if voice is disabled for this speaker show non voice speakers as disabled - bool is_in_voice = speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE - && speakerp->mStatus != LLSpeaker::STATUS_MUTED; - item->setOnline(!is_in_voice); - } + // Resort avatar list + mAvatarList->setDirty(true); + sort(); } } @@ -312,7 +305,6 @@ void LLParticipantList::sort() if ( !mAvatarList ) return; - // TODO: Implement more sorting orders after specs updating (EM) switch ( mSortOrder ) { case E_SORT_BY_NAME : // if mExcludeAgent == true , then no need to keep agent on top of the list @@ -326,6 +318,12 @@ void LLParticipantList::sort() mAvatarList->sort(); } break; + case E_SORT_BY_RECENT_SPEAKERS: + if (mSortByRecentSpeakers.isNull()) + mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this); + mAvatarList->setComparator(mSortByRecentSpeakers.get()); + mAvatarList->sort(); + break; default : llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl; return; @@ -402,6 +400,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + registrar.add("ParticipantList.Sort", boost::bind(&LLParticipantList::LLParticipantListMenu::sortParticipantList, this, _2)); registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2)); registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2)); @@ -447,6 +446,24 @@ void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteSelected", false); LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteOthers", false); } + + // Don't show sort options for P2P chat + bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); + LLMenuGL::sMenuContainer->childSetVisible("SortByName", is_sort_visible); + LLMenuGL::sMenuContainer->childSetVisible("SortByRecentSpeakers", is_sort_visible); +} + +void LLParticipantList::LLParticipantListMenu::sortParticipantList(const LLSD& userdata) +{ + std::string param = userdata.asString(); + if ("sort_by_name" == param) + { + mParent.setSortOrder(E_SORT_BY_NAME); + } + else if ("sort_by_recent_speakers" == param) + { + mParent.setSortOrder(E_SORT_BY_RECENT_SPEAKERS); + } } void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata) @@ -555,7 +572,7 @@ void LLParticipantList::LLParticipantListMenu::moderateVoiceOtherParticipants(co bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata) { std::string item = userdata.asString(); - if (item == "can_mute_text") + if (item == "can_mute_text" || "can_block" == item) { return mUUIDs.front() != gAgentID; } @@ -616,8 +633,45 @@ bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD& { return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); } + else if(item == "is_sorted_by_name") + { + return E_SORT_BY_NAME == mParent.mSortOrder; + } + else if(item == "is_sorted_by_recent_speakers") + { + return E_SORT_BY_RECENT_SPEAKERS == mParent.mSortOrder; + } return false; } +bool LLParticipantList::LLAvatarItemRecentSpeakerComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const +{ + if (mParent.mSpeakerMgr) + { + LLPointer<LLSpeaker> lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId()); + LLPointer<LLSpeaker> rhs = mParent.mSpeakerMgr->findSpeaker(avatar_item2->getAvatarId()); + if ( lhs.notNull() && rhs.notNull() ) + { + // Compare by last speaking time + if( lhs->mLastSpokeTime != rhs->mLastSpokeTime ) + return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime ); + else if ( lhs->mSortIndex != rhs->mSortIndex ) + return ( lhs->mSortIndex < rhs->mSortIndex ); + } + else if ( lhs.notNull() ) + { + // True if only avatar_item1 speaker info available + return true; + } + else if ( rhs.notNull() ) + { + // False if only avatar_item2 speaker info available + return false; + } + } + // By default compare by name. + return LLAvatarItemNameComparator::doCompare(avatar_item1, avatar_item2); +} + //EOF diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 72c413d188..c4eb180917 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -34,6 +34,7 @@ #include "llevent.h" #include "llpanelpeoplemenus.h" #include "llimview.h" +#include "llavatarlist.h" // for LLAvatarItemRecentSpeakerComparator class LLSpeakerMgr; class LLAvatarList; @@ -43,24 +44,25 @@ class LLParticipantList { LOG_CLASS(LLParticipantList); public: - LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu = true); + LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu = true, bool exclude_agent = true); ~LLParticipantList(); void setSpeakingIndicatorsVisible(BOOL visible); typedef enum e_participant_sort_oder { E_SORT_BY_NAME = 0, + E_SORT_BY_RECENT_SPEAKERS = 1, } EParticipantSortOrder; /** * Set and sort Avatarlist by given order */ void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME); + EParticipantSortOrder getSortOrder(); /** - * Refreshes participants to display ones not in voice as disabled. - * TODO: mantipov: probably should be moved into derived class for LLFloaterCall + * Refreshes the participant list if it's in sort by recent speaker order. */ - void refreshVoiceState(); + void updateRecentSpeakersOrder(); protected: /** @@ -139,6 +141,7 @@ class LLParticipantList bool enableContextMenuItem(const LLSD& userdata); bool checkContextMenuItem(const LLSD& userdata); + void sortParticipantList(const LLSD& userdata); void toggleAllowTextChat(const LLSD& userdata); void toggleMute(const LLSD& userdata, U32 flags); void toggleMuteText(const LLSD& userdata); @@ -195,6 +198,21 @@ class LLParticipantList void moderateVoiceOtherParticipants(const LLUUID& excluded_avatar_id, bool unmute); }; + /** + * Comparator for comparing avatar items by last spoken time + */ + class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator, public LLRefCount + { + LOG_CLASS(LLAvatarItemRecentSpeakerComparator); + public: + LLAvatarItemRecentSpeakerComparator(LLParticipantList& parent):mParent(parent){}; + virtual ~LLAvatarItemRecentSpeakerComparator() {}; + protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; + private: + LLParticipantList& mParent; + }; + private: void onAvatarListDoubleClicked(LLAvatarList* list); void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param); @@ -240,4 +258,6 @@ class LLParticipantList boost::signals2::connection mAvatarListDoubleClickConnection; boost::signals2::connection mAvatarListRefreshConnection; boost::signals2::connection mAvatarListReturnConnection; + + LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers; }; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 3861a96355..91b417c61f 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -630,8 +630,6 @@ void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) { - if (gAgentID == avatar_id) return; // do not process myself - LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id); if (!speakerp) return; diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 26f9824f9c..8c6ea59407 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -352,6 +352,7 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& } // Initialize chiclet. + mChiclet->setRect(LLRect(5, 28, 30, 3)); // *HACK: workaround for (EXT-3599) mChiclet->setChicletSizeChangedCallback(boost::bind(&LLIMWellWindow::RowPanel::onChicletSizeChanged, this, mChiclet, _2)); mChiclet->enableCounterControl(true); mChiclet->setCounter(chicletCounter); @@ -744,9 +745,6 @@ void LLIMWellWindow::sessionAdded(const LLUUID& session_id, { if (mMessageList->getItemByValue(session_id)) return; - // For im sessions started as voice call chiclet gets created on the first incoming message - if (gIMMgr->isVoiceCall(session_id)) return; - if (!gIMMgr->hasSession(session_id)) return; addIMRow(session_id, 0, name, other_participant_id); @@ -905,23 +903,6 @@ bool LLIMWellWindow::hasIMRow(const LLUUID& session_id) return mMessageList->getItemByValue(session_id); } -void LLIMWellWindow::onNewIM(const LLSD& data) -{ - LLUUID from_id = data["from_id"]; - if (from_id.isNull() || gAgentID == from_id) return; - - LLUUID session_id = data["session_id"]; - if (session_id.isNull()) return; - - if (!gIMMgr->isVoiceCall(session_id)) return; - - if (hasIMRow(session_id)) return; - - //first real message, time to create chiclet - addIMRow(session_id); -} - - void LLIMWellWindow::closeAll() { // Generate an ignorable alert dialog if there is an active voice IM sesion diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 6cfa25b84d..7030f4b427 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -191,8 +191,6 @@ public: /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); - void onNewIM(const LLSD& data); - void addObjectRow(const LLUUID& object_id, bool new_message = false); void removeObjectRow(const LLUUID& object_id); diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 7befb87248..347399f239 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -37,6 +37,7 @@ #include "llrootview.h" #include "llviewerwindow.h" #include "lldockablefloater.h" +#include "llmenugl.h" LLTransientFloaterMgr::LLTransientFloaterMgr() @@ -87,6 +88,13 @@ void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, for (controls_set_t::iterator it = mControlsSet.begin(); it != mControlsSet.end(); it++) { + // don't hide transient floater if any context menu opened + if (LLMenuGL::sMenuContainer->getVisibleMenu() != NULL) + { + hide = false; + break; + } + LLView* control_view = *it; if (!control_view->getVisible()) { diff --git a/indra/newview/skins/default/xui/en/menu_participant_list.xml b/indra/newview/skins/default/xui/en/menu_participant_list.xml index faf33bd1b1..31263fbea8 100644 --- a/indra/newview/skins/default/xui/en/menu_participant_list.xml +++ b/indra/newview/skins/default/xui/en/menu_participant_list.xml @@ -2,7 +2,29 @@ <context_menu layout="topleft" name="Participant List Context Menu"> - <menu_item_call + <menu_item_check + label="Sort by Name" + layout="topleft" + name="SortByName"> + <menu_item_check.on_click + function="ParticipantList.Sort" + parameter="sort_by_name" /> + <menu_item_check.on_check + function="ParticipantList.CheckItem" + parameter="is_sorted_by_name" /> + </menu_item_check> + <menu_item_check + label="Sort by Recent Speakers" + layout="topleft" + name="SortByRecentSpeakers"> + <menu_item_check.on_click + function="ParticipantList.Sort" + parameter="sort_by_recent_speakers" /> + <menu_item_check.on_check + function="ParticipantList.CheckItem" + parameter="is_sorted_by_recent_speakers" /> + </menu_item_check> + <menu_item_call label="View Profile" layout="topleft" name="View Profile"> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups.xml b/indra/newview/skins/default/xui/en/menu_people_groups.xml new file mode 100644 index 0000000000..afa680139d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_groups.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="false"> + <menu_item_call + label="View Info" + name="View Info"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="view_info" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="view_info" /> + </menu_item_call> + <menu_item_call + label="Chat" + name="Chat"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="chat" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="chat" /> + </menu_item_call> + <menu_item_call + label="Call" + name="Call"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="call" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="call" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Activate" + name="Activate"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="activate" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="activate" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Leave" + name="Leave"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="leave" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="leave" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 3e2910458f..5ae808581d 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -327,6 +327,7 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. min_width="35" user_resize="false"> <chiclet_im_well + max_displayed_count="99" flash_period="0.3" follows="right" height="23" @@ -356,7 +357,6 @@ image_pressed_selected "Lit" + "Selected" - there are new messages and the Well image_selected="PushButton_Selected_Press" label_color="Black" left="0" - max_displayed_count="99" name="Unread IM messages" pad_left="0" pad_right="0" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 8883c27c47..08a10553a8 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -411,5 +411,15 @@ background_visible="true" name="chat_btn" tool_tip="Open chat session" width="110" /> + <button + follows="bottom|left" + top="4" + left_pad="2" + height="23" + label="Group Call" + layout="topleft" + name="group_call_btn" + tool_tip="Call this group" + width="110" /> </panel> </panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index a1f2548f81..9aae04ba38 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2964,12 +2964,26 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. <string name="not_a_mod_error"> You are not a session moderator. </string> + <!--Some times string name is getting from the body of server response. + For ex.: from gIMMgr::showSessionStartError in the LLViewerChatterBoxSessionStartReply::post. + In case of the EXT-3562 issue 'muted' is passed into the gIMMgr::showSessionStartError as a string name. + So, let add string with name="muted" with the same value as "muted_error" --> + <string name="muted"> + A group moderator disabled your text chat. + </string> <string name="muted_error"> A group moderator disabled your text chat. </string> <string name="add_session_event"> Unable to add users to chat session with [RECIPIENT]. </string> + <!--Some times string name is getting from the body of server response. + For ex.: from gIMMgr::showSessionStartError in the LLViewerChatterBoxSessionStartReply::post. + In case of the EXT-3562 issue 'message' is passed into the gIMMgr::showSessionStartError as a string name. + So, let add string with name="message" with the same value as "message_session_event" --> + <string name="message"> + Unable to send your message to the chat session with [RECIPIENT]. + </string> <string name="message_session_event"> Unable to send your message to the chat session with [RECIPIENT]. </string> |