diff options
52 files changed, 900 insertions, 369 deletions
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index e9f6288f44..9ce8ce8d55 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -1022,6 +1022,20 @@ void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign a } } +void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color) +{ + if (image_id.isNull()) + { + mImageOverlay = NULL; + } + else + { + mImageOverlay = LLUI::getUIImageByID(image_id); + mImageOverlayAlignment = alignment; + mImageOverlayColor = color; + } +} + void LLButton::onMouseCaptureLost() { resetMouseDownTimer(); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 5e28b8cdff..cd149e3113 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -200,6 +200,7 @@ public: void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; } void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); + void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; } void autoResize(); // resize with label of current btn state diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 327dd01612..43c44f2253 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1507,6 +1507,37 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const L } } +void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color) +{ + static LLUICachedControl<S32> tab_padding ("UITabPadding", 0); + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + tuple->mButton->setImageOverlay(image_id, LLFontGL::RIGHT, color); + + if (!mIsVertical) + { + // remove current width from total tab strip width + mTotalTabWidth -= tuple->mButton->getRect().getWidth(); + + S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? + tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : + 0; + + tuple->mPadding = image_overlay_width; + + tuple->mButton->setRightHPad(6); + tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), + tuple->mButton->getRect().getHeight()); + // add back in button width to total tab strip width + mTotalTabWidth += tuple->mButton->getRect().getWidth(); + + // tabs have changed size, might need to scroll to see current tab + updateMaxScrollPos(); + } + } +} + void LLTabContainer::setTitle(const std::string& title) { if (mTitleBox) diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 5d0f194bf9..33c49e0d6f 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -172,6 +172,7 @@ public: BOOL getTabPanelFlashing(LLPanel* child); void setTabPanelFlashing(LLPanel* child, BOOL state); void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white); + void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white); void setTitle( const std::string& title ); const std::string getPanelTitle(S32 index); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a4fc095727..c29a3a0035 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10070,6 +10070,18 @@ <key>Value</key> <integer>1</integer> </map> + <key>SpeakerParticipantRemoveDelay</key> + <map> + <key>Comment</key> + <string>Timeout to remove participants who is not in channel before removed from list of active speakers (text/voice chat)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>10.0</real> + </map> + <key>UseStartScreen</key> <map> <key>Comment</key> diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index f346a4b8c2..d4c8adadc6 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -93,22 +93,6 @@ static void* create_non_avatar_caller(void*) return new LLNonAvatarCaller; } -LLCallFloater::LLAvatarListItemRemoveTimer::LLAvatarListItemRemoveTimer(callback_t remove_cb, F32 period, const LLUUID& speaker_id) -: LLEventTimer(period) -, mRemoveCallback(remove_cb) -, mSpeakerId(speaker_id) -{ -} - -BOOL LLCallFloater::LLAvatarListItemRemoveTimer::tick() -{ - if (mRemoveCallback) - { - mRemoveCallback(mSpeakerId); - } - return TRUE; -} - LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL; LLCallFloater::LLCallFloater(const LLSD& key) @@ -122,10 +106,9 @@ LLCallFloater::LLCallFloater(const LLSD& key) , mSpeakingIndicator(NULL) , mIsModeratorMutedVoice(false) , mInitParticipantsVoiceState(false) -, mVoiceLeftRemoveDelay(10) { static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10); - mVoiceLeftRemoveDelay = voice_left_remove_delay; + mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay); mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL); LLVoiceClient::getInstance()->addObserver(this); @@ -135,6 +118,7 @@ LLCallFloater::LLCallFloater(const LLSD& key) LLCallFloater::~LLCallFloater() { resetVoiceRemoveTimers(); + delete mSpeakerDelayRemover; delete mParticipants; mParticipants = NULL; @@ -648,33 +632,11 @@ void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state) void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id) { - - // If there is already a started timer for the current panel don't do anything. - bool no_timer_for_current_panel = true; - if (mVoiceLeftTimersMap.size() > 0) - { - timers_map::iterator found_it = mVoiceLeftTimersMap.find(voice_speaker_id); - if (found_it != mVoiceLeftTimersMap.end()) - { - no_timer_for_current_panel = false; - } - } - - if (no_timer_for_current_panel) - { - // Starting a timer to remove an avatar row panel after timeout - mVoiceLeftTimersMap.insert(timer_pair(voice_speaker_id, - new LLAvatarListItemRemoveTimer(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), mVoiceLeftRemoveDelay, voice_speaker_id))); - } + mSpeakerDelayRemover->setActionTimer(voice_speaker_id); } -void LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id) +bool LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id) { - if (mVoiceLeftTimersMap.size() > 0) - { - mVoiceLeftTimersMap.erase(mVoiceLeftTimersMap.find(voice_speaker_id)); - } - LLAvatarList::uuid_vector_t& speaker_uuids = mAvatarList->getIDs(); LLAvatarList::uuid_vector_t::iterator pos = std::find(speaker_uuids.begin(), speaker_uuids.end(), voice_speaker_id); if(pos != speaker_uuids.end()) @@ -682,34 +644,19 @@ void LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id) speaker_uuids.erase(pos); mAvatarList->setDirty(); } + + return false; } void LLCallFloater::resetVoiceRemoveTimers() { - if (mVoiceLeftTimersMap.size() > 0) - { - for (timers_map::iterator iter = mVoiceLeftTimersMap.begin(); - iter != mVoiceLeftTimersMap.end(); ++iter) - { - delete iter->second; - } - } - mVoiceLeftTimersMap.clear(); + mSpeakerDelayRemover->removeAllTimers(); } void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id) { - // Remove the timer if it has been already started - if (mVoiceLeftTimersMap.size() > 0) - { - timers_map::iterator found_it = mVoiceLeftTimersMap.find(voice_speaker_id); - if (found_it != mVoiceLeftTimersMap.end()) - { - delete found_it->second; - mVoiceLeftTimersMap.erase(found_it); - } - } + mSpeakerDelayRemover->unsetActionTimer(voice_speaker_id); } bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id) diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 096594aaa2..eded3a426b 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -44,6 +44,8 @@ class LLNonAvatarCaller; class LLOutputMonitorCtrl; class LLParticipantList; class LLSpeakerMgr; +class LLSpeakersDelayActionsStorage; + /** * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button. * It can be torn-off and freely positioned onscreen. @@ -169,7 +171,7 @@ private: * * @param voice_speaker_id LLUUID of Avatar List item to be removed from the list. */ - void removeVoiceLeftParticipant(const LLUUID& voice_speaker_id); + bool removeVoiceLeftParticipant(const LLUUID& voice_speaker_id); /** * Deletes all timers from the list to prevent started timers from ticking after destruction @@ -240,32 +242,11 @@ private: boost::signals2::connection mAvatarListRefreshConnection; + /** - * class LLAvatarListItemRemoveTimer - * - * Implements a timer that removes avatar list item of a participant - * who has left the call. + * time out speakers when they are not part of current session */ - class LLAvatarListItemRemoveTimer : public LLEventTimer - { - public: - typedef boost::function<void(const LLUUID&)> callback_t; - - LLAvatarListItemRemoveTimer(callback_t remove_cb, F32 period, const LLUUID& speaker_id); - virtual ~LLAvatarListItemRemoveTimer() {}; - - virtual BOOL tick(); - - private: - callback_t mRemoveCallback; - LLUUID mSpeakerId; - }; - - typedef std::pair<LLUUID, LLAvatarListItemRemoveTimer*> timer_pair; - typedef std::map<LLUUID, LLAvatarListItemRemoveTimer*> timers_map; - - timers_map mVoiceLeftTimersMap; - S32 mVoiceLeftRemoveDelay; + LLSpeakersDelayActionsStorage* mSpeakerDelayRemover; /** * Stores reference to current voice channel. diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 8da207f887..f1de4e2982 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -1272,6 +1272,7 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index) chiclet->setChicletSizeChangedCallback(boost::bind(&LLChicletPanel::onChicletSizeChanged, this, _1, index)); arrange(); + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, chiclet); return true; } @@ -1299,6 +1300,7 @@ void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it) mChicletList.erase(it); arrange(); + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, chiclet); chiclet->die(); } diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index fb94657278..0e42ff09d8 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -51,6 +51,7 @@ #include "llinventorymodel.h" #include "llfloaterworldmap.h" #include "lllandmarkactions.h" +#include "llnotificationsutil.h" #include "llsidetray.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" @@ -975,6 +976,10 @@ BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) void copy_slurl_to_clipboard_cb(std::string& slurl) { gClipboard.copyFromString(utf8str_to_wstring(slurl)); + + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); } diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index e0f2fca580..2efae0c8db 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -277,13 +277,8 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) { object_owner.append("Unknown"); } - childSetText("object_name", object_owner); - std::string owner_link = - LLSLURL::buildCommand("agent", mObjectID, "inspect"); - childSetText("owner_name", owner_link); - childSetText("abuser_name_edit", object_owner); - mAbuserID = object_id; - mOwnerName = object_owner; + + setFromAvatar(object_id, object_owner); } else { @@ -305,7 +300,6 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) } } - void LLFloaterReporter::onClickSelectAbuser() { gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE )); @@ -323,6 +317,17 @@ void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, } +void LLFloaterReporter::setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name) +{ + mAbuserID = mObjectID = avatar_id; + mOwnerName = avatar_name; + + std::string avatar_link = LLSLURL::buildCommand("agent", mObjectID, "inspect"); + childSetText("owner_name", avatar_link); + childSetText("object_name", avatar_name); // name + childSetText("abuser_name_edit", avatar_name); +} + // static void LLFloaterReporter::onClickSend(void *userdata) { @@ -458,9 +463,8 @@ void LLFloaterReporter::showFromMenu(EReportType report_type) } } - // static -void LLFloaterReporter::showFromObject(const LLUUID& object_id) +void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name) { LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter"); @@ -469,8 +473,11 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id) LLAgentUI::buildFullname(fullname); f->childSetText("reporter_field", fullname); - // Request info for this object - f->getObjectInfo(object_id); + if (avatar_name.empty()) + // Request info for this object + f->getObjectInfo(object_id); + else + f->setFromAvatar(object_id, avatar_name); // Need to deselect on close f->mDeselectOnClose = TRUE; @@ -479,6 +486,18 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id) } +// static +void LLFloaterReporter::showFromObject(const LLUUID& object_id) +{ + show(object_id); +} + +// static +void LLFloaterReporter::showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name) +{ + show(avatar_id, avatar_name); +} + void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id) { childSetText("object_name", object_name); diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index cc2dfb2f98..7c6473f975 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -93,6 +93,7 @@ public: static void showFromMenu(EReportType report_type); static void showFromObject(const LLUUID& object_id); + static void showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name); static void onClickSend (void *userdata); static void onClickCancel (void *userdata); @@ -109,6 +110,8 @@ public: void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id); private: + static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null); + void takeScreenshot(); void sendReportViaCaps(std::string url); void uploadImage(); @@ -121,6 +124,7 @@ private: void enableControls(BOOL own_avatar); void getObjectInfo(const LLUUID& object_id); void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids); + void setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name = LLStringUtil::null); private: EReportType mReportType; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index a63fb73032..b833c611bf 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1391,6 +1391,7 @@ void LLFolderView::startRenamingSelectedItem( void ) // set focus will fail unless item is visible mRenamer->setFocus( TRUE ); mRenamer->setTopLostCallback(boost::bind(onRenamerLost, _1)); + mRenamer->setFocusLostCallback(boost::bind(onRenamerLost, _1)); gFocusMgr.setTopCtrl( mRenamer ); } } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index e06e0c94ec..73597e7de3 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -110,6 +110,8 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) } } setOverlapsScreenChannel(true); + + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); } void LLIMFloater::onFocusLost() @@ -228,6 +230,7 @@ void LLIMFloater::sendMsg() LLIMFloater::~LLIMFloater() { + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); } //virtual @@ -513,14 +516,14 @@ bool LLIMFloater::toggle(const LLUUID& session_id) if(!isChatMultiTab()) { LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater && floater->getVisible()) + if (floater && floater->getVisible() && floater->hasFocus()) { // clicking on chiclet to close floater just hides it to maintain existing // scroll/text entry state floater->setVisible(false); return false; } - else if(floater && !floater->isDocked()) + else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus())) { floater->setVisible(TRUE); floater->setFocus(TRUE); diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index d9db385d06..0ca0325451 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -116,6 +116,8 @@ public: static void onIMChicletCreated(const LLUUID& session_id); + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + private: // process focus events to set a currently active session /* virtual */ void onFocusLost(); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 6cc985aef4..06a7b4a29c 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -35,19 +35,22 @@ #include "llimfloatercontainer.h" #include "llfloaterreg.h" +#include "llimview.h" +#include "llavatariconctrl.h" +#include "llagent.h" // // LLIMFloaterContainer // LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -: LLMultiFloater(seed), - mActiveVoiceFloater(NULL) +: LLMultiFloater(seed) { mAutoResize = FALSE; } LLIMFloaterContainer::~LLIMFloaterContainer() { + LLGroupMgr::getInstance()->removeObserver(this); } BOOL LLIMFloaterContainer::postBuild() @@ -87,13 +90,84 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); - // make sure active voice icon shows up for new tab - if (floaterp == mActiveVoiceFloater) + LLUUID session_id = floaterp->getKey(); + + if(gAgent.isInGroup(session_id)) + { + mSessions[session_id] = floaterp; + mID = session_id; + mGroupID.push_back(session_id); + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(session_id); + LLGroupMgr* gm = LLGroupMgr::getInstance(); + gm->addObserver(this); + + if (group_data && group_data->mInsigniaID.notNull()) + { + mTabContainer->setTabImage(get_ptr_in_map(mSessions, session_id), group_data->mInsigniaID); + } + else + { + gm->sendGroupPropertiesRequest(session_id); + } + } + else + { + LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); + LLAvatarPropertiesProcessor& app = LLAvatarPropertiesProcessor::instance(); + app.addObserver(avatar_id, this); + floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, avatar_id)); + mSessions[avatar_id] = floaterp; + + LLUUID* icon_id_ptr = LLAvatarIconIDCache::getInstance()->get(avatar_id); + if(!icon_id_ptr) + { + app.sendAvatarPropertiesRequest(avatar_id); + } + else + { + mTabContainer->setTabImage(floaterp, *icon_id_ptr); + } + } +} + +void LLIMFloaterContainer::processProperties(void* data, enum EAvatarProcessorType type) +{ + if (APT_PROPERTIES == type) + { + LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data); + if (avatar_data) + { + LLUUID avatar_id = avatar_data->avatar_id; + if(avatar_data->image_id != *LLAvatarIconIDCache::getInstance()->get(avatar_id)) + { + LLAvatarIconIDCache::getInstance()->add(avatar_id,avatar_data->image_id); + } + mTabContainer->setTabImage(get_ptr_in_map(mSessions, avatar_id), avatar_data->image_id); + } + } +} + +void LLIMFloaterContainer::changed(LLGroupChange gc) +{ + if (GC_PROPERTIES == gc) { - mTabContainer->setTabImage(floaterp, "active_voice_tab.tga"); + for(groupIDs_t::iterator it = mGroupID.begin(); it!=mGroupID.end(); it++) + { + LLUUID group_id = *it; + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id); + if (group_data && group_data->mInsigniaID.notNull()) + { + mTabContainer->setTabImage(get_ptr_in_map(mSessions, group_id), group_data->mInsigniaID); + } + } } } +void LLIMFloaterContainer::onCloseFloater(LLUUID id) +{ + LLAvatarPropertiesProcessor::instance().removeObserver(id, this); +} + LLIMFloaterContainer* LLIMFloaterContainer::findInstance() { return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container"); diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index d4a542dfc2..1333b098bc 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -33,12 +33,17 @@ #ifndef LL_LLIMFLOATERCONTAINER_H #define LL_LLIMFLOATERCONTAINER_H +#include <map> +#include <vector> + #include "llfloater.h" #include "llmultifloater.h" +#include "llavatarpropertiesprocessor.h" +#include "llgroupmgr.h" class LLTabContainer; -class LLIMFloaterContainer : public LLMultiFloater +class LLIMFloaterContainer : public LLMultiFloater, public LLAvatarPropertiesObserver, public LLGroupMgrObserver { public: LLIMFloaterContainer(const LLSD& seed); @@ -51,15 +56,23 @@ public: BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + void processProperties(void* data, EAvatarProcessorType type); + void changed(LLGroupChange gc); + static LLFloater* getCurrentVoiceFloater(); static LLIMFloaterContainer* findInstance(); static LLIMFloaterContainer* getInstance(); -protected: - - LLFloater* mActiveVoiceFloater; +private: + typedef std::map<LLUUID,LLPanel*> avatarID_panel_map_t; + avatarID_panel_map_t mSessions; + + typedef std::vector<LLUUID> groupIDs_t; + groupIDs_t mGroupID; + + void onCloseFloater(LLUUID avatar_id); }; #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ff20a55358..c2a7969c0d 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -55,6 +55,7 @@ #include "llfloaterchatterbox.h" #include "llimfloater.h" #include "llgroupiconctrl.h" +#include "llmd5.h" #include "llmutelist.h" #include "llrecentpeople.h" #include "llviewermessage.h" @@ -215,12 +216,14 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID); } + buildHistoryFileName(); + if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) { std::list<LLSD> chat_history; //involves parsing of a chat history - LLLogChat::loadAllHistory(mName, chat_history); + LLLogChat::loadAllHistory(mHistoryFileName, chat_history); addMessagesFromHistory(chat_history); } } @@ -467,6 +470,44 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline() return !mOtherParticipantIsAvatar; } +void LLIMModel::LLIMSession::buildHistoryFileName() +{ + mHistoryFileName = mName; + + //ad-hoc requires sophisticated chat history saving schemes + if (isAdHoc()) + { + //in case of outgoing ad-hoc sessions + if (mInitialTargetIDs.size()) + { + std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); + mHistoryFileName = mName + " hash" + generateHash(sorted_uuids); + return; + } + + //in case of incoming ad-hoc sessions + mHistoryFileName = mName + " " + LLLogChat::timestamp(true) + " " + mSessionID.asString().substr(0, 4); + } +} + +//static +std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids) +{ + LLMD5 md5_uuid; + + std::set<LLUUID>::const_iterator it = sorted_uuids.begin(); + while (it != sorted_uuids.end()) + { + md5_uuid.update((unsigned char*)(*it).mData, 16); + it++; + } + md5_uuid.finalize(); + + LLUUID participants_md5_hash; + md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData); + return participants_md5_hash.asString(); +} + void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id) { @@ -614,11 +655,11 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, return true; } -bool LLIMModel::logToFile(const std::string& session_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) +bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) { - LLLogChat::saveHistory(session_name, from, from_id, utf8_text); + LLLogChat::saveHistory(file_name, from, from_id, utf8_text); return true; } else @@ -629,15 +670,7 @@ bool LLIMModel::logToFile(const std::string& session_name, const std::string& fr bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { - if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) - { - LLLogChat::saveHistory(LLIMModel::getInstance()->getName(session_id), from, from_id, utf8_text); - return true; - } - else - { - return false; - } + return logToFile(LLIMModel::getInstance()->getHistoryFileName(session_id), from, from_id, utf8_text); } bool LLIMModel::proccessOnlineOfflineNotification( @@ -782,6 +815,18 @@ LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const return session->mSpeakers; } +const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const +{ + LLIMSession* session = findIMSession(session_id); + if (!session) + { + llwarns << "session " << session_id << " does not exist " << llendl; + return LLStringUtil::null; + } + + return session->mHistoryFileName; +} + // TODO get rid of other participant ID void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index a226d66b12..a3b4f78af0 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -69,6 +69,8 @@ public: void addMessagesFromHistory(const std::list<LLSD>& history); void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time); void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction); + + /** @deprecated */ static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata); bool isAdHoc(); @@ -80,12 +82,20 @@ public: bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} bool isAvalineSessionType() const { return mSessionType == AVALINE_SESSION;} + //*TODO make private + /** ad-hoc sessions involve sophisticated chat history file naming schemes */ + void buildHistoryFileName(); + + //*TODO make private + static std::string generateHash(const std::set<LLUUID>& sorted_uuids); + LLUUID mSessionID; std::string mName; EInstantMessage mType; SType mSessionType; LLUUID mOtherParticipantID; std::vector<LLUUID> mInitialTargetIDs; + std::string mHistoryFileName; // connection to voice channel state change signal boost::signals2::connection mVoiceChannelStateChangeConnection; @@ -231,6 +241,8 @@ public: */ LLIMSpeakerMgr* getSpeakerManager(const LLUUID& session_id) const; + const std::string& getHistoryFileName(const LLUUID& session_id) const; + static void sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id); static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids, EInstantMessage dialog); @@ -243,7 +255,7 @@ public: /** * Saves an IM message into a file */ - bool logToFile(const std::string& session_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); + bool logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); private: diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 0374a1d25b..4b0539337b 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -689,7 +689,7 @@ void LLInspectAvatar::onToggleMute() void LLInspectAvatar::onClickReport() { - LLFloaterReporter::showFromObject(mAvatarID); + LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName); closeFloater(); } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index e9da0ea408..6c9c7d15be 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3685,18 +3685,6 @@ BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, return rv; } -BOOL LLCallingCardBridge::removeItem() -{ - if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem())) - { - LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID()); - return FALSE; - } - else - { - return LLItemBridge::removeItem(); - } -} // +=================================================+ // | LLNotecardBridge | // +=================================================+ diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index fced0047e8..759d0cba18 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -448,7 +448,6 @@ public: EDragAndDropType cargo_type, void* cargo_data); void refreshFolderViewItem(); - BOOL removeItem(); protected: LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ); diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 404e266806..7f49a7defb 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -43,8 +43,11 @@ #include "lltrans.h" #include "lluictrlfactory.h" #include "lltooltip.h" +#include "llnotificationsutil.h" +#include "llregionflags.h" // newview includes +#include "llagent.h" #include "llinventoryobserver.h" #include "lllandmarkactions.h" #include "lllandmarklist.h" @@ -56,6 +59,7 @@ #include "lltrans.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" +#include "llviewerregion.h" #include "llviewercontrol.h" #include "llviewermenu.h" #include "llurllineeditorctrl.h" @@ -256,36 +260,42 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) voice_icon.tool_tip = LLTrans::getString("LocationCtrlVoiceTooltip"); voice_icon.mouse_opaque = true; mParcelIcon[VOICE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(voice_icon); + mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, VOICE_ICON)); addChild(mParcelIcon[VOICE_ICON]); LLIconCtrl::Params fly_icon = p.fly_icon; fly_icon.tool_tip = LLTrans::getString("LocationCtrlFlyTooltip"); fly_icon.mouse_opaque = true; mParcelIcon[FLY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(fly_icon); + mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, FLY_ICON)); addChild(mParcelIcon[FLY_ICON]); LLIconCtrl::Params push_icon = p.push_icon; push_icon.tool_tip = LLTrans::getString("LocationCtrlPushTooltip"); push_icon.mouse_opaque = true; mParcelIcon[PUSH_ICON] = LLUICtrlFactory::create<LLIconCtrl>(push_icon); + mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PUSH_ICON)); addChild(mParcelIcon[PUSH_ICON]); LLIconCtrl::Params build_icon = p.build_icon; build_icon.tool_tip = LLTrans::getString("LocationCtrlBuildTooltip"); build_icon.mouse_opaque = true; mParcelIcon[BUILD_ICON] = LLUICtrlFactory::create<LLIconCtrl>(build_icon); + mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, BUILD_ICON)); addChild(mParcelIcon[BUILD_ICON]); LLIconCtrl::Params scripts_icon = p.scripts_icon; scripts_icon.tool_tip = LLTrans::getString("LocationCtrlScriptsTooltip"); scripts_icon.mouse_opaque = true; mParcelIcon[SCRIPTS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(scripts_icon); + mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SCRIPTS_ICON)); addChild(mParcelIcon[SCRIPTS_ICON]); LLIconCtrl::Params damage_icon = p.damage_icon; damage_icon.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip"); damage_icon.mouse_opaque = true; mParcelIcon[DAMAGE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(damage_icon); + mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, DAMAGE_ICON)); addChild(mParcelIcon[DAMAGE_ICON]); LLTextBox::Params damage_text = p.damage_text; @@ -918,3 +928,45 @@ bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata) return false; } + +void LLLocationInputCtrl::onParcelIconClick(EParcelIcon icon) +{ + switch (icon) + { + case VOICE_ICON: + LLNotificationsUtil::add("NoVoice"); + break; + case FLY_ICON: + LLNotificationsUtil::add("NoFly"); + break; + case PUSH_ICON: + LLNotificationsUtil::add("PushRestricted"); + break; + case BUILD_ICON: + LLNotificationsUtil::add("NoBuild"); + break; + case SCRIPTS_ICON: + { + LLViewerRegion* region = gAgent.getRegion(); + if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS) + { + LLNotificationsUtil::add("ScriptsStopped"); + } + else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS) + { + LLNotificationsUtil::add("ScriptsNotRunning"); + } + else + { + LLNotificationsUtil::add("NoOutsideScripts"); + } + break; + } + case DAMAGE_ICON: + LLNotificationsUtil::add("NotSafe"); + break; + case ICON_COUNT: + break; + // no default to get compiler warning when a new icon gets added + } +} diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 7959fab2de..607ccd4da6 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -102,6 +102,18 @@ public: void handleLoginComplete(); private: + + enum EParcelIcon + { + VOICE_ICON = 0, + FLY_ICON, + PUSH_ICON, + BUILD_ICON, + SCRIPTS_ICON, + DAMAGE_ICON, + ICON_COUNT + }; + friend class LLUICtrlFactory; LLLocationInputCtrl(const Params&); virtual ~LLLocationInputCtrl(); @@ -138,6 +150,7 @@ private: // callbacks bool onLocationContextMenuItemEnabled(const LLSD& userdata); void onLocationContextMenuItemClicked(const LLSD& userdata); + void onParcelIconClick(EParcelIcon icon); LLMenuGL* mLocationContextMenu; LLButton* mAddLandmarkBtn; @@ -146,16 +159,6 @@ private: S32 mIconHPad; // pad between all icons S32 mAddLandmarkHPad; // pad to left of landmark star - enum EParcelIcon - { - VOICE_ICON = 0, - FLY_ICON, - PUSH_ICON, - BUILD_ICON, - SCRIPTS_ICON, - DAMAGE_ICON, - ICON_COUNT - }; LLIconCtrl* mParcelIcon[ICON_COUNT]; LLTextBox* mDamageText; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 4e5aaeb66a..dc187bf36c 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -237,15 +237,15 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line) messages.back()[IM_TEXT] = im_text; } -void LLLogChat::loadAllHistory(const std::string& session_name, std::list<LLSD>& messages) +void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages) { - if (session_name.empty()) + if (file_name.empty()) { llwarns << "Session name is Empty!" << llendl; return ; } - LLFILE* fptr = LLFile::fopen(makeLogFileName(session_name), "r"); /*Flawfinder: ignore*/ + LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r"); /*Flawfinder: ignore*/ if (!fptr) return; //No previous conversation with this name. char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 3d3f5c4458..4290e4bbc0 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -56,7 +56,7 @@ public: void (*callback)(ELogLineType, const LLSD&, void*), void* userdata); - static void loadAllHistory(const std::string& session_name, std::list<LLSD>& messages); + static void loadAllHistory(const std::string& file_name, std::list<LLSD>& messages); private: static std::string cleanFileName(std::string filename); }; diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 569d3001bf..c30ef3221d 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -356,13 +356,6 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) (*it)->setGroupID(group_id); - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); - if(gdatap) - { - childSetValue("group_name", gdatap->mName); - childSetToolTip("group_name",gdatap->mName); - } - LLButton* button_apply = findChild<LLButton>("btn_apply"); LLButton* button_refresh = findChild<LLButton>("btn_refresh"); LLButton* button_create = findChild<LLButton>("btn_create"); @@ -464,6 +457,17 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) } reposButtons(); + + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); + + if(gdatap) + { + childSetValue("group_name", gdatap->mName); + childSetToolTip("group_name",gdatap->mName); + + //group data is already present, call update manually + update(GC_ALL); + } } bool LLPanelGroup::apply(LLPanelGroupTab* tab) diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 0e55ff3214..45f0381d6f 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -452,6 +452,7 @@ LLPanelGroupSubTab::LLPanelGroupSubTab() : LLPanelGroupTab(), mHeader(NULL), mFooter(NULL), + mActivated(false), mSearchEditor(NULL) { } @@ -504,13 +505,14 @@ void LLPanelGroupSubTab::setGroupID(const LLUUID& id) mSearchEditor->clear(); setSearchFilter(""); } + + mActivated = false; } void LLPanelGroupSubTab::setSearchFilter(const std::string& filter) { if(mSearchFilter == filter) return; - lldebugs << "LLPanelGroupSubTab::setSearchFilter() ==> '" << filter << "'" << llendl; mSearchFilter = filter; LLStringUtil::toLower(mSearchFilter); update(GC_ALL); @@ -518,13 +520,11 @@ void LLPanelGroupSubTab::setSearchFilter(const std::string& filter) void LLPanelGroupSubTab::activate() { - lldebugs << "LLPanelGroupSubTab::activate()" << llendl; setOthersVisible(TRUE); } void LLPanelGroupSubTab::deactivate() { - lldebugs << "LLPanelGroupSubTab::deactivate()" << llendl; setOthersVisible(FALSE); } @@ -534,19 +534,11 @@ void LLPanelGroupSubTab::setOthersVisible(BOOL b) { mHeader->setVisible( b ); } - else - { - llwarns << "LLPanelGroupSubTab missing header!" << llendl; - } if (mFooter) { mFooter->setVisible( b ); } - else - { - llwarns << "LLPanelGroupSubTab missing footer!" << llendl; - } } bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action) @@ -875,10 +867,12 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() for (itor = selection.begin(); itor != selection.end(); ++itor) { - selected_members.push_back( (*itor)->getUUID() ); + LLUUID member_id = (*itor)->getValue()["uuid"]; + + selected_members.push_back( member_id ); // Get this member's power mask including any unsaved changes - U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID()); + U64 powers = getAgentPowersBasedOnRoleChanges( member_id ); allowed_by_all &= powers; allowed_by_some |= powers; @@ -1022,6 +1016,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() //last owner. We should check for this special case // -jwolk check->setEnabled(cb_enable); + item->setEnabled(cb_enable); } } else @@ -1098,7 +1093,8 @@ void LLPanelGroupMembersSubTab::handleEjectMembers() for (itor = selection.begin() ; itor != selection.end(); ++itor) { - selected_members.push_back((*itor)->getUUID()); + LLUUID member_id = (*itor)->getValue()["uuid"]; + selected_members.push_back( member_id ); } mMembersList->deleteSelectedItems(); @@ -1154,7 +1150,8 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id, for (std::vector<LLScrollListItem*>::iterator itor = selection.begin() ; itor != selection.end(); ++itor) { - member_id = (*itor)->getUUID(); + + member_id = (*itor)->getValue()["uuid"]; //see if we requested a change for this member before if ( mMemberRoleChangeData.find(member_id) == mMemberRoleChangeData.end() ) @@ -1245,15 +1242,19 @@ void LLPanelGroupMembersSubTab::handleMemberDoubleClick() LLScrollListItem* selected = mMembersList->getFirstSelected(); if (selected) { - LLAvatarActions::showProfile(selected->getUUID()); + LLUUID member_id = selected->getValue()["uuid"]; + LLAvatarActions::showProfile( member_id ); } } void LLPanelGroupMembersSubTab::activate() { LLPanelGroupSubTab::activate(); - - update(GC_ALL); + if(!mActivated) + { + update(GC_ALL); + mActivated = true; + } } void LLPanelGroupMembersSubTab::deactivate() @@ -1629,7 +1630,9 @@ void LLPanelGroupMembersSubTab::updateMembers() row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus(); row["columns"][2]["font"] = "SANSSERIF_SMALL"; - mMembersList->addElement(row);//, ADD_SORTED); + LLScrollListItem* member = mMembersList->addElement(row);//, ADD_SORTED); + + LLUUID id = member->getValue()["uuid"]; mHasMatch = TRUE; } } diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 2f81900e60..eac22a6338 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -149,6 +149,8 @@ protected: icon_map_t mActionIcons; + bool mActivated; + void setOthersVisible(BOOL b); }; diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index b1cdb4d81f..a334eb9d68 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -245,7 +245,6 @@ void LLPanelIMControlPanel::nameUpdatedCallback(const LLUUID& id, const std::str LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): mParticipantList(NULL) { - mSpeakerManager = LLIMModel::getInstance()->getSpeakerManager(session_id); } BOOL LLPanelGroupControlPanel::postBuild() @@ -264,9 +263,6 @@ LLPanelGroupControlPanel::~LLPanelGroupControlPanel() // virtual 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(); @@ -307,7 +303,10 @@ void LLPanelGroupControlPanel::setSessionId(const LLUUID& 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"), true,false); + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true,false); + } } diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index c18be5a6df..25fdf944c9 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -37,8 +37,6 @@ #include "llvoicechannel.h" #include "llcallingcard.h" -class LLSpeakerMgr; -class LLAvatarList; class LLParticipantList; class LLPanelChatControlPanel : public LLPanel @@ -110,7 +108,6 @@ public: protected: LLUUID mGroupID; - LLSpeakerMgr* mSpeakerManager; LLParticipantList* mParticipantList; diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index b80eb9db38..0c10f11bfc 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -34,26 +34,20 @@ #include "llpanelplaceinfo.h" -#include "roles_constants.h" #include "llsdutil.h" -#include "llsecondlifeurls.h" #include "llsdutil_math.h" + #include "llregionhandle.h" -#include "message.h" #include "lliconctrl.h" #include "lltextbox.h" #include "llagent.h" -#include "llavatarpropertiesprocessor.h" #include "llexpandabletextbox.h" #include "llpanelpick.h" #include "lltexturectrl.h" -#include "llviewerinventory.h" -#include "llviewerparcelmgr.h" #include "llviewerregion.h" -#include "llviewertexteditor.h" LLPanelPlaceInfo::LLPanelPlaceInfo() : LLPanel(), @@ -265,25 +259,6 @@ void LLPanelPlaceInfo::reshape(S32 width, S32 height, BOOL called_from_parent) } } -// virtual -void LLPanelPlaceInfo::handleVisibilityChange(BOOL new_visibility) -{ - LLPanel::handleVisibilityChange(new_visibility); - - LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); - if (!parcel_mgr) - return; - - // Remove land selection when panel hides. - if (!new_visibility) - { - if (!parcel_mgr->selectionEmpty()) - { - parcel_mgr->deselectLand(); - } - } -} - void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel) { std::string region_name = mRegionName->getText(); diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 7dfc7b2444..3091f7ed24 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -94,7 +94,6 @@ public: /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void handleVisibilityChange (BOOL new_visibility); // Create a pick for the location specified // by global_pos. diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index 402d50ba9c..d892e2885b 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -257,6 +257,25 @@ void LLPanelPlaceProfile::processParcelInfo(const LLParcelData& parcel_data) } } +// virtual +void LLPanelPlaceProfile::handleVisibilityChange(BOOL new_visibility) +{ + LLPanel::handleVisibilityChange(new_visibility); + + LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); + if (!parcel_mgr) + return; + + // Remove land selection when panel hides. + if (!new_visibility) + { + if (!parcel_mgr->selectionEmpty()) + { + parcel_mgr->deselectUnused(); + } + } +} + void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, LLViewerRegion* region, const LLVector3d& pos_global, diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h index 8c30ca92fb..8ca9526875 100644 --- a/indra/newview/llpanelplaceprofile.h +++ b/indra/newview/llpanelplaceprofile.h @@ -52,6 +52,8 @@ public: /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); + /*virtual*/ void handleVisibilityChange(BOOL new_visibility); + // Displays information about the currently selected parcel // without sending a request to the server. // If is_current_parcel true shows "You Are Here" banner. diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index b037674c37..a4f0e55a93 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -289,89 +289,92 @@ BOOL LLPanelPlaces::postBuild() void LLPanelPlaces::onOpen(const LLSD& key) { - if(!mPlaceProfile || !mLandmarkInfo || key.size() == 0) + if (!mPlaceProfile || !mLandmarkInfo) return; - mFilterEditor->clear(); - onFilterEdit("", false); - - mPlaceInfoType = key["type"].asString(); - mPosGlobal.setZero(); - mItem = NULL; - isLandmarkEditModeOn = false; - togglePlaceInfoPanel(TRUE); - - if (mPlaceInfoType == AGENT_INFO_TYPE) - { - mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT); - } - else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE) + if (key.size() != 0) { - mLandmarkInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK); + mFilterEditor->clear(); + onFilterEdit("", false); - if (key.has("x") && key.has("y") && key.has("z")) + mPlaceInfoType = key["type"].asString(); + mPosGlobal.setZero(); + mItem = NULL; + isLandmarkEditModeOn = false; + togglePlaceInfoPanel(TRUE); + + if (mPlaceInfoType == AGENT_INFO_TYPE) { - mPosGlobal = LLVector3d(key["x"].asReal(), - key["y"].asReal(), - key["z"].asReal()); + mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT); } - else + else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE) { - mPosGlobal = gAgent.getPositionGlobal(); - } - - mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal); + mLandmarkInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK); - // Disable Save button because there is no item to save yet. - // The button will be enabled in onLandmarkLoaded callback. - mSaveBtn->setEnabled(FALSE); - } - else if (mPlaceInfoType == LANDMARK_INFO_TYPE) - { - mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK); + if (key.has("x") && key.has("y") && key.has("z")) + { + mPosGlobal = LLVector3d(key["x"].asReal(), + key["y"].asReal(), + key["z"].asReal()); + } + else + { + mPosGlobal = gAgent.getPositionGlobal(); + } - LLInventoryItem* item = gInventory.getItem(key["id"].asUUID()); - if (!item) - return; + mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal); - setItem(item); - } - else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) - { - if (key.has("id")) + // Disable Save button because there is no item to save yet. + // The button will be enabled in onLandmarkLoaded callback. + mSaveBtn->setEnabled(FALSE); + } + else if (mPlaceInfoType == LANDMARK_INFO_TYPE) { - LLUUID parcel_id = key["id"].asUUID(); - mPlaceProfile->setParcelID(parcel_id); + mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK); + + LLInventoryItem* item = gInventory.getItem(key["id"].asUUID()); + if (!item) + return; - // query the server to get the global 3D position of this - // parcel - we need this for teleport/mapping functions. - mRemoteParcelObserver->setParcelID(parcel_id); + setItem(item); } - else + else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) { - mPosGlobal = LLVector3d(key["x"].asReal(), - key["y"].asReal(), - key["z"].asReal()); - mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); + if (key.has("id")) + { + LLUUID parcel_id = key["id"].asUUID(); + mPlaceProfile->setParcelID(parcel_id); + + // query the server to get the global 3D position of this + // parcel - we need this for teleport/mapping functions. + mRemoteParcelObserver->setParcelID(parcel_id); + } + else + { + mPosGlobal = LLVector3d(key["x"].asReal(), + key["y"].asReal(), + key["z"].asReal()); + mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); + } + + mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE); } + else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) + { + S32 index = key["id"].asInteger(); - mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE); - } - else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) - { - S32 index = key["id"].asInteger(); + const LLTeleportHistoryStorage::slurl_list_t& hist_items = + LLTeleportHistoryStorage::getInstance()->getItems(); - const LLTeleportHistoryStorage::slurl_list_t& hist_items = - LLTeleportHistoryStorage::getInstance()->getItems(); + mPosGlobal = hist_items[index].mGlobalPos; - mPosGlobal = hist_items[index].mGlobalPos; + mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY); + mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); + } - mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY); - mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); + updateVerbs(); } - updateVerbs(); - LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); if (!parcel_mgr) return; @@ -388,9 +391,12 @@ void LLPanelPlaces::onOpen(const LLSD& key) { parcel_mgr->removeObserver(mParcelObserver); + // Clear the reference to selection to allow its removal in deselectUnused(). + mParcel.clear(); + if (!parcel_mgr->selectionEmpty()) { - parcel_mgr->deselectLand(); + parcel_mgr->deselectUnused(); } } } @@ -765,23 +771,23 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) mPickPanel->reshape(rect.getWidth(), rect.getHeight()); mPickPanel->setRect(rect); } - else if (item == "add_to_favbar") - { - if ( mItem.notNull() ) - { - const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - if ( favorites_id.notNull() ) - { - copy_inventory_item(gAgent.getID(), - mItem->getPermissions().getOwner(), - mItem->getUUID(), - favorites_id, - std::string(), - LLPointer<LLInventoryCallback>(NULL)); - llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl; - } - } - } + else if (item == "add_to_favbar") + { + if ( mItem.notNull() ) + { + const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + if ( favorites_id.notNull() ) + { + copy_inventory_item(gAgent.getID(), + mItem->getPermissions().getOwner(), + mItem->getUUID(), + favorites_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl; + } + } + } } void LLPanelPlaces::onBackButtonClicked() @@ -826,6 +832,14 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) mLandmarkInfo->setVisible(FALSE); } + else if (mPlaceInfoType == AGENT_INFO_TYPE) + { + LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver); + + // Clear reference to parcel selection when closing place profile panel. + // LLViewerParcelMgr removes the selection if it has 1 reference to it. + mParcel.clear(); + } } else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE || mPlaceInfoType == LANDMARK_INFO_TYPE) @@ -858,6 +872,20 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) } } +// virtual +void LLPanelPlaces::handleVisibilityChange(BOOL new_visibility) +{ + LLPanel::handleVisibilityChange(new_visibility); + + if (!new_visibility && mPlaceInfoType == AGENT_INFO_TYPE) + { + LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver); + + // Clear reference to parcel selection when closing places panel. + mParcel.clear(); + } +} + void LLPanelPlaces::changedParcelSelection() { if (!mPlaceProfile) diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 27b5911ebb..0eba7f3afc 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -97,6 +97,8 @@ private: void togglePickPanel(BOOL visible); void togglePlaceInfoPanel(BOOL visible); + /*virtual*/ void handleVisibilityChange(BOOL new_visibility); + void updateVerbs(); LLPanelPlaceInfo* getCurrentInfoPanel(); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index b049f914ad..88b706fb6b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -125,6 +125,8 @@ LLParticipantList::~LLParticipantList() delete mParticipantListMenu; mParticipantListMenu = NULL; } + + mAvatarList->setContextMenu(NULL); } void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) @@ -431,6 +433,10 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() LLContextMenu* main_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( "menu_participant_list.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); + // Don't show sort options for P2P chat + bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); + main_menu->setItemVisible("SortByName", is_sort_visible); + main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible); main_menu->setItemVisible("Moderator Options", isGroupModerator()); main_menu->arrangeAndClear(); @@ -456,11 +462,6 @@ 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) diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 0dd9203c6d..9608cd1263 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -44,7 +44,6 @@ #include "llvoavatar.h" #include "llworld.h" -const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); @@ -73,8 +72,6 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy } gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); - - mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); } @@ -164,6 +161,89 @@ bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPo return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); } +LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id) +: LLEventTimer(action_period) +, mActionCallback(action_cb) +, mSpeakerId(speaker_id) +{ +} + +BOOL LLSpeakerActionTimer::tick() +{ + if (mActionCallback) + { + return (BOOL)mActionCallback(mSpeakerId); + } + return TRUE; +} + +LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) +: mActionCallback(action_cb) +, mActionDelay(action_delay) +{ +} + +LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage() +{ + removeAllTimers(); +} + +void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id) +{ + bool not_found = true; + if (mActionTimersMap.size() > 0) + { + not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end(); + } + + // If there is already a started timer for the passed UUID don't do anything. + if (not_found) + { + // Starting a timer to remove an participant after delay is completed + mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id, + new LLSpeakerActionTimer( + boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1), + mActionDelay, speaker_id))); + } +} + +void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id) +{ + if (mActionTimersMap.size() == 0) return; + + LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id); + + if (it_speaker != mActionTimersMap.end()) + { + delete it_speaker->second; + mActionTimersMap.erase(it_speaker); + } +} + +void LLSpeakersDelayActionsStorage::removeAllTimers() +{ + LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin(); + for (; iter != mActionTimersMap.end(); ++iter) + { + delete iter->second; + } + mActionTimersMap.clear(); +} + +bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id) +{ + unsetActionTimer(speaker_id); + + if (mActionCallback) + { + mActionCallback(speaker_id); + } + + // do not return true to avoid deleting of an timer twice: + // in LLSpeakersDelayActionsStorage::unsetActionTimer() & LLEventTimer::updateClass() + return false; +} + // // LLSpeakerMgr @@ -172,10 +252,14 @@ bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPo LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : mVoiceChannel(channelp) { + static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0); + + mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); } LLSpeakerMgr::~LLSpeakerMgr() { + delete mSpeakerDelayRemover; } LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) @@ -198,7 +282,6 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin { // keep highest priority status (lowest value) instead of overriding current value speakerp->mStatus = llmin(speakerp->mStatus, status); - speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id // we need to override speakers that we think are objects when we find out they are really // residents @@ -210,6 +293,8 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin } } + mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); + return speakerp; } @@ -314,7 +399,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) S32 sort_index = 0; speaker_list_t::iterator sorted_speaker_it; for(sorted_speaker_it = mSpeakersSorted.begin(); - sorted_speaker_it != mSpeakersSorted.end(); ) + sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) { LLPointer<LLSpeaker> speakerp = *sorted_speaker_it; @@ -327,19 +412,6 @@ void LLSpeakerMgr::update(BOOL resort_ok) // stuff sort ordinal into speaker so the ui can sort by this value speakerp->mSortIndex = sort_index++; - - // remove speakers that have been gone too long - if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) - { - fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove"); - - mSpeakers.erase(speakerp->mID); - sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); - } - else - { - ++sorted_speaker_it; - } } } @@ -363,6 +435,35 @@ void LLSpeakerMgr::updateSpeakerList() } } +void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) +{ + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + mSpeakerDelayRemover->setActionTimer(speakerp->mID); +} + +bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id) +{ + mSpeakers.erase(speaker_id); + + speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); + + for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) + { + if (speaker_id == (*sorted_speaker_it)->mID) + { + mSpeakersSorted.erase(sorted_speaker_it); + break; + } + } + + fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove"); + + update(TRUE); + + return false; +} + LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) { //In some conditions map causes crash if it is empty(Windows only), adding check (EK) @@ -511,9 +612,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) { if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + setSpeakerNotInChannel(speakerp); } else if (agent_data["transition"].asString() == "ENTER") { @@ -563,9 +662,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) std::string agent_transition = update_it->second.asString(); if (agent_transition == "LEAVE" && speakerp.notNull()) { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + setSpeakerNotInChannel(speakerp); } else if ( agent_transition == "ENTER") { @@ -734,12 +831,13 @@ void LLActiveSpeakerMgr::updateSpeakerList() mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); // always populate from active voice channel - if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) + if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false { fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); mSpeakers.clear(); mSpeakersSorted.clear(); mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + mSpeakerDelayRemover->removeAllTimers(); } LLSpeakerMgr::updateSpeakerList(); @@ -800,9 +898,7 @@ void LLLocalSpeakerMgr::updateSpeakerList() LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS) { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + setSpeakerNotInChannel(speakerp); } } } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index da8dfdf548..63237204c8 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -73,7 +73,6 @@ public: F32 mLastSpokeTime; // timestamp when this speaker last spoke F32 mSpeechVolume; // current speech amplitude (timea average rms amplitude?) std::string mDisplayName; // cache user name for this speaker - LLFrameTimer mActivityTimer; // time out speakers when they are not part of current voice channel BOOL mHasSpoken; // has this speaker said anything this session? BOOL mHasLeftCurrentCall; // has this speaker left the current voice call? LLColor4 mDotColor; @@ -120,6 +119,92 @@ private: const LLUUID& mSpeakerID; }; +/** + * class LLSpeakerActionTimer + * + * Implements a timer that calls stored callback action for stored speaker after passed period. + * + * Action is called until callback returns "true". + * In this case the timer will be removed via LLEventTimer::updateClass(). + * Otherwise it should be deleted manually in place where it is used. + * If action callback is not set timer will tick only once and deleted. + */ +class LLSpeakerActionTimer : public LLEventTimer +{ +public: + typedef boost::function<bool(const LLUUID&)> action_callback_t; + typedef std::map<LLUUID, LLSpeakerActionTimer*> action_timers_map_t; + typedef action_timers_map_t::value_type action_value_t; + typedef action_timers_map_t::const_iterator action_timer_const_iter_t; + typedef action_timers_map_t::iterator action_timer_iter_t; + + /** + * Constructor. + * + * @param action_cb - callback which will be called each time after passed action period. + * @param action_period - time in seconds timer should tick. + * @param speaker_id - LLUUID of speaker which will be passed into action callback. + */ + LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id); + virtual ~LLSpeakerActionTimer() {}; + + /** + * Implements timer "tick". + * + * If action callback is not specified returns true. Instance will be deleted by LLEventTimer::updateClass(). + */ + virtual BOOL tick(); + +private: + action_callback_t mActionCallback; + LLUUID mSpeakerId; +}; + +/** + * Represents a functionality to store actions for speakers with delay. + * Is based on LLSpeakerActionTimer. + */ +class LLSpeakersDelayActionsStorage +{ +public: + LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay); + ~LLSpeakersDelayActionsStorage(); + + /** + * Sets new LLSpeakerActionTimer with passed speaker UUID. + */ + void setActionTimer(const LLUUID& speaker_id); + + /** + * Removes stored LLSpeakerActionTimer for passed speaker UUID from internal map and deletes it. + * + * @see onTimerActionCallback() + */ + void unsetActionTimer(const LLUUID& speaker_id); + + void removeAllTimers(); +private: + /** + * Callback of the each instance of LLSpeakerActionTimer. + * + * Unsets an appropriate timer instance and calls action callback for specified speacker_id. + * It always returns false to not use LLEventTimer::updateClass functionality of timer deleting. + * + * @see unsetActionTimer() + */ + bool onTimerActionCallback(const LLUUID& speaker_id); + + LLSpeakerActionTimer::action_timers_map_t mActionTimersMap; + LLSpeakerActionTimer::action_callback_t mActionCallback; + + /** + * Delay to call action callback for speakers after timer was set. + */ + F32 mActionDelay; + +}; + + class LLSpeakerMgr : public LLOldEvents::LLObservable { public: @@ -144,6 +229,8 @@ public: protected: virtual void updateSpeakerList(); + void setSpeakerNotInChannel(LLSpeaker* speackerp); + bool removeSpeaker(const LLUUID& speaker_id); typedef std::map<LLUUID, LLPointer<LLSpeaker> > speaker_map_t; speaker_map_t mSpeakers; @@ -151,6 +238,11 @@ protected: speaker_list_t mSpeakersSorted; LLFrameTimer mSpeechTimer; LLVoiceChannel* mVoiceChannel; + + /** + * time out speakers when they are not part of current session + */ + LLSpeakersDelayActionsStorage* mSpeakerDelayRemover; }; class LLIMSpeakerMgr : public LLSpeakerMgr diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 9c4825763b..2b846d33fc 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1103,7 +1103,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op) { if (op == TEXTURE_CANCEL) mViewModel->resetDirty(); - else + // If the "no_commit_on_selection" parameter is set + // we get dirty only when user presses OK in the picker + // (i.e. op == TEXTURE_SELECT) or texture changes via DnD. + else if (mCommitOnSelection || op == TEXTURE_SELECT) mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? if( floaterp->isDirty() ) @@ -1125,7 +1128,7 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op) { // If the "no_commit_on_selection" parameter is set // we commit only when user presses OK in the picker - // (i.e. op == TEXTURE_SELECT) or changes texture via DnD. + // (i.e. op == TEXTURE_SELECT) or texture changes via DnD. if (mCommitOnSelection || op == TEXTURE_SELECT) onCommit(); } @@ -1165,6 +1168,9 @@ BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, { if(doDrop(item)) { + if (!mCommitOnSelection) + mViewModel->setDirty(); + // This removes the 'Multiple' overlay, since // there is now only one texture selected. setTentative( FALSE ); diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 023329a9b2..8ca92c3d87 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -74,7 +74,8 @@ public: Optional<std::string> default_image_name; Optional<bool> allow_no_texture; Optional<bool> can_apply_immediately; - Optional<bool> no_commit_on_selection; // don't commit unless it's DnD or OK button press + Optional<bool> no_commit_on_selection; // alternative mode: commit occurs and the widget gets dirty + // only on DnD or when OK is pressed in the picker Optional<S32> label_width; Optional<LLUIColor> border_color; diff --git a/indra/newview/lltransientdockablefloater.cpp b/indra/newview/lltransientdockablefloater.cpp index 7e4d4988d1..c9bfe178ce 100644 --- a/indra/newview/lltransientdockablefloater.cpp +++ b/indra/newview/lltransientdockablefloater.cpp @@ -42,6 +42,7 @@ LLTransientDockableFloater::LLTransientDockableFloater(LLDockControl* dockContro LLDockableFloater(dockControl, uniqueDocking, key, params) { LLTransientFloaterMgr::getInstance()->registerTransientFloater(this); + LLTransientFloater::init(this); } LLTransientDockableFloater::~LLTransientDockableFloater() diff --git a/indra/newview/lltransientdockablefloater.h b/indra/newview/lltransientdockablefloater.h index 6e8a3afd22..e0541d6597 100644 --- a/indra/newview/lltransientdockablefloater.h +++ b/indra/newview/lltransientdockablefloater.h @@ -37,12 +37,13 @@ #include "llfloater.h" #include "lldockcontrol.h" #include "lldockablefloater.h" +#include "lltransientfloatermgr.h" /** * Represents floater that can dock and managed by transient floater manager. * Transient floaters should be hidden if user click anywhere except defined view list. */ -class LLTransientDockableFloater : public LLDockableFloater +class LLTransientDockableFloater : public LLDockableFloater, LLTransientFloater { public: LOG_CLASS(LLTransientDockableFloater); @@ -52,6 +53,7 @@ public: /*virtual*/ void setVisible(BOOL visible); /* virtual */void setDocked(bool docked, bool pop_on_undock = true); + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } }; #endif /* LL_TRANSIENTDOCKABLEFLOATER_H */ diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 347399f239..8f1a738453 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -44,57 +44,68 @@ LLTransientFloaterMgr::LLTransientFloaterMgr() { gViewerWindow->getRootView()->addMouseDownCallback(boost::bind( &LLTransientFloaterMgr::leftMouseClickCallback, this, _1, _2, _3)); + + mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(GLOBAL, std::set<LLView*>())); + mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(IM, std::set<LLView*>())); } -void LLTransientFloaterMgr::registerTransientFloater(LLFloater* floater) +void LLTransientFloaterMgr::registerTransientFloater(LLTransientFloater* floater) { mTransSet.insert(floater); } -void LLTransientFloaterMgr::unregisterTransientFloater(LLFloater* floater) +void LLTransientFloaterMgr::unregisterTransientFloater(LLTransientFloater* floater) { mTransSet.erase(floater); } +void LLTransientFloaterMgr::addControlView(ETransientGroup group, LLView* view) +{ + mGroupControls.find(group)->second.insert(view); +} + +void LLTransientFloaterMgr::removeControlView(ETransientGroup group, LLView* view) +{ + mGroupControls.find(group)->second.erase(view); +} + void LLTransientFloaterMgr::addControlView(LLView* view) { - mControlsSet.insert(view); + addControlView(GLOBAL, view); } void LLTransientFloaterMgr::removeControlView(LLView* view) { // we will still get focus lost callbacks on this view, but that's ok // since we run sanity checking logic every time - mControlsSet.erase(view); + removeControlView(GLOBAL, view); } -void LLTransientFloaterMgr::hideTransientFloaters() +void LLTransientFloaterMgr::hideTransientFloaters(S32 x, S32 y) { - for (std::set<LLFloater*>::iterator it = mTransSet.begin(); it + for (std::set<LLTransientFloater*>::iterator it = mTransSet.begin(); it != mTransSet.end(); it++) { - LLFloater* floater = *it; - if (floater->isDocked()) + LLTransientFloater* floater = *it; + if (floater->isTransientDocked()) { - floater->setVisible(FALSE); + ETransientGroup group = floater->getGroup(); + + bool hide = isControlClicked(mGroupControls.find(group)->second, x, y); + if (hide) + { + floater->setTransientVisible(FALSE); + } } } } -void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, - MASK mask) +bool LLTransientFloaterMgr::isControlClicked(std::set<LLView*>& set, S32 x, S32 y) { - bool hide = true; - for (controls_set_t::iterator it = mControlsSet.begin(); it - != mControlsSet.end(); it++) + bool res = true; + for (controls_set_t::iterator it = set.begin(); it + != set.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()) { @@ -105,14 +116,32 @@ void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, // if click inside view rect if (rect.pointInRect(x, y)) { - hide = false; + res = false; break; } } + return res; +} + +void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, + MASK mask) +{ + // don't hide transient floater if any context menu opened + if (LLMenuGL::sMenuContainer->getVisibleMenu() != NULL) + { + return; + } + bool hide = isControlClicked(mGroupControls.find(GLOBAL)->second, x, y); if (hide) { - hideTransientFloaters(); + hideTransientFloaters(x, y); } } +void LLTransientFloater::init(LLFloater* thiz) +{ + // used since LLTransientFloater(this) can't be used in descendant constructor parameter initialization. + mFloater = thiz; +} + diff --git a/indra/newview/lltransientfloatermgr.h b/indra/newview/lltransientfloatermgr.h index cef6e1fe45..1f99325a7f 100644 --- a/indra/newview/lltransientfloatermgr.h +++ b/indra/newview/lltransientfloatermgr.h @@ -37,27 +37,60 @@ #include "llsingleton.h" #include "llfloater.h" +class LLTransientFloater; /** * Provides functionality to hide transient floaters. */ class LLTransientFloaterMgr: public LLSingleton<LLTransientFloaterMgr> { -public: +protected: LLTransientFloaterMgr(); - void registerTransientFloater(LLFloater* floater); - void unregisterTransientFloater(LLFloater* floater); + friend class LLSingleton<LLTransientFloaterMgr>; + +public: + enum ETransientGroup + { + GLOBAL, IM + }; + + void registerTransientFloater(LLTransientFloater* floater); + void unregisterTransientFloater(LLTransientFloater* floater); + void addControlView(ETransientGroup group, LLView* view); + void removeControlView(ETransientGroup group, LLView* view); void addControlView(LLView* view); void removeControlView(LLView* view); private: - void hideTransientFloaters(); + void hideTransientFloaters(S32 x, S32 y); void leftMouseClickCallback(S32 x, S32 y, MASK mask); - + bool isControlClicked(std::set<LLView*>& set, S32 x, S32 y); private: - std::set<LLFloater*> mTransSet; + std::set<LLTransientFloater*> mTransSet; + typedef std::set<LLView*> controls_set_t; - controls_set_t mControlsSet; + typedef std::map<ETransientGroup, std::set<LLView*> > group_controls_t; + group_controls_t mGroupControls; +}; + +/** + * An abstract class declares transient floater interfaces. + */ +class LLTransientFloater +{ +protected: + /** + * Class initialization method. + * Should be called from descendant constructor. + */ + void init(LLFloater* thiz); +public: + virtual LLTransientFloaterMgr::ETransientGroup getGroup() = 0; + bool isTransientDocked() { return mFloater->isDocked(); }; + void setTransientVisible(BOOL visible) {mFloater->setVisible(visible); } + +private: + LLFloater* mFloater; }; #endif // LL_LLTRANSIENTFLOATERMGR_H diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7487fa9997..0358efc0af 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1472,7 +1472,12 @@ void inventory_offer_handler(LLOfferInfo* info) { LLStringUtil::truncate(msg, indx); } - + + if(LLAssetType::AT_LANDMARK == info->mType) + { + msg = LLViewerInventoryItem::getDisplayName(msg); + } + LLSD args; args["[OBJECTNAME]"] = msg; diff --git a/indra/newview/skins/default/xui/en/favorites_bar_button.xml b/indra/newview/skins/default/xui/en/favorites_bar_button.xml index 6adf2a5950..b365040c20 100644 --- a/indra/newview/skins/default/xui/en/favorites_bar_button.xml +++ b/indra/newview/skins/default/xui/en/favorites_bar_button.xml @@ -10,6 +10,8 @@ image_selected="transparent.j2c" image_unselected="transparent.j2c" image_pressed="Favorite_Link_Over" + image_hover_selected="Favorite_Link_Over" + image_hover_unselected="Favorite_Link_Over" hover_glow_amount="0.15" label_shadow="false" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index 1d51d19a4a..bd25288a9e 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <multi_floater - can_minimize="false" + can_close="false" + can_minimize="true" can_resize="true" height="390" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index d3db9fd531..09ec2137b7 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -416,7 +416,6 @@ image_pressed_selected "Lit" + "Selected" - there are new messages and the Well name="Unread" image_overlay="Notices_Unread" image_overlay_alignment="center" - pad_right="15" tool_tip="Notifications" width="34" > <button.init_callback diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml index 673052c3b5..0893c204e7 100644 --- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml @@ -67,6 +67,7 @@ background_visible="true" layout="topleft" left="20" name="insignia" + no_commit_on_selection="true" tool_tip="Click to choose a picture" top_pad="5" width="100" /> diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 6b3fb04549..618d2f3b8e 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -49,6 +49,18 @@ Select multiple Members by holding the Ctrl key and clicking on their names. </panel.string> + <panel.string + name="power_folder_icon"> + Inv_FolderClosed + </panel.string> + <panel.string + name="power_all_have_icon"> + Checkbox_On + </panel.string> + <panel.string + name="power_partial_icon"> + Checkbox_Off + </panel.string> <filter_editor layout="topleft" top="5" @@ -310,7 +322,7 @@ things in this group. There's a broad variety of Abilities. width="285"> Allowed Abilities </text> - <scroll_list + <scroll_list draw_stripes="true" height="90" layout="topleft" @@ -322,8 +334,16 @@ things in this group. There's a broad variety of Abilities. width="300"> <scroll_list.columns label="" + name="icon" + width="2" /> + <scroll_list.columns + label="" + name="checkbox" + width="20" /> + <scroll_list.columns + label="" name="action" - width="300" /> + width="270" /> </scroll_list> </panel> <panel diff --git a/indra/newview/skins/default/xui/en/panel_my_profile.xml b/indra/newview/skins/default/xui/en/panel_my_profile.xml index 34cde61252..4894ae01da 100644 --- a/indra/newview/skins/default/xui/en/panel_my_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_my_profile.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel follows="all" - height="535" + height="500" label="Profile" layout="topleft" left="0" @@ -41,7 +41,7 @@ layout="topleft" left="0" top="0" - height="535" + height="480" width="313" border_size="0"> <layout_panel @@ -50,7 +50,7 @@ layout="topleft" top="0" left="0" - height="505" + height="480" user_resize="false" width="313"> <scroll_container @@ -60,7 +60,7 @@ left="0" name="profile_scroll" opaque="true" - height="505" + height="480" width="313" top="0"> <panel @@ -374,8 +374,9 @@ follows="bottom|right" height="23" left="20" - top="0" + top="5" label="Edit Profile" + layout="topleft" name="edit_profile_btn" tool_tip="Edit your personal information" width="130" /> @@ -384,7 +385,9 @@ height="23" label="Edit Appearance" left_pad="10" + layout="topleft" name="edit_appearance_btn" + top="5" tool_tip="Create/edit your appearance: physical data, clothes and etc." width="130" /> </layout_panel> |