diff options
Diffstat (limited to 'indra/newview')
24 files changed, 395 insertions, 155 deletions
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 248685b964..3326103d03 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -697,15 +697,15 @@ namespace action_give_inventory } // static -void LLAvatarActions::buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) +void LLAvatarActions::buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string) { llassert(avatar_names.size() > 0); - + + std::sort(avatar_names.begin(), avatar_names.end()); const std::string& separator = LLTrans::getString("words_separator"); for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) { - LLAvatarName av_name = *it; - residents_string.append(av_name.mDisplayName); + residents_string.append((*it).mDisplayName); if (++it == avatar_names.end()) { break; @@ -714,6 +714,28 @@ void LLAvatarActions::buildResidentsString(const std::vector<LLAvatarName> avata } } +// static +void LLAvatarActions::buildResidentsString(const uuid_vec_t& avatar_uuids, std::string& residents_string) +{ + std::vector<LLAvatarName> avatar_names; + uuid_vec_t::const_iterator it = avatar_uuids.begin(); + for (; it != avatar_uuids.end(); ++it) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(*it, &av_name)) + { + avatar_names.push_back(av_name); + } + } + + // We should check whether the vector is not empty to pass the assertion + // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. + if (!avatar_names.empty()) + { + LLAvatarActions::buildResidentsString(avatar_names, residents_string); + } +} + //static std::set<LLUUID> LLAvatarActions::getInventorySelectedUUIDs() { diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 6e60f624ad..6e1198cd09 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -216,7 +216,15 @@ public: * @param avatar_names - a vector of given avatar names from which resulting string is built * @param residents_string - the resulting string */ - static void buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string); + static void buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string); + + /** + * Builds a string of residents' display names separated by "words_separator" string. + * + * @param avatar_uuids - a vector of given avatar uuids from which resulting string is built + * @param residents_string - the resulting string + */ + static void buildResidentsString(const uuid_vec_t& avatar_uuids, std::string& residents_string); /** * Opens the chat history for avatar diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index f51d7b622c..6395f5b694 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -874,6 +874,7 @@ class LLIMWellChiclet : public LLSysWellChiclet, LLIMSessionObserver friend class LLUICtrlFactory; public: /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h index 7d0d904810..a9a5b61ae7 100644 --- a/indra/newview/llchicletbar.h +++ b/indra/newview/llchicletbar.h @@ -51,6 +51,7 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h index 373406aa6f..b92cf0f5e2 100644 --- a/indra/newview/llconversationlog.h +++ b/indra/newview/llconversationlog.h @@ -124,6 +124,7 @@ public: // LLIMSessionObserver triggers virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; // Stub virtual void sessionRemoved(const LLUUID& session_id){} // Stub virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){}; // Stub virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){}; // Stub diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 5fc305da81..e5232d730c 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -27,6 +27,11 @@ #include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llavataractions.h" +#include "llevents.h" +#include "llsdutil.h" #include "llconversationmodel.h" #include "llimview.h" //For LLIMModel @@ -64,6 +69,14 @@ LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_mod { } +void LLConversationItem::postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant) +{ + LLUUID session_id = (session ? session->getUUID() : LLUUID()); + LLUUID participant_id = (participant ? participant->getUUID() : LLUUID()); + LLSD event(LLSDMap("type", event_type)("session_uuid", session_id)("participant_uuid", participant_id)); + LLEventPumps::instance().obtain("ConversationsEvents").post(event); +} + // Virtual action callbacks void LLConversationItem::performAction(LLInventoryModel* model, std::string action) { @@ -130,12 +143,55 @@ void LLConversationItemSession::addParticipant(LLConversationItemParticipant* pa addChild(participant); mIsLoaded = true; mNeedsRefresh = true; + updateParticipantName(participant); + postEvent("add_participant", this, participant); +} + +void LLConversationItemSession::updateParticipantName(LLConversationItemParticipant* participant) +{ + // We modify the session name only in the case of an ad-hoc session, exit otherwise (nothing to do) + if (getType() != CONV_SESSION_AD_HOC) + { + return; + } + // Avoid changing the default name if no participant present yet + if (mChildren.size() == 0) + { + return; + } + // Build a string containing the participants names and check if ready for display (we don't want "(waiting)" in there) + bool all_names_resolved = true; + uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string + child_list_t::iterator iter = mChildren.begin(); + while (iter != mChildren.end()) + { + LLConversationItemParticipant* current_participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + temp_uuids.push_back(current_participant->getUUID()); + LLAvatarName av_name; + if (!LLAvatarNameCache::get(current_participant->getUUID(), &av_name)) + { + // If the name is not in the cache yet, bail out + // Note: we don't bind ourselves to the LLAvatarNameCache event as we are called by + // onAvatarNameCache() which is itself attached to the same event. + all_names_resolved = false; + break; + } + iter++; + } + if (all_names_resolved) + { + std::string new_session_name; + LLAvatarActions::buildResidentsString(temp_uuids, new_session_name); + renameItem(new_session_name); + postEvent("update_session", this, NULL); + } } void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant) { removeChild(participant); mNeedsRefresh = true; + postEvent("remove_participant", this, participant); } void LLConversationItemSession::removeParticipant(const LLUUID& participant_id) @@ -319,7 +375,10 @@ void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t items; menuentry_vec_t disabled_items; - buildParticipantMenuOptions(items); + if(gAgent.getID() != mUUID) + { + buildParticipantMenuOptions(items); + } hide_context_entries(menu, items, disabled_items); } @@ -328,10 +387,13 @@ void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_nam mName = (av_name.mUsername.empty() ? av_name.mDisplayName : av_name.mUsername); mDisplayName = (av_name.mDisplayName.empty() ? av_name.mUsername : av_name.mDisplayName); mNeedsRefresh = true; - if (mParent) + LLConversationItemSession* parent_session = dynamic_cast<LLConversationItemSession*>(mParent); + if (parent_session) { - mParent->requestSort(); + parent_session->requestSort(); + parent_session->updateParticipantName(this); } + postEvent("update_participant", parent_session, this); } void LLConversationItemParticipant::dumpDebugData() diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index bc72cd96ea..1d082852f5 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -126,6 +126,8 @@ public: void resetRefresh() { mNeedsRefresh = false; } bool needsRefresh() { return mNeedsRefresh; } + void postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant); + void buildParticipantMenuOptions(menuentry_vec_t& items); protected: @@ -147,6 +149,7 @@ public: LLPointer<LLUIImage> getIcon() const { return NULL; } void setSessionID(const LLUUID& session_id) { mUUID = session_id; mNeedsRefresh = true; } void addParticipant(LLConversationItemParticipant* participant); + void updateParticipantName(LLConversationItemParticipant* participant); void removeParticipant(LLConversationItemParticipant* participant); void removeParticipant(const LLUUID& participant_id); void clearParticipants(); diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index d4eb551f7a..b7ebb70e86 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -32,6 +32,8 @@ #include <boost/bind.hpp> #include "llagentdata.h" #include "llconversationmodel.h" +#include "llimfloater.h" +#include "llnearbychat.h" #include "llimconversation.h" #include "llimfloatercontainer.h" #include "llfloaterreg.h" @@ -181,10 +183,8 @@ void LLConversationViewSession::draw() // draw highlight for selected items drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); - // draw children if root folder, or any other folder that is open or animating to closed state - bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) - || isOpen() - || mCurHeight != mTargetHeight; + // Draw children if root folder, or any other folder that is open. Do not draw children when animating to closed state or you get rendering overlap. + bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) || isOpen(); for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end();) @@ -407,21 +407,50 @@ void LLConversationViewParticipant::draw() static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); - const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled const LLFontGL* font = getLabelFontForStyle(mLabelStyle); F32 right_x = 0; F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; F32 text_left = (F32)getLabelXPos(); - LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; + LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; - drawHighlight(show_context, filled, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); + drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); drawLabel(font, text_left, y, color, right_x); LLView::draw(); } +void LLConversationViewParticipant::selectItem() +{ + LLConversationItem* vmi = this->getParentFolder() ? static_cast<LLConversationItem*>(this->getParentFolder()->getViewModelItem()) : NULL; + LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); + LLFloater* session_floater; + + //Only execute when switching floaters (conversations) + if(vmi && vmi->getUUID() != container->getSelectedSession()) + { + //When null, show the nearby chat conversation floater + if(vmi->getUUID().isNull()) + { + LLNearbyChat* nearbyChat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + nearbyChat->show(); + } + //Otherwise, show the IM conversation floater + else + { + LLIMFloater::show(vmi->getUUID()); + } + } + //Focus the current conversation floater (it is already visible so just focus it) + else + { + session_floater = LLIMConversation::getConversation(vmi->getUUID()); + session_floater->setFocus(TRUE); + } + + LLFolderViewItem::selectItem(); +} void LLConversationViewParticipant::refresh() { diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index c81c70b456..bd95387bbe 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -113,6 +113,7 @@ public: }; virtual ~LLConversationViewParticipant( void ) { } + void selectItem(); bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } virtual void refresh(); void addToFolder(LLFolderViewFolder* folder); diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 2ad7f9b193..bd2a2419a8 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -54,7 +54,6 @@ LLIMConversation::LLIMConversation(const LLSD& session_id) , mInputEditor(NULL) , mInputEditorTopPad(0) , mRefreshTimer(new LLTimer()) - , mHasFocus(false) { mSession = LLIMModel::getInstance()->findIMSession(mSessionID); @@ -216,21 +215,11 @@ void LLIMConversation::onFocusReceived() } LLTransientDockableFloater::onFocusReceived(); - - mHadFocus = mHasFocus; - mHasFocus = true; - - if (! mHadFocus) - { - LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); - container->setConvItemSelect(mSessionID); - } } void LLIMConversation::onFocusLost() { setBackgroundOpaque(false); - mHasFocus = false; LLTransientDockableFloater::onFocusLost(); } @@ -393,7 +382,7 @@ void LLIMConversation::updateHeaderAndToolbar() // prevent start conversation before its container LLIMFloaterContainer::getInstance(); - bool is_torn_off = !getHost(); + bool is_torn_off = checkIfTornOff(); if (!is_torn_off) { hideAllStandardButtons(); @@ -505,16 +494,12 @@ void LLIMConversation::onSlide(LLIMConversation* self) /*virtual*/ void LLIMConversation::onOpen(const LLSD& key) { - LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); - bool is_hosted = !!host_floater; - if (is_hosted) + if (!checkIfTornOff()) { + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); // Show the messages pane when opening a floater hosted in the Conversations host_floater->collapseMessagesPane(false); } - - setTornOff(!is_hosted); - updateHeaderAndToolbar(); } // virtual @@ -546,3 +531,16 @@ bool LLIMConversation::isChatMultiTab() // Restart is required in order to change chat window type. return true; } + +bool LLIMConversation::checkIfTornOff() +{ + bool isTorn = !getHost(); + + if (isTorn != isTornOff()) + { + setTornOff(isTorn); + updateHeaderAndToolbar(); + } + + return isTorn; +} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index c54081d316..603e0d0197 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -138,10 +138,9 @@ private: */ void reshapeChatHistory(); - LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. + bool checkIfTornOff(); - bool mHadFocus; - bool mHasFocus; + LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. }; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 99337bd5f3..e4032738a7 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -60,10 +60,6 @@ #include "llnotificationmanager.h" #include "llautoreplace.h" -/// Helper function to resolve resident names from given uuids -/// and form a string of names separated by "words_separator". -static void build_names_string(const uuid_vec_t& uuids, std::string& names_string); - floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; LLIMFloater::LLIMFloater(const LLUUID& session_id) @@ -476,7 +472,7 @@ void LLIMFloater::addP2PSessionParticipants(const LLSD& notification, const LLSD void LLIMFloater::sendParticipantsAddedNotification(const uuid_vec_t& uuids) { std::string names_string; - build_names_string(uuids, names_string); + LLAvatarActions::buildResidentsString(uuids, names_string); LLStringUtil::format_map_t args; args["[NAME]"] = names_string; @@ -581,7 +577,7 @@ void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) if (all_names_resolved) { std::string ui_title; - build_names_string(temp_uuids, ui_title); + LLAvatarActions::buildResidentsString(temp_uuids, ui_title); updateSessionName(ui_title, ui_title); } } @@ -719,6 +715,19 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) } } +void LLIMFloater::setFocus(BOOL focusFlag) +{ + LLTransientDockableFloater::setFocus(focusFlag); + + //Redirect focus to input editor + if (focusFlag) + { + updateMessages(); + mInputEditor->setFocus(TRUE); + } + +} + void LLIMFloater::setVisible(BOOL visible) { LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*> @@ -734,21 +743,6 @@ void LLIMFloater::setVisible(BOOL visible) channel->redrawToasts(); } - BOOL is_minimized = visible && isChatMultiTab() - ? LLIMFloaterContainer::getInstance()->isMinimized() - : !visible; - - if (!is_minimized && mChatHistory && mInputEditor) - { - //only if floater was construced and initialized from xml - updateMessages(); - //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) - if (!isChatMultiTab() || hasFocus()) - { - mInputEditor->setFocus(TRUE); - } - } - if(!visible) { LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(mSessionID); @@ -761,7 +755,10 @@ void LLIMFloater::setVisible(BOOL visible) if (visible && isInVisibleChain()) { sIMFloaterShowedSignal(mSessionID); + } + + setFocus(visible); } BOOL LLIMFloater::getVisible() @@ -1334,25 +1331,3 @@ boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floate { return LLIMFloater::sIMFloaterShowedSignal.connect(cb); } - -// static -void build_names_string(const uuid_vec_t& uuids, std::string& names_string) -{ - std::vector<LLAvatarName> avatar_names; - uuid_vec_t::const_iterator it = uuids.begin(); - for (; it != uuids.end(); ++it) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(*it, &av_name)) - { - avatar_names.push_back(av_name); - } - } - - // We should check whether the vector is not empty to pass the assertion - // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. - if (!avatar_names.empty()) - { - LLAvatarActions::buildResidentsString(avatar_names, names_string); - } -} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 5ed1d1ab35..26daf00afd 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -65,6 +65,7 @@ public: // LLView overrides /*virtual*/ BOOL postBuild(); + /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 4af170b3db..5c1105531e 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -50,6 +50,7 @@ #include "llcallbacklist.h" #include "llworld.h" +#include "llsdserialize.h" // // LLIMFloaterContainer // @@ -57,6 +58,7 @@ LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) : LLMultiFloater(seed), mExpandCollapseBtn(NULL), mConversationsRoot(NULL), + mConversationsEventStream("ConversationsEvents"), mInitialized(false) { mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2)); @@ -77,6 +79,8 @@ LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) LLIMFloaterContainer::~LLIMFloaterContainer() { + mConversationsEventStream.stopListening("ConversationsRefresh"); + gIdleCallbacks.deleteFunction(idle, this); mNewMessageConnection.disconnect(); @@ -94,19 +98,23 @@ LLIMFloaterContainer::~LLIMFloaterContainer() void LLIMFloaterContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) { LLIMFloater::addToHost(session_id, true); - addConversationListItem(session_id); + addConversationListItem(session_id, true); +} + +void LLIMFloaterContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + setItemSelect(session_id); } void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) { LLIMFloater::addToHost(session_id, true); - addConversationListItem(session_id); + addConversationListItem(session_id, true); } void LLIMFloaterContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) { - removeConversationListItem(old_session_id); - addConversationListItem(new_session_id); + addConversationListItem(new_session_id, removeConversationListItem(old_session_id)); } void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) @@ -138,6 +146,9 @@ BOOL LLIMFloaterContainer::postBuild() mConversationsListPanel = getChild<LLPanel>("conversations_list_panel"); + // Open IM session with selected participant on double click event + mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLIMFloaterContainer::doToSelected, this, LLSD("im"))); + // Create the root model and view for all conversation sessions LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); @@ -155,6 +166,9 @@ BOOL LLIMFloaterContainer::postBuild() mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + // Add listener to conversation model events + mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLIMFloaterContainer::onConversationModelEvent, this, _1)); + // a scroller for folder view LLRect scroller_view_rect = mConversationsListPanel->getRect(); scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); @@ -397,62 +411,81 @@ void LLIMFloaterContainer::idle(void* user_data) self->mConversationsRoot->update(); } -void LLIMFloaterContainer::draw() +bool LLIMFloaterContainer::onConversationModelEvent(const LLSD& event) { - // CHUI Notes - // Currently, the model is not responsible for creating the view which is a good thing. This means that - // the model could change substantially and the view could decide to echo only a portion of this model. - // Consequently, the participant views need to be created either by the session view or by the container panel. - // For the moment, we create them here (which makes for complicated code...) to conform to the pattern - // implemented in llinventorypanel.cpp (see LLInventoryPanel::buildNewViews()). - // The best however would be to have an observer on the model so that we would not pool on each draw to know - // if the view needs refresh. The current implementation (testing for change on draw) is less - // efficient perf wise than a listener/observer scheme. We will implement that shortly. + // For debug only + //std::ostringstream llsd_value; + //llsd_value << LLSDOStreamer<LLSDNotationFormatter>(event) << std::endl; + //llinfos << "Merov debug : onConversationModelEvent, event = " << llsd_value.str() << llendl; + // end debug - // On each session in mConversationsItems - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that + // the model could change substantially and the view could echo only a portion of this model (though currently the + // conversation view does echo the conversation model 1 to 1). + // Consequently, the participant views need to be created either by the session view or by the container panel. + // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp + // (see LLInventoryPanel::buildNewViews()). + + std::string type = event.get("type").asString(); + LLUUID session_id = event.get("session_uuid").asUUID(); + LLUUID participant_id = event.get("participant_uuid").asUUID(); + + LLConversationViewSession* session_view = dynamic_cast<LLConversationViewSession*>(mConversationsWidgets[session_id]); + if (!session_view) { - // Get the current session descriptors - LLConversationItem* session_model = it_session->second; - LLUUID session_id = it_session->first; - LLConversationViewSession* session_view = dynamic_cast<LLConversationViewSession*>(mConversationsWidgets[session_id]); - // If the session model has been changed, refresh the corresponding view - if (session_model->needsRefresh()) + // We skip events that are not associated to a session + return false; + } + LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); + + if (type == "remove_participant") + { + if (participant_view) { + session_view->extractItem(participant_view); + delete participant_view; session_view->refresh(); + mConversationsRoot->arrangeAll(); } - // Iterate through each model participant child - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); - while (current_participant_model != end_participant_model) + } + else if (type == "add_participant") + { + if (!participant_view) { - LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model); - LLUUID participant_id = participant_model->getUUID(); - LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); - // Is there a corresponding view? If not create it - if (!participant_view) + LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(mConversationsItems[session_id]); + if (session_model) { - participant_view = createConversationViewParticipant(participant_model); - participant_view->addToFolder(session_view); - participant_view->setVisible(TRUE); - } - else - // Else, see if it needs refresh - { - if (participant_model->needsRefresh()) + LLConversationItemParticipant* participant_model = session_model->findParticipant(participant_id); + if (participant_model) { - participant_view->refresh(); + participant_view = createConversationViewParticipant(participant_model); + participant_view->addToFolder(session_view); + participant_view->setVisible(TRUE); } } - // Reset the need for refresh - session_model->resetRefresh(); - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - // Next participant - current_participant_model++; + + } + } + else if (type == "update_participant") + { + if (participant_view) + { + participant_view->refresh(); } } + else if (type == "update_session") + { + session_view->refresh(); + } + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + + return false; +} + +void LLIMFloaterContainer::draw() +{ if (mTabContainer->getTabCount() == 0) { // Do not close the container when every conversation is torn off because the user @@ -527,12 +560,25 @@ void LLIMFloaterContainer::collapseMessagesPane(bool collapse) gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); } + // Save left pane rectangle before collapsing/expanding right pane. + LLRect prevRect = mConversationsPane->getRect(); + // Show/hide the messages pane. mConversationsStack->collapsePanel(mMessagesPane, collapse); + if (!collapse) + { + // Make sure layout is updated before resizing conversation pane. + mConversationsStack->updateLayout(); + } + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); + if (!collapse) + { + // Restore conversation's pane previous width after expanding messages pane. + mConversationsPane->setTargetDim(prevRect.getWidth()); + } } - void LLIMFloaterContainer::collapseConversationsPane(bool collapse) { if (mConversationsPane->isCollapsed() == collapse) @@ -862,7 +908,13 @@ void LLIMFloaterContainer::doToSelectedConversation(const std::string& command, } else if("chat_history" == command) { - LLAvatarActions::viewChatHistory(conversationItem->getUUID()); + const LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(conversationItem->getUUID()); + + if (NULL != session) + { + const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID; + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } } else { @@ -914,6 +966,12 @@ bool LLIMFloaterContainer::enableContextMenuItem(const LLSD& userdata) uuid_vec_t mUUIDs; getParticipantUUIDs(mUUIDs); + if(item == std::string("can_activate_group")) + { + LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID(); + return gAgent.getGroupID() != selected_group_id; + } + if(mUUIDs.size() <= 0) { return false; @@ -1014,6 +1072,7 @@ bool LLIMFloaterContainer::checkContextMenuItem(const LLSD& userdata) return false; } +//Will select only the conversation item void LLIMFloaterContainer::setConvItemSelect(const LLUUID& session_id) { LLFolderViewItem* widget = mConversationsWidgets[session_id]; @@ -1024,6 +1083,26 @@ void LLIMFloaterContainer::setConvItemSelect(const LLUUID& session_id) } } +//Will select the conversation/participant item +void LLIMFloaterContainer::setItemSelect(const LLUUID& session_id) +{ + + if(mConversationsRoot->getCurSelectedItem() && mConversationsRoot->getCurSelectedItem()->getParentFolder()) + { + //Retreive the conversation id. When a participant is selected, then have to to get the converation id from the parent. + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(mConversationsRoot->getCurSelectedItem()->getParentFolder()->getViewModelItem()); + + //Will allow selection/highlighting of the conversation/participant + if(session_id != vmi->getUUID()) + { + mSelectedSession = session_id; + LLFolderViewItem* widget = mConversationsWidgets[session_id]; + (widget->getRoot())->setSelection(widget, FALSE, FALSE); + } + } +} + + void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) { conversations_items_map::iterator item_it = mConversationsItems.find(session_id); @@ -1070,7 +1149,7 @@ void LLIMFloaterContainer::setNearbyDistances() } } -void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) +void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/) { bool is_nearby_chat = uuid.isNull(); @@ -1089,7 +1168,7 @@ void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) removeConversationListItem(uuid,false); // Create a conversation session model - LLConversationItem* item = NULL; + LLConversationItemSession* item = NULL; LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid)); if (speaker_manager) { @@ -1101,6 +1180,7 @@ void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) return; } item->renameItem(display_name); + item->updateParticipantName(NULL); mConversationsItems[uuid] = item; @@ -1124,7 +1204,10 @@ void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) current_participant_model++; } - setConvItemSelect(uuid); + if (isWidgetSelected) + { + setConvItemSelect(uuid); + } // set the widget to minimized mode if conversations pane is collapsed widget->toggleMinimizedMode(mConversationsPane->isCollapsed()); @@ -1132,17 +1215,19 @@ void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) return; } -void LLIMFloaterContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) +bool LLIMFloaterContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) { // Delete the widget and the associated conversation item // Note : since the mConversationsItems is also the listener to the widget, deleting // the widget will also delete its listener + bool isWidgetSelected = false; conversations_widgets_map::iterator widget_it = mConversationsWidgets.find(uuid); if (widget_it != mConversationsWidgets.end()) { LLFolderViewItem* widget = widget_it->second; if (widget) { + isWidgetSelected = widget->isSelected(); widget->destroyView(); } } @@ -1158,10 +1243,12 @@ void LLIMFloaterContainer::removeConversationListItem(const LLUUID& uuid, bool c conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); if (widget_it != mConversationsWidgets.end()) { + mSelectedSession = widget_it->first; LLFolderViewItem* widget = widget_it->second; widget->selectItem(); } } + return isWidgetSelected; } LLConversationViewSession* LLIMFloaterContainer::createConversationItemWidget(LLConversationItem* item) diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 76e468f979..6643471d97 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -31,6 +31,7 @@ #include <vector> #include "llimview.h" +#include "llevents.h" #include "llfloater.h" #include "llmultifloater.h" #include "llavatarpropertiesprocessor.h" @@ -63,6 +64,7 @@ public: BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); void setConvItemSelect(const LLUUID& session_id); + void setItemSelect(const LLUUID& session_id); /*virtual*/ void tabClose(); static LLFloater* getCurrentVoiceFloater(); @@ -80,11 +82,13 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); LLConversationViewModel& getRootViewModel() { return mConversationViewModel; } + LLUUID getSelectedSession() { return mSelectedSession; } private: typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t; @@ -132,14 +136,15 @@ private: // Conversation list implementation public: - void removeConversationListItem(const LLUUID& uuid, bool change_focus = true); - void addConversationListItem(const LLUUID& uuid); + bool removeConversationListItem(const LLUUID& uuid, bool change_focus = true); + void addConversationListItem(const LLUUID& uuid, bool isWidgetSelected = false); void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); void setNearbyDistances(); private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); + bool onConversationModelEvent(const LLSD& event); // Conversation list data LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to @@ -147,6 +152,7 @@ private: conversations_widgets_map mConversationsWidgets; LLConversationViewModel mConversationViewModel; LLFolderView* mConversationsRoot; + LLEventStream mConversationsEventStream; }; #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b45903835a..115da54ec8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -647,8 +647,6 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con { mId2SessionMap.erase(old_session_id); mId2SessionMap[new_session_id] = session; - - gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); } LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); @@ -657,6 +655,11 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con im_floater->sessionInitReplyReceived(new_session_id); } + if (old_session_id != new_session_id) + { + gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); + } + // auto-start the call on session initialization? if (session->mStartCallOnInitialize) { @@ -2650,10 +2653,17 @@ LLUUID LLIMMgr::addSession( } } + //Notify observers that a session was added if (new_session) { LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice); } + //Notifies observers that the session was already added + else + { + std::string session_name = LLIMModel::getInstance()->getName(session_id); + LLIMMgr::getInstance()->notifyObserverSessionActivated(session_id, session_name, other_participant_id); + } //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions if (!new_session) return session_id; @@ -2956,6 +2966,14 @@ void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::st } } +void LLIMMgr::notifyObserverSessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionActivated(session_id, name, other_participant_id); + } +} + void LLIMMgr::notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id) { for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 82cfa394a6..00b67f520c 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -303,6 +303,7 @@ class LLIMSessionObserver public: virtual ~LLIMSessionObserver() {} virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; + virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) = 0; virtual void sessionRemoved(const LLUUID& session_id) = 0; virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) = 0; @@ -469,6 +470,8 @@ private: static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group); void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + //Triggers when a session has already been added + void notifyObserverSessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); void notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id); void notifyObserverSessionRemoved(const LLUUID& session_id); void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index b96b486868..a89ae4a2dc 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -271,14 +271,27 @@ void LLNearbyChat::removeScreenChat() } } -void LLNearbyChat::setVisible(BOOL visible) +void LLNearbyChat::setFocus(BOOL focusFlag) { - if(visible) - { - removeScreenChat(); - } + LLTransientDockableFloater::setFocus(focusFlag); + + //Redirect focus to input editor + if (focusFlag) + { + mInputEditor->setFocus(TRUE); + } + +} +void LLNearbyChat::setVisible(BOOL visible) +{ LLIMConversation::setVisible(visible); + + if(visible) + { + removeScreenChat(); + } + setFocus(visible); } diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 93168ba96a..7ada4daea8 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -52,7 +52,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - + /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); void loadHistory(); diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 6be12711ac..378d5e0aa2 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -171,6 +171,7 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 5c74f7f9bb..8cd0463de8 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -246,7 +246,7 @@ parse_highlights="true" parse_urls="true" width="230" - left="0"> + left="5"> </chat_history> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml index 912ff811d9..682d70e4f0 100644 --- a/indra/newview/skins/default/xui/en/menu_conversation.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -117,6 +117,7 @@ layout="topleft" name="activate_group"> <on_click function="Group.DoToSelected" parameter="activate_group"/> + <on_enable function="Avatar.EnableItem" parameter="can_activate_group" /> </menu_item_call> <menu_item_call label="Leave Group" diff --git a/indra/newview/skins/default/xui/en/menu_participant_view.xml b/indra/newview/skins/default/xui/en/menu_participant_view.xml index 0043c14479..6fa0707eea 100644 --- a/indra/newview/skins/default/xui/en/menu_participant_view.xml +++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml @@ -59,17 +59,6 @@ function="IMFloaterContainer.Check" parameter="sort_participants_by_recent" /> </menu_item_check> - <menu_item_check - label="Sort participants by distance from you" - layout="topleft" - name="sort_participants_by_distance"> - <on_click - function="IMFloaterContainer.Action" - parameter="sort_participants_by_distance" /> - <on_check - function="IMFloaterContainer.Check" - parameter="sort_participants_by_distance" /> - </menu_item_check> <menu_item_separator layout="topleft" /> <menu_item_call diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 88b30c8272..c805b6db42 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -218,8 +218,18 @@ label="Communicate" name="Communicate" tear_off="true"> - <menu_item_check - label="Chat..." + <menu_item_check + label="Conversations..." + name="Conversations"> + <menu_item_check.on_check + function="Floater.IsOpen" + parameter="im_container" /> + <menu_item_check.on_click + function="Floater.ToggleOrBringToFront" + parameter="im_container" /> + </menu_item_check> + <menu_item_check + label="Nearby Chat..." name="Nearby Chat" shortcut="control|H" use_mac_ctrl="true"> @@ -244,6 +254,17 @@ parameter="speak" /> </menu_item_check> <menu_item_check + label="Conversations Log..." + name="ConversationsLog"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="conversation" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="conversation" /> + </menu_item_check> + <menu_item_separator/> + <menu_item_check label="Voice morphing..." name="ShowVoice" visibility_control="VoiceMorphingEnabled"> |