diff options
Diffstat (limited to 'indra/newview')
63 files changed, 1554 insertions, 627 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 344079b640..515199604b 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3489,16 +3489,27 @@ <key>Value</key> <real>10.0</real> </map> - <key>FilterItemsPerFrame</key> + <key>FilterItemsMaxTimePerFrameVisible</key> <map> - <key>Comment</key> - <string>Maximum number of inventory items to match against search filter every frame (lower to increase framerate while searching, higher to improve search speed)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>500</integer> + <key>Comment</key> + <string>Max time devoted to items filtering per frame for visible inventory listings (in milliseconds)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>10</integer> + </map> + <key>FilterItemsMaxTimePerFrameUnvisible</key> + <map> + <key>Comment</key> + <string>Max time devoted to items filtering per frame for non visible inventory listings (in milliseconds)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>1</integer> </map> <key>FindLandArea</key> <map> @@ -6430,7 +6441,10 @@ <key>NotificationConferenceIMOptions</key> <map> <key>Comment</key> - <string>Specifies how the UI responds to Conference IM Notifications.</string> + <string> + Specifies how the UI responds to Conference IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -6441,7 +6455,10 @@ <key>NotificationFriendIMOptions</key> <map> <key>Comment</key> - <string>Specifies how the UI responds to Friend IM Notifications.</string> + <string> + Specifies how the UI responds to Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -6452,7 +6469,10 @@ <key>NotificationGroupChatOptions</key> <map> <key>Comment</key> - <string>Specifies how the UI responds to Group Chat Notifications.</string> + <string> + Specifies how the UI responds to Group Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -6463,7 +6483,10 @@ <key>NotificationNearbyChatOptions</key> <map> <key>Comment</key> - <string>Specifies how the UI responds to Nearby Chat Notifications.</string> + <string> + Specifies how the UI responds to Nearby Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -6474,7 +6497,24 @@ <key>NotificationNonFriendIMOptions</key> <map> <key>Comment</key> - <string>Specifies how the UI responds to Non Friend IM Notifications.</string> + <string> + Specifies how the UI responds to Non Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>toast</string> + </map> + <key>NotificationObjectIMOptions</key> + <map> + <key>Comment</key> + <string> + Specifies how the UI responds to Object IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -7020,6 +7060,72 @@ <key>Value</key> <integer>0</integer> </map> + <key>PlaySoundFriendIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when friend's IM received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PlaySoundNonFriendIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when non-friend's IM received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PlaySoundConferenceIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when conference IM received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PlaySoundGroupChatIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when group chat IM received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PlaySoundNearbyChatIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when nearby chat IM received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PlaySoundObjectIM</key> + <map> + <key>Comment</key> + <string>Plays a sound when IM fom an object received.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>PlaySoundNewConversation</key> <map> <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 3e94c5edf7..f960b31cc7 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -46,6 +46,7 @@ #include "llenvmanager.h" #include "llfirstuse.h" #include "llfloatercamera.h" +#include "llfloaterimcontainer.h" #include "llfloaterreg.h" #include "llfloatertools.h" #include "llgroupactions.h" @@ -91,6 +92,7 @@ #include "llworld.h" #include "llworldmap.h" #include "stringize.h" +#include "boost/foreach.hpp" using namespace LLAvatarAppearanceDefines; @@ -2037,7 +2039,16 @@ void LLAgent::endAnimationUpdateUI() { skip_list.insert(LLFloaterReg::findInstance("mini_map")); } - + + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + LLFloaterIMContainer::floater_list_t conversations; + im_box->getDetachedConversationFloaters(conversations); + BOOST_FOREACH(LLFloater* conversation, conversations) + { + llinfos << "skip_list.insert(session_floater): " << conversation->getTitle() << llendl; + skip_list.insert(conversation); + } + gFloaterView->popVisibleAll(skip_list); #endif mViewsPushed = FALSE; diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 43757d0174..8b2d9e639f 100755 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -139,8 +139,6 @@ void LLChannelManager::onLoginCompleted() } LLPersistentNotificationStorage::getInstance()->loadNotifications(); - - LLDoNotDisturbNotificationStorage::getInstance()->initialize(); LLDoNotDisturbNotificationStorage::getInstance()->loadNotifications(); } diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 0f138873ac..af3c6eff11 100755 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -628,6 +628,7 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) editor_params.enabled = false; // read only editor_params.show_context_menu = "true"; mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this); + mEditor->setIsFriendCallback(LLAvatarActions::isFriend); } LLSD LLChatHistory::getValue() const diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index a1a9463d43..fd4f17b694 100755 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -323,12 +323,11 @@ BOOL LLFloaterIMNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->showHistory(); + LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")->showHistory(); return FALSE; } } - - (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->showHistory(); + LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index c74ce24872..192a594c9d 100755 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -363,7 +363,10 @@ void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) lldebugs << "LLConversationItemParticipant::buildContextMenu()" << llendl; menuentry_vec_t items; menuentry_vec_t disabled_items; - + if((flags & ITEM_IN_MULTI_SELECTION) && (this->getType() != CONV_SESSION_NEARBY)) + { + items.push_back(std::string("close_selected_conversations")); + } if(this->getType() == CONV_SESSION_1_ON_1) { items.push_back(std::string("close_conversation")); @@ -386,6 +389,10 @@ void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) addVoiceOptions(items); items.push_back(std::string("chat_history")); } + else if(this->getType() == CONV_SESSION_NEARBY) + { + items.push_back(std::string("chat_history")); + } hide_context_entries(menu, items, disabled_items); } diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 8766585049..d8cdcdfc97 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -252,11 +252,10 @@ public: const std::string& getName() const { return mEmpty; } const std::string& getFilterText() { return mEmpty; } void setModified(EFilterModified behavior = FILTER_RESTART) { } - - void setFilterCount(S32 count) { } - S32 getFilterCount() const { return 0; } - void decrementFilterCount() { } - + + void resetTime(S32 timeout) { } + bool isTimedOut() { return false; } + bool isDefault() const { return true; } bool isNotDefault() const { return false; } void markDefault() { } diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index b6c53e5e30..9faa12b2ee 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -118,9 +118,19 @@ void LLConversationViewSession::setFlashState(bool flash_state) mFlashTimer->stopFlashing(); } +void LLConversationViewSession::setHighlightState(bool hihglight_state) +{ + mFlashStateOn = hihglight_state; + mFlashStarted = true; + mFlashTimer->stopFlashing(); +} + void LLConversationViewSession::startFlashing() { - if (isInVisibleChain() && mFlashStateOn && !mFlashStarted) + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + + // Need to start flashing only when "Conversations" is opened or brought on top + if (isInVisibleChain() && !im_box->isMinimized() && mFlashStateOn && !mFlashStarted) { mFlashStarted = true; mFlashTimer->startFlashing(); @@ -263,6 +273,29 @@ BOOL LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask ) return result; } +BOOL LLConversationViewSession::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + BOOL result = LLFolderViewFolder::handleMouseUp(x, y, mask); + + LLFloater* volume_floater = LLFloaterReg::findInstance("floater_voice_volume"); + LLFloater* chat_volume_floater = LLFloaterReg::findInstance("chat_voice"); + if (result + && getRoot() + && !(volume_floater && volume_floater->isShown() && volume_floater->hasFocus()) + && !(chat_volume_floater && chat_volume_floater->isShown() && chat_volume_floater->hasFocus())) + { + LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem()); + LLUUID session_id = item? item->getUUID() : LLUUID(); + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); + if(!session_floater->hasFocus()) + { + session_floater->setFocus(true); + } + } + + return result; +} + BOOL LLConversationViewSession::handleRightMouseDown( S32 x, S32 y, MASK mask ) { BOOL result = LLFolderViewFolder::handleRightMouseDown(x, y, mask); @@ -340,16 +373,20 @@ void LLConversationViewSession::setVisibleIfDetached(BOOL visible) { // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here - LLFolderViewModelItem* item = mViewModelItem; - LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID(); - LLFloater* session_floater = LLFloaterIMSessionTab::getConversation(session_uuid); - - if (session_floater && !session_floater->getHost() && !session_floater->isMinimized()) + LLFloater* session_floater = getSessionFloater(); + if (session_floater && session_floater->isDetachedAndNotMinimized()) { session_floater->setVisible(visible); } } +LLFloater* LLConversationViewSession::getSessionFloater() +{ + LLFolderViewModelItem* item = mViewModelItem; + LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID(); + return LLFloaterIMSessionTab::getConversation(session_uuid); +} + LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id) { // This is *not* a general tree parsing algorithm. We search only in the mItems list diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 3eb2e63792..5a74974302 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -69,6 +69,7 @@ public: /*virtual*/ void draw(); /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask ); /*virtual*/ S32 arrange(S32* width, S32* height); @@ -86,6 +87,9 @@ public: virtual void refresh(); /*virtual*/ void setFlashState(bool flash_state); + void setHighlightState(bool hihglight_state); + + LLFloater* getSessionFloater(); private: diff --git a/indra/newview/lldonotdisturbnotificationstorage.cpp b/indra/newview/lldonotdisturbnotificationstorage.cpp index 82affcf068..495cd01349 100755 --- a/indra/newview/lldonotdisturbnotificationstorage.cpp +++ b/indra/newview/lldonotdisturbnotificationstorage.cpp @@ -70,7 +70,7 @@ BOOL LLDoNotDisturbNotificationStorageTimer::tick() LLDoNotDisturbNotificationStorage::LLDoNotDisturbNotificationStorage() : LLSingleton<LLDoNotDisturbNotificationStorage>() - , LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml")) + , LLNotificationStorage("") , mDirty(false) { nameToPayloadParameterMap[toastName] = "SESSION_ID"; @@ -83,6 +83,7 @@ LLDoNotDisturbNotificationStorage::~LLDoNotDisturbNotificationStorage() void LLDoNotDisturbNotificationStorage::initialize() { + setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml")); getCommunicationChannel()->connectFailedFilter(boost::bind(&LLDoNotDisturbNotificationStorage::onChannelChanged, this, _1)); } @@ -115,7 +116,8 @@ void LLDoNotDisturbNotificationStorage::saveNotifications() { LLNotificationPtr notificationPtr = historyIter->second; - if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired()) + if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && + !notificationPtr->isExpired() && !notificationPtr->isPersistent()) { data.append(notificationPtr->asLLSD(true)); } @@ -210,12 +212,8 @@ void LLDoNotDisturbNotificationStorage::loadNotifications() } - if(imToastExists) - { - LLFloaterReg::showInstance("im_container"); - } - - if(group_ad_hoc_toast_exists) + bool isConversationLoggingAllowed = gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; + if(group_ad_hoc_toast_exists && isConversationLoggingAllowed) { LLFloaterReg::showInstance("conversation"); } @@ -266,11 +264,6 @@ void LLDoNotDisturbNotificationStorage::updateNotifications() } } - if(imToastExists) - { - LLFloaterReg::showInstance("im_container"); - } - if(imToastExists || offerExists) { make_ui_sound("UISndNewIncomingIMSession"); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 6d90667194..b35ef3a961 100755 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -400,6 +400,8 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) mMoreTextBox->setClickedCallback(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); addChild(mMoreTextBox); + mDropDownItemsCount = 0; + LLTextBox::Params label_param(p.label); mBarLabel = LLUICtrlFactory::create<LLTextBox> (label_param); addChild(mBarLabel); @@ -820,11 +822,13 @@ void LLFavoritesBarCtrl::updateButtons() } // Update overflow menu LLToggleableMenu* overflow_menu = static_cast <LLToggleableMenu*> (mOverflowMenuHandle.get()); - if (overflow_menu && overflow_menu->getVisible()) + if (overflow_menu && overflow_menu->getVisible() && (overflow_menu->getItemCount() != mDropDownItemsCount)) { overflow_menu->setVisible(FALSE); if (mUpdateDropDownItems) + { showDropDownMenu(); + } } } else @@ -940,6 +944,7 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu->updateParent(LLMenuGL::sMenuContainer); menu->setButtonRect(mMoreTextBox->getRect(), this); positionAndShowMenu(menu); + mDropDownItemsCount = menu->getItemCount(); } } diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index f06e9b9b64..211d3c4ce3 100755 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -101,6 +101,7 @@ protected: LLUUID mFavoriteFolderId; const LLFontGL *mFont; S32 mFirstDropDownItem; + S32 mDropDownItemsCount; bool mUpdateDropDownItems; bool mRestoreOverflowMenu; diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 113aa9a8f2..c0afb72cff 100755 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -513,6 +513,7 @@ void LLFloaterAvatarPicker::find() url += "/"; } url += "?page_size=100&names="; + std::replace(text.begin(), text.end(), '.', ' '); url += LLURI::escape(text); llinfos << "avatar picker " << url << llendl; LLHTTPClient::get(url, new LLAvatarPickerResponder(mQueryID, getKey().asString())); @@ -748,7 +749,12 @@ void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD& { getChildView("ok_btn")->setEnabled(true); search_results->setEnabled(true); - search_results->selectFirstItem(); + search_results->sortByColumnIndex(1, TRUE); + std::string text = getChild<LLUICtrl>("Edit")->getValue().asString(); + if (!search_results->selectItemByLabel(text, TRUE, 1)) + { + search_results->selectFirstItem(); + } onList(); search_results->setFocus(TRUE); } diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp index a3d715530d..1a3fa27593 100755 --- a/indra/newview/llfloaterconversationpreview.cpp +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -50,6 +50,7 @@ LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_i BOOL LLFloaterConversationPreview::postBuild() { mChatHistory = getChild<LLChatHistory>("chat_history"); + LLLoadHistoryThread::setLoadEndSignal(boost::bind(&LLFloaterConversationPreview::SetPages, this, _1, _2)); const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID); std::string name; @@ -70,7 +71,7 @@ BOOL LLFloaterConversationPreview::postBuild() name = LLTrans::getString("NearbyChatTitle"); file = "chat"; } - + mChatHistoryFileName = file; LLStringUtil::format_map_t args; args["[NAME]"] = name; std::string title = getString("Title", args); @@ -80,23 +81,46 @@ BOOL LLFloaterConversationPreview::postBuild() load_params["load_all_history"] = true; load_params["cut_off_todays_date"] = false; - LLLogChat::loadChatHistory(file, mMessages, load_params); - mCurrentPage = mMessages.size() / mPageSize; + LLSD loading; + loading[LL_IM_TEXT] = LLTrans::getString("loading_chat_logs"); + mMessages.push_back(loading); mPageSpinner = getChild<LLSpinCtrl>("history_page_spin"); mPageSpinner->setCommitCallback(boost::bind(&LLFloaterConversationPreview::onMoreHistoryBtnClick, this)); mPageSpinner->setMinValue(1); - mPageSpinner->setMaxValue(mCurrentPage + 1); - mPageSpinner->set(mCurrentPage + 1); - - std::string total_page_num = llformat("/ %d", mCurrentPage + 1); - getChild<LLTextBox>("page_num_label")->setValue(total_page_num); - + mPageSpinner->set(1); + mPageSpinner->setEnabled(false); + mChatHistoryLoaded = false; + LLLogChat::startChatHistoryThread(file, load_params); return LLFloater::postBuild(); } +void LLFloaterConversationPreview::SetPages(std::list<LLSD>& messages, const std::string& file_name) +{ + if(file_name == mChatHistoryFileName) + { + mMessages = messages; + + + mCurrentPage = mMessages.size() / mPageSize; + mPageSpinner->setEnabled(true); + mPageSpinner->setMaxValue(mCurrentPage+1); + mPageSpinner->set(mCurrentPage+1); + + std::string total_page_num = llformat("/ %d", mCurrentPage+1); + getChild<LLTextBox>("page_num_label")->setValue(total_page_num); + mChatHistoryLoaded = true; + + } + +} void LLFloaterConversationPreview::draw() { + if(mChatHistoryLoaded) + { + showHistory(); + mChatHistoryLoaded = false; + } LLFloater::draw(); } diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h index b17ae84b63..389f3dfd09 100755 --- a/indra/newview/llfloaterconversationpreview.h +++ b/indra/newview/llfloaterconversationpreview.h @@ -42,6 +42,7 @@ public: virtual ~LLFloaterConversationPreview(){}; virtual BOOL postBuild(); + void SetPages(std::list<LLSD>& messages,const std::string& file_name); virtual void draw(); virtual void onOpen(const LLSD& key); @@ -59,6 +60,8 @@ private: std::list<LLSD> mMessages; std::string mAccountName; std::string mCompleteName; + std::string mChatHistoryFileName; + bool mChatHistoryLoaded; }; #endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */ diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index b40789db9c..4591b80ac4 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -47,6 +47,7 @@ #include "llfloaterpreference.h" #include "llimview.h" #include "llnotificationsutil.h" +#include "lltoolbarview.h" #include "lltransientfloatermgr.h" #include "llviewercontrol.h" #include "llconversationview.h" @@ -54,6 +55,7 @@ #include "llworld.h" #include "llsdserialize.h" #include "llviewerobjectlist.h" +#include "boost/foreach.hpp" // // LLFloaterIMContainer @@ -63,7 +65,8 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param mExpandCollapseBtn(NULL), mConversationsRoot(NULL), mConversationsEventStream("ConversationsEvents"), - mInitialized(false) + mInitialized(false), + mIsFirstLaunch(true) { mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2)); mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2)); @@ -113,6 +116,10 @@ void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::str void LLFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) { + if(!isInVisibleChain()) + { + setVisibleAndFrontmost(false); + } selectConversationPair(session_id, true); collapseMessagesPane(false); } @@ -204,6 +211,7 @@ BOOL LLFloaterIMContainer::postBuild() // a scroller for folder view LLRect scroller_view_rect = mConversationsListPanel->getRect(); scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + scroller_view_rect.mBottom += getChild<LLLayoutStack>("conversations_pane_buttons_stack")->getRect().getHeight(); LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); scroller_params.rect(scroller_view_rect); @@ -221,7 +229,8 @@ BOOL LLFloaterIMContainer::postBuild() mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this)); mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn"); mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this)); - getChild<LLButton>("speak_btn")->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this)); + mSpeakBtn = getChild<LLButton>("speak_btn"); + mSpeakBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this)); childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this)); @@ -258,7 +267,6 @@ BOOL LLFloaterIMContainer::postBuild() void LLFloaterIMContainer::onOpen(const LLSD& key) { LLMultiFloater::onOpen(key); - openNearbyChat(); reSelectConversation(); assignResizeLimits(); } @@ -593,6 +601,7 @@ void LLFloaterIMContainer::setMinimized(BOOL b) //Switching from minimized to un-minimized if(was_minimized && !b) { + gToolBarView->flashCommand(LLCommandId("chat"), false); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); if(session_floater && !session_floater->isTornOff()) @@ -621,7 +630,6 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLFloaterReg::toggleInstanceOrBringToFront(name); selectConversationPair(LLUUID(NULL), false, false); } - openNearbyChat(); flashConversationItemWidget(mSelectedSession,false); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); @@ -651,7 +659,11 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); if (widget) { - widget->setVisibleIfDetached(visible); + LLFloater* session_floater = widget->getSessionFloater(); + if (session_floater != nearby_chat) + { + widget->setVisibleIfDetached(visible); + } } } @@ -659,10 +671,37 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLMultiFloater::setVisible(visible); } +void LLFloaterIMContainer::getDetachedConversationFloaters(floater_list_t& floaters) +{ + typedef conversations_widgets_map::value_type conv_pair; + BOOST_FOREACH(conv_pair item, mConversationsWidgets) + { + LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(item.second); + if (widget) + { + LLFloater* session_floater = widget->getSessionFloater(); + if (session_floater && session_floater->isDetachedAndNotMinimized()) + { + floaters.push_back(session_floater); + } + } + } +} + void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key) { LLMultiFloater::setVisibleAndFrontmost(take_focus, key); - selectConversationPair(getSelectedSession(), false, take_focus); + // Do not select "Nearby Chat" conversation, since it will bring its window to front + // Only select other sessions + if (!getSelectedSession().isNull()) + { + selectConversationPair(getSelectedSession(), false, take_focus); + } + if (mInitialized && mIsFirstLaunch) + { + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + mIsFirstLaunch = false; + } } void LLFloaterIMContainer::updateResizeLimits() @@ -779,13 +818,6 @@ void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(bool collapse, S32 d setCanMinimize(at_least_one_panel_is_expanded); assignResizeLimits(); - - // force set correct size for the title after show/hide minimize button - LLRect cur_rect = getRect(); - LLRect force_rect = cur_rect; - force_rect.mRight = cur_rect.mRight + 1; - setRect(force_rect); - setRect(cur_rect); } void LLFloaterIMContainer::assignResizeLimits() @@ -793,15 +825,12 @@ void LLFloaterIMContainer::assignResizeLimits() bool is_conv_pane_expanded = !mConversationsPane->isCollapsed(); bool is_msg_pane_expanded = !mMessagesPane->isCollapsed(); - // With two panels visible number of borders is three, because the borders - // between the panels are merged into one - S32 number_of_visible_borders = llmin((is_conv_pane_expanded? 2 : 0) + (is_msg_pane_expanded? 2 : 0), 3); - S32 summary_width_of_visible_borders = number_of_visible_borders * LLPANEL_BORDER_WIDTH; - S32 conv_pane_target_width = is_conv_pane_expanded? - (is_msg_pane_expanded? - mConversationsPane->getRect().getWidth() - : mConversationsPane->getExpandedMinDim()) - : mConversationsPane->getMinDim(); + S32 summary_width_of_visible_borders = (is_msg_pane_expanded ? mConversationsStack->getPanelSpacing() : 0) + 1; + + S32 conv_pane_target_width = is_conv_pane_expanded + ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() ) + : mConversationsPane->getMinDim(); + S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0; S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders; @@ -961,11 +990,11 @@ void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order) conversation_floater->setSortOrder(order); } } - + gSavedSettings.setU32("ConversationSortOrder", (U32)order); } -void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) +void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids, bool participant_uuids/* = true*/) { const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList(); @@ -978,7 +1007,7 @@ void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem()); //When a one-on-one conversation exists, retrieve the participant id from the conversation floater - if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) + if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1 && participant_uuids) { LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(conversationItem->getUUID()); LLUUID participant_id = conversation_floaterp->getOtherParticipantUUID(); @@ -1133,6 +1162,11 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, { LLFloater::onClickClose(conversationFloater); } + else if("close_selected_conversations" == command) + { + getSelectedUUIDs(selectedIDS,false); + closeSelectedConversations(selectedIDS); + } else if("open_voice_conversation" == command) { gIMMgr->startCall(conversationItem->getUUID()); @@ -1143,7 +1177,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, } else if("chat_history" == command) { - if (selectedIDS.size() > 0) + if (selectedIDS.size() > 0) { LLAvatarActions::viewChatHistory(selectedIDS.front()); } @@ -1156,6 +1190,17 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, } } } + //if there is no LLFloaterIMSession* instance for selected conversation it might be Nearby chat + else + { + if(conversationItem->getType() == LLConversationItem::CONV_SESSION_NEARBY) + { + if("chat_history" == command) + { + LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); + } + } + } } void LLFloaterIMContainer::doToSelected(const LLSD& userdata) @@ -1185,7 +1230,7 @@ void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata) if (action == "group_profile") { - LLGroupActions::show(mSelectedSession); + LLGroupActions::show(mSelectedSession); } else if (action == "activate_group") { @@ -1211,7 +1256,19 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata) //Enable Chat history item for ad-hoc and group conversations if ("can_chat_history" == item && uuids.size() > 0) { - return LLLogChat::isTranscriptExist(uuids.front()); + //Disable menu item if selected participant is user agent + if(uuids.front() != gAgentID) + { + if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_NEARBY) + { + return LLLogChat::isNearbyTranscriptExist(); + } + else + { + bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP); + return LLLogChat::isTranscriptExist(uuids.front(),is_group); + } + } } // If nothing is selected(and selected item is not group chat), everything needs to be disabled @@ -1886,13 +1943,6 @@ void LLFloaterIMContainer::openNearbyChat() } } -void LLFloaterIMContainer::onNearbyChatClosed() -{ - // If nearby chat is the only remaining conversation and it is closed, close whole conversation floater as well - if (mConversationsItems.size() == 1) - closeFloater(); -} - void LLFloaterIMContainer::reSelectConversation() { LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); @@ -1904,7 +1954,6 @@ void LLFloaterIMContainer::reSelectConversation() void LLFloaterIMContainer::updateSpeakBtnState() { - LLButton* mSpeakBtn = getChild<LLButton>("speak_btn"); mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState()); mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak")); } @@ -1925,6 +1974,17 @@ void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, } } +void LLFloaterIMContainer::highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted) +{ + //Finds the conversation line item to highlight using the session_id + LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id)); + + if (widget) + { + widget->setHighlightState(is_highlighted); + } +} + bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget) { llassert(conversation_item_widget != NULL); @@ -1940,23 +2000,28 @@ bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conve BOOL LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask ) { + BOOL handled = FALSE; + if(mask == MASK_ALT) { if (KEY_RETURN == key ) { expandConversation(); + handled = TRUE; } if ((KEY_DOWN == key ) || (KEY_RIGHT == key)) { selectNextorPreviousConversation(true); + handled = TRUE; } if ((KEY_UP == key) || (KEY_LEFT == key)) { selectNextorPreviousConversation(false); + handled = TRUE; } } - return TRUE; + return handled; } bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected) @@ -2013,7 +2078,22 @@ void LLFloaterIMContainer::expandConversation() } } -void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) +// By default, if torn off session is currently frontmost, LLFloater::isFrontmost() will return FALSE, which can lead to some bugs +// So LLFloater::isFrontmost() is overriden here to check both selected session and the IM floater itself +// Exclude "Nearby Chat" session from the check, as "Nearby Chat" window and "Conversations" floater can be brought +// to front independently +/*virtual*/ +BOOL LLFloaterIMContainer::isFrontmost() +{ + LLFloaterIMSessionTab* selected_session = LLFloaterIMSessionTab::getConversation(mSelectedSession); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); + return (selected_session && selected_session->isFrontmost() && (selected_session != nearby_chat)) + || LLFloater::isFrontmost(); +} + +// For conversations, closeFloater() (linked to Ctrl-W) does not actually close the floater but the active conversation. +// This is intentional so it doesn't confuse the user. onClickCloseBtn() closes the whole floater. +void LLFloaterIMContainer::onClickCloseBtn() { // Always unminimize before trying to close. // Most of the time the user will never see this state. @@ -2022,7 +2102,80 @@ void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) LLMultiFloater::setMinimized(FALSE); } - LLFloater::closeFloater(app_quitting); + LLFloater::closeFloater(); +} + +void LLFloaterIMContainer::closeHostedFloater() +{ + onClickCloseBtn(); +} + +void LLFloaterIMContainer::closeAllConversations() +{ + LLDynamicArray<LLUUID> ids; + for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + { + LLUUID session_id = it_session->first; + if (session_id != LLUUID()) + { + ids.push_back(session_id); + } + } + + for (LLDynamicArray<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it); + LLFloater::onClickClose(conversationFloater); + } +} + +void LLFloaterIMContainer::closeSelectedConversations(const uuid_vec_t& ids) +{ + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + //We don't need to close Nearby chat, so skip it + if (*it != LLUUID()) + { + LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it); + if(conversationFloater) + { + LLFloater::onClickClose(conversationFloater); + } + } + } +} +void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) +{ + if(app_quitting) + { + closeAllConversations(); + onClickCloseBtn(); + } + else + { + // Check for currently active session + LLUUID session_id = getSelectedSession(); + // If current session is Nearby Chat or there is only one session remaining, close the floater + if (mConversationsItems.size() == 1 || session_id == LLUUID() || app_quitting) + { + onClickCloseBtn(); + } + else + { + // Otherwise, close current conversation + LLFloaterIMSessionTab* active_conversation = LLFloaterIMSessionTab::getConversation(session_id); + if (active_conversation) + { + active_conversation->closeFloater(); + } + } + } +} + +void LLFloaterIMContainer::handleReshape(const LLRect& rect, bool by_user) +{ + LLMultiFloater::handleReshape(rect, by_user); + storeRectControl(); } // EOF diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h index e39d20ec35..36da457cac 100755 --- a/indra/newview/llfloaterimcontainer.h +++ b/indra/newview/llfloaterimcontainer.h @@ -63,6 +63,8 @@ public: /*virtual*/ void setVisible(BOOL visible); /*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); /*virtual*/ void updateResizeLimits(); + /*virtual*/ void handleReshape(const LLRect& rect, bool by_user); + void onCloseFloater(LLUUID& id); /*virtual*/ void addFloater(LLFloater* floaterp, @@ -105,8 +107,6 @@ public: LLConversationItem* getSessionModel(const LLUUID& session_id) { return get_ptr_in_map(mConversationsItems,session_id); } LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); } - void onNearbyChatClosed(); - // Handling of lists of participants is public so to be common with llfloatersessiontab // *TODO : Find a better place for this. bool checkContextMenuItem(const std::string& item, uuid_vec_t& selectedIDS); @@ -116,6 +116,10 @@ public: void assignResizeLimits(); virtual BOOL handleKeyHere(KEY key, MASK mask ); /*virtual*/ void closeFloater(bool app_quitting = false); + void closeAllConversations(); + void closeSelectedConversations(const uuid_vec_t& ids); + /*virtual*/ BOOL isFrontmost(); + private: typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t; @@ -130,6 +134,8 @@ private: void onStubCollapseButtonClicked(); void processParticipantsStyleUpdate(); void onSpeakButtonClicked(); + /*virtual*/ void onClickCloseBtn(); + /*virtual*/ void closeHostedFloater(); void collapseConversationsPane(bool collapse, bool save_is_allowed=true); @@ -144,7 +150,7 @@ private: void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order); void setSortOrder(const LLConversationSort& order); - void getSelectedUUIDs(uuid_vec_t& selected_uuids); + void getSelectedUUIDs(uuid_vec_t& selected_uuids, bool participant_uuids = true); const LLConversationItem * getCurSelectedViewModelItem(); void getParticipantUUIDs(uuid_vec_t& selected_uuids); void doToSelected(const LLSD& userdata); @@ -169,6 +175,7 @@ private: LLButton* mExpandCollapseBtn; LLButton* mStubCollapseBtn; + LLButton* mSpeakBtn; LLPanel* mStubPanel; LLTextBox* mStubTextBox; LLLayoutPanel* mMessagesPane; @@ -176,6 +183,7 @@ private: LLLayoutStack* mConversationsStack; bool mInitialized; + bool mIsFirstLaunch; LLUUID mSelectedSession; std::string mGeneralTitle; @@ -190,9 +198,12 @@ public: void updateSpeakBtnState(); static bool isConversationLoggingAllowed(); void flashConversationItemWidget(const LLUUID& session_id, bool is_flashes); + void highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted); bool isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget); boost::signals2::connection mMicroChangedSignal; S32 getConversationListItemSize() { return mConversationsWidgets.size(); } + typedef std::list<LLFloater*> floater_list_t; + void getDetachedConversationFloaters(floater_list_t& floaters); private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 56b0c15cb9..5999c74d51 100755 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -138,19 +138,28 @@ BOOL LLFloaterIMNearbyChat::postBuild() // virtual void LLFloaterIMNearbyChat::closeHostedFloater() { - // Should check how many conversations are ongoing. Close all if 1 only (the Nearby Chat), select next one otherwise + // If detached from conversations window close anyway + if (!getHost()) + { + setVisible(FALSE); + } + + // Should check how many conversations are ongoing. Select next to "Nearby Chat" in case there are some other besides. + // Close conversations window in case "Nearby Chat" is attached and the only conversation LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance(); if (floater_container->getConversationListItemSize() == 1) { - floater_container->closeFloater(); + if (getHost()) + { + floater_container->closeFloater(); + } } else { if (!getHost()) { - setVisible(FALSE); + floater_container->selectNextConversationByID(LLUUID()); } - floater_container->selectNextConversationByID(LLUUID()); } } @@ -262,7 +271,7 @@ void LLFloaterIMNearbyChat::setVisibleAndFrontmost(BOOL take_focus, const LLSD& { LLFloaterIMSessionTab::setVisibleAndFrontmost(take_focus, key); - if(!isTornOff() && matchesKey(key)) + if(matchesKey(key)) { LLFloaterIMContainer::getInstance()->selectConversationPair(mSessionID, true, take_focus); } @@ -296,7 +305,6 @@ void LLFloaterIMNearbyChat::onClose(bool app_quitting) { // Override LLFloaterIMSessionTab::onClose() so that Nearby Chat is not removed from the conversation floater LLFloaterIMSessionTab::restoreFloater(); - onClickCloseBtn(); } // virtual @@ -306,13 +314,7 @@ void LLFloaterIMNearbyChat::onClickCloseBtn() { return; } - LLFloaterIMSessionTab::onTearOffClicked(); - - LLFloaterIMContainer *im_box = LLFloaterIMContainer::findInstance(); - if (im_box) - { - im_box->onNearbyChatClosed(); - } + closeHostedFloater(); } void LLFloaterIMNearbyChat::onChatFontChange(LLFontGL* fontp) @@ -350,11 +352,17 @@ bool LLFloaterIMNearbyChat::isChatVisible() const void LLFloaterIMNearbyChat::showHistory() { openFloater(); + LLFloaterIMContainer::getInstance()->selectConversation(LLUUID(NULL)); + if(!isMessagePaneExpanded()) { restoreFloater(); setFocus(true); } + else + { + LLFloaterIMContainer::getInstance()->setFocus(TRUE); + } setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); } @@ -568,7 +576,10 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) if (0 == channel) { // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text)) + { + utf8_revised_text = utf8text; + } } else { @@ -636,10 +647,7 @@ void LLFloaterIMNearbyChat::addMessage(const LLChat& chat,bool archive,const LLS void LLFloaterIMNearbyChat::onChatBoxCommit() { - if (mInputEditor->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } + sendChat(CHAT_TYPE_NORMAL); gAgent.stopTyping(); } diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp index 9ce5e12897..cc00b6fd10 100755 --- a/indra/newview/llfloaterimnearbychathandler.cpp +++ b/indra/newview/llfloaterimnearbychathandler.cpp @@ -606,6 +606,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg, //Don't show nearby toast, if conversation is visible and selected if ((nearby_chat->hasFocus()) || + (LLFloater::isVisible(nearby_chat) && nearby_chat->isTornOff() && !nearby_chat->isMinimized()) || ((im_box->getSelectedSession().isNull() && ((LLFloater::isVisible(im_box) && !im_box->isMinimized() && im_box->isFrontmost()) || (LLFloater::isVisible(nearby_chat) && !nearby_chat->isMinimized() && nearby_chat->isFrontmost()))))) diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 8ec85e1160..5cb9df5625 100755 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -109,18 +109,6 @@ void LLFloaterIMSession::refresh() void LLFloaterIMSession::onTearOffClicked() { LLFloaterIMSessionTab::onTearOffClicked(); - - if(mIsP2PChat) - { - if(isTornOff()) - { - mSpeakingIndicator->setSpeakerId(mOtherParticipantUUID, mSessionID); - } - else - { - mSpeakingIndicator->setSpeakerId(LLUUID::null); - } - } } // virtual @@ -442,8 +430,11 @@ void LLFloaterIMSession::addSessionParticipants(const uuid_vec_t& uuids) } else { - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + if(findInstance(mSessionID)) + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + } inviteToSession(uuids); } @@ -469,13 +460,18 @@ void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, con temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); // then we can close the current session - onClose(false); + if(findInstance(mSessionID)) + { + onClose(false); + + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + } // we start a new session so reset the initialization flag mSessionInitialized = false; - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + // Start a new ad hoc voice call if we invite new participants to a P2P call, // or start a text chat otherwise. diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index ce6e639305..0ccfdb9a7b 100755 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -55,7 +55,6 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) , mSessionID(session_id.asUUID()) , mConversationsRoot(NULL) , mScroller(NULL) - , mSpeakingIndicator(NULL) , mChatHistory(NULL) , mInputEditor(NULL) , mInputEditorPad(0) @@ -212,7 +211,7 @@ void LLFloaterIMSessionTab::assignResizeLimits() mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed); S32 participants_pane_target_width = is_participants_pane_collapsed? - 0 : (mParticipantListPanel->getRect().getWidth() + LLPANEL_BORDER_WIDTH); + 0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth; @@ -241,7 +240,10 @@ BOOL LLFloaterIMSessionTab::postBuild() mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); mGearBtn = getChild<LLButton>("gear_btn"); - + mAddBtn = getChild<LLButton>("add_btn"); + mVoiceButton = getChild<LLButton>("voice_call_btn"); + mTranslationCheckBox = getChild<LLUICtrl>("translate_chat_checkbox_lp"); + mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder"); @@ -256,8 +258,6 @@ BOOL LLFloaterIMSessionTab::postBuild() scroller_params.rect(scroller_view_rect); mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); mScroller->setFollowsAll(); - - mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); // Insert that scroller into the panel widgets hierarchy mParticipantListPanel->addChild(mScroller); @@ -270,6 +270,7 @@ BOOL LLFloaterIMSessionTab::postBuild() mInputPanels = getChild<LLLayoutStack>("input_panels"); mInputEditor->setTextExpandedCallback(boost::bind(&LLFloaterIMSessionTab::reshapeChatLayoutPanel, this)); + mInputEditor->setMouseUpCallback(boost::bind(&LLFloaterIMSessionTab::onInputEditorClicked, this)); mInputEditor->setCommitOnFocusLost( FALSE ); mInputEditor->setPassDelete(TRUE); mInputEditor->setFont(LLViewerChat::getChatFont()); @@ -372,7 +373,7 @@ void LLFloaterIMSessionTab::draw() void LLFloaterIMSessionTab::enableDisableCallBtn() { - getChildView("voice_call_btn")->setEnabled( + mVoiceButton->setEnabled( mSessionID.notNull() && mSession && mSession->mSessionInitialized @@ -399,6 +400,16 @@ void LLFloaterIMSessionTab::onFocusLost() LLTransientDockableFloater::onFocusLost(); } +void LLFloaterIMSessionTab::onInputEditorClicked() +{ + LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance(); + if (im_box) + { + im_box->flashConversationItemWidget(mSessionID,false); + } + gToolBarView->flashCommand(LLCommandId("chat"), false); +} + std::string LLFloaterIMSessionTab::appendTime() { time_t utc_time; @@ -758,7 +769,7 @@ void LLFloaterIMSessionTab::reshapeChatLayoutPanel() void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show) { - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); + mTranslationCheckBox->setVisible(mIsNearbyChat && show); } // static @@ -805,15 +816,10 @@ void LLFloaterIMSessionTab::reloadEmptyFloaters() void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive) { - LLButton* voiceButton = getChild<LLButton>("voice_call_btn"); - voiceButton->setImageOverlay( - callIsActive? getString("call_btn_stop") : getString("call_btn_start")); - - voiceButton->setToolTip( - callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip")); + mVoiceButton->setImageOverlay(callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + mVoiceButton->setToolTip(callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip")); enableDisableCallBtn(); - } void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self) @@ -898,6 +904,7 @@ void LLFloaterIMSessionTab::restoreFloater() mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon")); setMessagePaneExpanded(true); saveCollapsedState(); + mInputEditor->enableSingleLineMode(false); enableResizeCtrls(true, true, true); } } @@ -953,8 +960,8 @@ void LLFloaterIMSessionTab::updateGearBtn() if(prevVisibility != mGearBtn->getVisible()) { LLRect gear_btn_rect = mGearBtn->getRect(); - LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect(); - LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect(); + LLRect add_btn_rect = mAddBtn->getRect(); + LLRect call_btn_rect = mVoiceButton->getRect(); S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight; S32 right_shift = gear_btn_rect.getWidth() + gap_width; if(mGearBtn->getVisible()) @@ -968,24 +975,24 @@ void LLFloaterIMSessionTab::updateGearBtn() add_btn_rect.translate(-right_shift,0); call_btn_rect.translate(-right_shift,0); } - getChild<LLButton>("add_btn")->setRect(add_btn_rect); - getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect); + mAddBtn->setRect(add_btn_rect); + mVoiceButton->setRect(call_btn_rect); } } void LLFloaterIMSessionTab::initBtns() { LLRect gear_btn_rect = mGearBtn->getRect(); - LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect(); - LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect(); + LLRect add_btn_rect = mAddBtn->getRect(); + LLRect call_btn_rect = mVoiceButton->getRect(); S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight; S32 right_shift = gear_btn_rect.getWidth() + gap_width; add_btn_rect.translate(-right_shift,0); call_btn_rect.translate(-right_shift,0); - getChild<LLButton>("add_btn")->setRect(add_btn_rect); - getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect); + mAddBtn->setRect(add_btn_rect); + mVoiceButton->setRect(call_btn_rect); } // static @@ -1083,21 +1090,26 @@ void LLFloaterIMSessionTab::saveCollapsedState() } BOOL LLFloaterIMSessionTab::handleKeyHere(KEY key, MASK mask ) { + BOOL handled = FALSE; + if(mask == MASK_ALT) { LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance(); if (KEY_RETURN == key && !isTornOff()) { floater_container->expandConversation(); + handled = TRUE; } if ((KEY_UP == key) || (KEY_LEFT == key)) { floater_container->selectNextorPreviousConversation(false); + handled = TRUE; } if ((KEY_DOWN == key ) || (KEY_RIGHT == key)) { floater_container->selectNextorPreviousConversation(true); + handled = TRUE; } } - return TRUE; + return handled; } diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 302d5a8066..e7b05a584b 100755 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -171,8 +171,7 @@ protected: LLFolderView* mConversationsRoot; LLScrollContainer* mScroller; - LLOutputMonitorCtrl* mSpeakingIndicator; - LLChatHistory* mChatHistory; + LLChatHistory* mChatHistory; LLChatEntry* mInputEditor; LLLayoutPanel * mChatLayoutPanel; LLLayoutStack * mInputPanels; @@ -182,6 +181,9 @@ protected: LLButton* mTearOffBtn; LLButton* mCloseBtn; LLButton* mGearBtn; + LLButton* mAddBtn; + LLButton* mVoiceButton; + LLUICtrl* mTranslationCheckBox; private: // Handling selection and contextual menu @@ -201,6 +203,8 @@ private: */ void reshapeChatLayoutPanel(); + void onInputEditorClicked(); + bool checkIfTornOff(); bool mIsHostAttached; bool mHasVisibleBeenInitialized; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 55b03986d0..4ebe813be6 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -452,6 +452,7 @@ BOOL LLFloaterPreference::postBuild() getChild<LLComboBox>("ConferenceIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ConferenceIMOptions")); getChild<LLComboBox>("GroupChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"GroupChatOptions")); getChild<LLComboBox>("NearbyChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NearbyChatOptions")); + getChild<LLComboBox>("ObjectIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ObjectIMOptions")); // if floater is opened before login set default localized do not disturb message if (LLStartUp::getStartupState() < STATE_STARTED) @@ -721,6 +722,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) onNotificationsChange("ConferenceIMOptions"); onNotificationsChange("GroupChatOptions"); onNotificationsChange("NearbyChatOptions"); + onNotificationsChange("ObjectIMOptions"); LLPanelLogin::setAlwaysRefresh(true); refresh(); @@ -928,7 +930,7 @@ void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) bool show_notifications_alert = true; for (notifications_map::iterator it_notification = mNotificationOptions.begin(); it_notification != mNotificationOptions.end(); it_notification++) { - if(it_notification->second != "None") + if(it_notification->second != "No action") { show_notifications_alert = false; break; diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index 586965e5a0..c28657dbcd 100755 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -74,6 +74,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) it != end_it; ++it) { + // Recursive call to sort() on child (CHUI-849) LLFolderViewFolder* child_folderp = *it; sort(child_folderp); @@ -129,12 +130,12 @@ void LLFolderViewModelItemInventory::requestSort() void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) { LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); - - bool passed_filter_before = mPrevPassedAllFilters; + bool before = mPrevPassedAllFilters; mPrevPassedAllFilters = passedFilter(filter_generation); - if (passed_filter_before != mPrevPassedAllFilters) + if (before != mPrevPassedAllFilters) { + // Need to rearrange the folder if the filtered state of the item changed LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); if (parent_folder) { @@ -150,11 +151,11 @@ bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* ite bool continue_filtering = true; if (item->getLastFilterGeneration() < filter_generation) { - // recursive application of the filter for child items + // Recursive application of the filter for child items (CHUI-849) continue_filtering = item->filter( filter ); } - // track latest generation to pass any child items, for each folder up to root + // Update latest generation to pass filter in parent and propagate up to root if (item->passedFilter()) { LLFolderViewModelItemInventory* view_model = this; @@ -174,53 +175,61 @@ bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) const S32 filter_generation = filter.getCurrentGeneration(); const S32 must_pass_generation = filter.getFirstRequiredGeneration(); - if (getLastFilterGeneration() >= must_pass_generation + if (getLastFilterGeneration() >= must_pass_generation && getLastFolderFilterGeneration() >= must_pass_generation && !passedFilter(must_pass_generation)) { // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done + // go ahead and flag this item as not pass setPassedFilter(false, filter_generation); setPassedFolderFilter(false, filter_generation); return true; } - const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) - ? filter.checkFolder(this) - : true; + // *TODO : Revise the logic for fast pass on less restrictive filter case + /* + const S32 sufficient_pass_generation = filter.getFirstSuccessGeneration(); + if (getLastFilterGeneration() >= sufficient_pass_generation + && getLastFolderFilterGeneration() >= sufficient_pass_generation + && passedFilter(sufficient_pass_generation)) + { + // passed an earlier filter that was a superset of the current one + // go ahead and flag this item as pass + setPassedFilter(true, filter_generation); + setPassedFolderFilter(true, filter_generation); + return true; + } + */ + + const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) ? filter.checkFolder(this) : true; setPassedFolderFilter(passed_filter_folder, filter_generation); - if(!mChildren.empty() + bool continue_filtering = true; + + if (!mChildren.empty() && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass - || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement + || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement { // now query children - for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); - iter != end_iter && filter.getFilterCount() > 0; - ++iter) + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); iter != end_iter; ++iter) { - if (!filterChildItem((*iter), filter)) + continue_filtering = filterChildItem((*iter), filter); + if (!continue_filtering) { break; } } } - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // so filter ourselves now - if (filter.getFilterCount() > 0) + // If we didn't use all the filter time that means we filtered all of our descendants so we can filter ourselves now + if (continue_filtering) { - filter.decrementFilterCount(); - + // This is where filter check on the item done (CHUI-849) const bool passed_filter = filter.check(this); setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize()); - return true; - } - else - { - return false; + continue_filtering = !filter.isTimedOut(); } + return continue_filtering; } LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() @@ -307,8 +316,8 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, } } -LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) - : LLFolderViewModelItemCommon(root_view_model), - mPrevPassedAllFilters(false) +LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) : + LLFolderViewModelItemCommon(root_view_model), + mPrevPassedAllFilters(false) { } diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index 890d03d1c9..9dcfdfa185 100755 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -59,9 +59,8 @@ public: virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; virtual LLToolDragAndDrop::ESource getDragSource() const = 0; - protected: - bool mPrevPassedAllFilters; + bool mPrevPassedAllFilters; }; class LLInventorySort diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index a0f2918bd7..302d21c2e4 100755 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -116,6 +116,80 @@ public: }; LLGroupHandler gGroupHandler; +// This object represents a pending request for specified group member information +// which is needed to check whether avatar can leave group +class LLFetchGroupMemberData : public LLGroupMgrObserver +{ +public: + LLFetchGroupMemberData(const LLUUID& group_id) : + mGroupId(group_id), + mRequestProcessed(false), + LLGroupMgrObserver(group_id) + { + llinfos << "Sending new group member request for group_id: "<< group_id << llendl; + LLGroupMgr* mgr = LLGroupMgr::getInstance(); + // register ourselves as an observer + mgr->addObserver(this); + // send a request + mgr->sendGroupPropertiesRequest(group_id); + mgr->sendCapGroupMembersRequest(group_id); + } + + ~LLFetchGroupMemberData() + { + if (!mRequestProcessed) + { + // Request is pending + llwarns << "Destroying pending group member request for group_id: " + << mGroupId << llendl; + } + // Remove ourselves as an observer + LLGroupMgr::getInstance()->removeObserver(this); + } + + void changed(LLGroupChange gc) + { + if (gc == GC_MEMBER_DATA && !mRequestProcessed) + { + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupId); + if (!gdatap) + { + llwarns << "LLGroupMgr::getInstance()->getGroupData() was NULL" << llendl; + } + else if (!gdatap->isMemberDataComplete()) + { + llwarns << "LLGroupMgr::getInstance()->getGroupData()->isMemberDataComplete() was FALSE" << llendl; + } + else + { + processGroupData(); + mRequestProcessed = true; + } + } + } + + LLUUID getGroupId() { return mGroupId; } + virtual void processGroupData() = 0; +protected: + LLUUID mGroupId; +private: + bool mRequestProcessed; +}; + +class LLFetchLeaveGroupData: public LLFetchGroupMemberData +{ +public: + LLFetchLeaveGroupData(const LLUUID& group_id) + : LLFetchGroupMemberData(group_id) + {} + void processGroupData() + { + LLGroupActions::processLeaveGroupDataResponse(mGroupId); + } +}; + +LLFetchLeaveGroupData* gFetchLeaveGroupData = NULL; + // static void LLGroupActions::search() { @@ -208,23 +282,52 @@ bool LLGroupActions::onJoinGroup(const LLSD& notification, const LLSD& response) void LLGroupActions::leave(const LLUUID& group_id) { if (group_id.isNull()) + { return; + } - S32 count = gAgent.mGroups.count(); - S32 i; - for (i = 0; i < count; ++i) + LLGroupData group_data; + if (gAgent.getGroupData(group_id, group_data)) { - if(gAgent.mGroups.get(i).mID == group_id) - break; + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); + if (!gdatap || !gdatap->isMemberDataComplete()) + { + if (gFetchLeaveGroupData != NULL) + { + delete gFetchLeaveGroupData; + gFetchLeaveGroupData = NULL; + } + gFetchLeaveGroupData = new LLFetchLeaveGroupData(group_id); + } + else + { + processLeaveGroupDataResponse(group_id); + } } - if (i < count) +} + +//static +void LLGroupActions::processLeaveGroupDataResponse(const LLUUID group_id) +{ + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); + LLUUID agent_id = gAgent.getID(); + LLGroupMgrGroupData::member_list_t::iterator mit = gdatap->mMembers.find(agent_id); + //get the member data for the group + if ( mit != gdatap->mMembers.end() ) { - LLSD args; - args["GROUP"] = gAgent.mGroups.get(i).mName; - LLSD payload; - payload["group_id"] = group_id; - LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup); + LLGroupMemberData* member_data = (*mit).second; + + if ( member_data && member_data->isOwner() && gdatap->mMemberCount == 1) + { + LLNotificationsUtil::add("OwnerCannotLeaveGroup"); + return; + } } + LLSD args; + args["GROUP"] = gdatap->mName; + LLSD payload; + payload["group_id"] = group_id; + LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup); } // static diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h index 3f9852f194..afc4686dd7 100755 --- a/indra/newview/llgroupactions.h +++ b/indra/newview/llgroupactions.h @@ -114,6 +114,14 @@ public: private: static bool onJoinGroup(const LLSD& notification, const LLSD& response); static bool onLeaveGroup(const LLSD& notification, const LLSD& response); + + /** + * This function is called by LLFetchLeaveGroupData upon receiving a response to a group + * members data request. + */ + static void processLeaveGroupDataResponse(const LLUUID group_id); + + friend class LLFetchLeaveGroupData; }; #endif // LL_LLGROUPACTIONS_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 3b72ad3cd9..9e23755d73 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -103,6 +103,7 @@ BOOL LLSessionTimeoutTimer::tick() } +void notify_of_message(const LLSD& msg, bool is_dnd_msg); void process_dnd_im(const LLSD& notification) { @@ -129,15 +130,9 @@ void process_dnd_im(const LLSD& notification) fromID, false, false); //will need slight refactor to retrieve whether offline message or not (assume online for now) + } - LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); - - if (im_box) - { - im_box->flashConversationItemWidget(sessionID, true); - } - - } + notify_of_message(data, true); } @@ -158,75 +153,106 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, LLNotificationsUtil::add("IMToast", args, args, boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID())); } -void on_new_message(const LLSD& msg) +void notify_of_message(const LLSD& msg, bool is_dnd_msg) { - std::string user_preferences; - LLUUID participant_id = msg["from_id"].asUUID(); - LLUUID session_id = msg["session_id"].asUUID(); - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); - - // do not show notification which goes from agent - if (gAgent.getID() == participant_id) - { - return; - } + std::string user_preferences; + LLUUID participant_id = msg[is_dnd_msg ? "FROM_ID" : "from_id"].asUUID(); + LLUUID session_id = msg[is_dnd_msg ? "SESSION_ID" : "session_id"].asUUID(); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); - // determine state of conversations floater - enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; + // do not show notification which goes from agent + if (gAgent.getID() == participant_id) + { + return; + } + // determine state of conversations floater + enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; - LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id); - + bool store_dnd_message = false; // flag storage of a dnd message + bool is_session_focused = session_floater->isTornOff() && session_floater->hasFocus(); if (!LLFloater::isVisible(im_box) || im_box->isMinimized()) { conversations_floater_status = CLOSED; } else if (!im_box->hasFocus() && - !(session_floater && LLFloater::isVisible(session_floater) - && !session_floater->isMinimized() && session_floater->hasFocus())) + !(session_floater && LLFloater::isVisible(session_floater) + && !session_floater->isMinimized() && session_floater->hasFocus())) { conversations_floater_status = NOT_ON_TOP; } else if (im_box->getSelectedSession() != session_id) { conversations_floater_status = ON_TOP; - } + } else { conversations_floater_status = ON_TOP_AND_ITEM_IS_SELECTED; } - // determine user prefs for this session - if (session_id.isNull()) - { - user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); - } - else if(session->isP2PSessionType()) - { - if (LLAvatarTracker::instance().isBuddy(participant_id)) - { - user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); - } - else - { - user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); - } - } - else if(session->isAdHocSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); - } - else if(session->isGroupSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); - } + // determine user prefs for this session + if (session_id.isNull()) + { + if (msg["source_type"].asInteger() == CHAT_SOURCE_OBJECT) + { + user_preferences = gSavedSettings.getString("NotificationObjectIMOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundObjectIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } + else + { + user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNearbyChatIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } + } + else if(session->isP2PSessionType()) + { + if (LLAvatarTracker::instance().isBuddy(participant_id)) + { + user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundFriendIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } + else + { + user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNonFriendIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } + } + else if(session->isAdHocSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundConferenceIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } + else if(session->isGroupSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundGroupChatIM") == TRUE)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + } - // actions: + // actions: // 0. nothing - exit - if (("none" == user_preferences || + if (("noaction" == user_preferences || ON_TOP_AND_ITEM_IS_SELECTED == conversations_floater_status) && session_floater->isMessagePaneExpanded()) { @@ -261,57 +287,103 @@ void on_new_message(const LLSD& msg) } } } - else - { - //If in DND mode, allow notification to be stored so upon DND exit - //useMostItrusiveIMNotification will be called to notify user a message exists - if(session_id.notNull() - && participant_id.notNull() - && !session_floater->isShown()) - { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } - } + else + { + store_dnd_message = true; + } - // 2. Flash line item - if ("openconversations" == user_preferences - || ON_TOP == conversations_floater_status - || ("toast" == user_preferences && ON_TOP != conversations_floater_status) - || ("flash" == user_preferences && CLOSED == conversations_floater_status)) - { - if(!LLMuteList::getInstance()->isMuted(participant_id)) - { - im_box->flashConversationItemWidget(session_id, true); - } - } + } - // 3. Flash FUI button - if (("toast" == user_preferences || "flash" == user_preferences) && - (CLOSED == conversations_floater_status - || NOT_ON_TOP == conversations_floater_status)) - { - if(!LLMuteList::getInstance()->isMuted(participant_id) - && !gAgent.isDoNotDisturb()) - { - gToolBarView->flashCommand(LLCommandId("chat"), true); - } - } + // 2. Flash line item + if ("openconversations" == user_preferences + || ON_TOP == conversations_floater_status + || ("toast" == user_preferences && ON_TOP != conversations_floater_status) + || ("flash" == user_preferences && (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status)) + || is_dnd_msg) + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { + if(gAgent.isDoNotDisturb()) + { + store_dnd_message = true; + } + else + { + if (is_dnd_msg && (ON_TOP == conversations_floater_status || + NOT_ON_TOP == conversations_floater_status || + CLOSED == conversations_floater_status)) + { + im_box->highlightConversationItemWidget(session_id, true); + } + else + { + im_box->flashConversationItemWidget(session_id, true); + } + } + } + } - // 4. Toast - if ((("toast" == user_preferences) && - (CLOSED == conversations_floater_status - || NOT_ON_TOP == conversations_floater_status)) - || !session_floater->isMessagePaneExpanded()) + // 3. Flash FUI button + if (("toast" == user_preferences || "flash" == user_preferences) && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status) + && !is_session_focused + && !is_dnd_msg) //prevent flashing FUI button because the conversation floater will have already opened + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { + if(!gAgent.isDoNotDisturb()) + { + gToolBarView->flashCommand(LLCommandId("chat"), true, im_box->isMinimized()); + } + else + { + store_dnd_message = true; + } + } + } - { - //Show IM toasts (upper right toasts) - // Skip toasting for system messages and for nearby chat - if(session_id.notNull() && participant_id.notNull()) - { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } + // 4. Toast + if ((("toast" == user_preferences) && + (ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status) && + (!session_floater->isTornOff() || !LLFloater::isVisible(session_floater))) + || !session_floater->isMessagePaneExpanded()) + + { + //Show IM toasts (upper right toasts) + // Skip toasting for system messages and for nearby chat + if(session_id.notNull() && participant_id.notNull()) + { + if(!is_dnd_msg) + { + if(gAgent.isDoNotDisturb()) + { + store_dnd_message = true; + } + else + { + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } + } + } + if (store_dnd_message) + { + // If in DND mode, allow notification to be stored so upon DND exit + // the user will be notified with some limitations (see 'is_dnd_msg' flag checks) + if(session_id.notNull() + && participant_id.notNull() + && !session_floater->isShown()) + { + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } +} + +void on_new_message(const LLSD& msg) +{ + notify_of_message(msg, false); } LLIMModel::LLIMModel() @@ -2597,6 +2669,13 @@ void LLIMMgr::addMessage( fixed_session_name = session_name; name_is_setted = true; } + bool skip_message = false; + if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) + { + // Evaluate if we need to skip this message when that setting is true (default is false) + skip_message = (LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); // Skip non friends... + skip_message &= !(other_participant_id == gAgentID); // You are your best friend... Don't skip yourself + } bool new_session = !hasSession(new_session_id); if (new_session) @@ -2608,6 +2687,12 @@ void LLIMMgr::addMessage( } LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); + skip_message &= !session->isGroupSessionType(); // Do not skip group chats... + if(skip_message) + { + gIMMgr->leaveSession(new_session_id); + } // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons // when answering questions. @@ -2648,23 +2733,13 @@ void LLIMMgr::addMessage( } } - bool skip_message = false; - if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) - { - // Evaluate if we need to skip this message when that setting is true (default is false) - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); - skip_message = (LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); // Skip non friends... - skip_message &= !session->isGroupSessionType(); // Do not skip group chats... - skip_message &= !(other_participant_id == gAgentID); // You are your best friend... Don't skip yourself - } - if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message) { LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg); } // Open conversation floater if offline messages are present - if (is_offline_msg) + if (is_offline_msg && !skip_message) { LLFloaterReg::showInstance("im_container"); LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")-> @@ -2971,10 +3046,9 @@ void LLIMMgr::inviteToSession( { bool isRejectGroupCall = (gSavedSettings.getBOOL("VoiceCallsRejectGroup") && (notify_box_type == "VoiceInviteGroup")); bool isRejectNonFriendCall = (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)); - bool isRejectDoNotDisturb = (gAgent.isDoNotDisturb() && !hasSession(session_id)); - if (isRejectGroupCall || isRejectNonFriendCall || isRejectDoNotDisturb) + if (isRejectGroupCall || isRejectNonFriendCall || gAgent.isDoNotDisturb()) { - if (isRejectDoNotDisturb && !isRejectGroupCall && !isRejectNonFriendCall) + if (gAgent.isDoNotDisturb() && !isRejectGroupCall && !isRejectNonFriendCall) { LLSD args; addSystemMessage(session_id, "you_auto_rejected_call", args); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 92f2d33073..3c6974cf6d 100755 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -70,11 +70,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p) mFilterSubString(p.substring), mCurrentGeneration(0), mFirstRequiredGeneration(0), - mFirstSuccessGeneration(0), - mFilterCount(0) + mFirstSuccessGeneration(0) { - mNextFilterGeneration = mCurrentGeneration + 1; - // copy mFilterOps into mDefaultFilterOps markDefault(); } @@ -92,9 +89,7 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) return passed_clipboard; } - std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; - - BOOL passed = (mFilterSubString.size() == 0 || string_offset != std::string::npos); + bool passed = (mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) != std::string::npos : true); passed = passed && checkAgainstFilterType(listener); passed = passed && checkAgainstPermissions(listener); passed = passed && checkAgainstFilterLinks(listener); @@ -105,17 +100,12 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) bool LLInventoryFilter::check(const LLInventoryItem* item) { - std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; - + const bool passed_string = (mFilterSubString.size() ? item->getName().find(mFilterSubString) != std::string::npos : true); const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); - const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); - const bool passed = (passed_filtertype - && passed_permissions - && passed_clipboard - && (mFilterSubString.size() == 0 || string_offset != std::string::npos)); + const bool passed_clipboard = checkAgainstClipboard(item->getUUID()); - return passed; + return passed_filtertype && passed_permissions && passed_clipboard && passed_string; } bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const @@ -439,7 +429,7 @@ void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) current_types = types; if (more_bits_set && fewer_bits_set) { - // neither less or more restrive, both simultaneously + // neither less or more restrictive, both simultaneously // so we need to filter from scratch setModified(FILTER_RESTART); } @@ -714,7 +704,7 @@ void LLInventoryFilter::resetDefault() void LLInventoryFilter::setModified(EFilterModified behavior) { mFilterText.clear(); - mCurrentGeneration = mNextFilterGeneration++; + mCurrentGeneration++; if (mFilterModified == FILTER_NONE) { @@ -1021,21 +1011,19 @@ LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const return mFilterOps.mShowFolderState; } -void LLInventoryFilter::setFilterCount(S32 count) -{ - mFilterCount = count; -} -S32 LLInventoryFilter::getFilterCount() const +bool LLInventoryFilter::isTimedOut() { - return mFilterCount; + return mFilterTime.hasExpired(); } -void LLInventoryFilter::decrementFilterCount() -{ - mFilterCount--; +void LLInventoryFilter::resetTime(S32 timeout) +{ + mFilterTime.reset(); + F32 time_in_sec = (F32)(timeout)/1000.0; + mFilterTime.setTimerExpirySec(time_in_sec); } -S32 LLInventoryFilter::getCurrentGeneration() const +S32 LLInventoryFilter::getCurrentGeneration() const { return mCurrentGeneration; } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 4912b5ca91..ce516af0b9 100755 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -215,12 +215,11 @@ public: void setModified(EFilterModified behavior = FILTER_RESTART); // +-------------------------------------------------------------------+ - // + Count + // + Time // +-------------------------------------------------------------------+ - void setFilterCount(S32 count); - S32 getFilterCount() const; - void decrementFilterCount(); - + void resetTime(S32 timeout); + bool isTimedOut(); + // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ @@ -262,13 +261,15 @@ private: const std::string mName; S32 mCurrentGeneration; + // The following makes checking for pass/no pass possible even if the item is not checked against the current generation + // Any item that *did not pass* the "required generation" will *not pass* the current one + // Any item that *passes* the "success generation" will *pass* the current one S32 mFirstRequiredGeneration; S32 mFirstSuccessGeneration; - S32 mNextFilterGeneration; - S32 mFilterCount; EFilterModified mFilterModified; - + LLTimer mFilterTime; + std::string mFilterText; std::string mEmptyLookupMessage; }; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index cf1fd4c0d0..e5b9e11d48 100755 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -192,7 +192,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) p.show_item_link_overlays = mShowItemLinkOverlays; p.root = NULL; p.options_menu = "menu_inventory.xml"; - + return LLUICtrlFactory::create<LLFolderView>(p); } @@ -396,6 +396,7 @@ LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() return getFilter().getShowFolderState(); } +// Called when something changed in the global model (new item, item coming through the wire, rename, move, etc...) (CHUI-849) void LLInventoryPanel::modelChanged(U32 mask) { static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh"); diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 2d7454b636..90b169ecd3 100755 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -25,7 +25,7 @@ */ #include "llviewerprecompiledheaders.h" - +#include "llfloaterconversationpreview.h" #include "llagent.h" #include "llagentui.h" #include "llavatarnamecache.h" @@ -206,6 +206,7 @@ private: }; LLLogChat::save_history_signal_t * LLLogChat::sSaveHistorySignal = NULL; +LLLoadHistoryThread::load_end_signal_t * LLLoadHistoryThread::mLoadEndSignal = NULL; //static std::string LLLogChat::makeLogFileName(std::string filename) @@ -336,75 +337,83 @@ void LLLogChat::saveHistory(const std::string& filename, void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params) { if (file_name.empty()) - { - llwarns << "Session name is Empty!" << llendl; - return ; - } - - bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; + { + LL_WARNS("LLLogChat::loadChatHistory") << "Session name is Empty!" << LL_ENDL; + return ; + } - LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ - if (!fptr) - { - fptr = LLFile::fopen(oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ - if (!fptr) - { - return; //No previous conversation with this name. - } - } - - char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ - char *bptr; - S32 len; - bool firstline = TRUE; + bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; - if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) - { //We need to load the whole historyFile or it's smaller than recall size, so get it all. - firstline = FALSE; - if (fseek(fptr, 0, SEEK_SET)) - { - fclose(fptr); - return; - } - } + LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + return; //No previous conversation with this name. + } + } - while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) - { - len = strlen(buffer) - 1; /*Flawfinder: ignore*/ - for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; - - if (firstline) - { - firstline = FALSE; - continue; - } + char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ + char *bptr; + S32 len; + bool firstline = TRUE; + + if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) + { //We need to load the whole historyFile or it's smaller than recall size, so get it all. + firstline = FALSE; + if (fseek(fptr, 0, SEEK_SET)) + { + fclose(fptr); + return; + } + } + while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) + { + len = strlen(buffer) - 1; /*Flawfinder: ignore*/ + for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; + + if (firstline) + { + firstline = FALSE; + continue; + } + + std::string line(buffer); + + //updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message + if (' ' == line[0]) + { + line.erase(0, MULTI_LINE_PREFIX.length()); + append_to_last_message(messages, '\n' + line); + } + else if (0 == len && ('\n' == line[0] || '\r' == line[0])) + { + //to support old format's multilined messages with new lines used to divide paragraphs + append_to_last_message(messages, line); + } + else + { + LLSD item; + if (!LLChatLogParser::parse(line, item, load_params)) + { + item[LL_IM_TEXT] = line; + } + messages.push_back(item); + } + } + fclose(fptr); - std::string line(buffer); - //updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message - if (' ' == line[0]) - { - line.erase(0, MULTI_LINE_PREFIX.length()); - append_to_last_message(messages, '\n' + line); - } - else if (0 == len && ('\n' == line[0] || '\r' == line[0])) - { - //to support old format's multilined messages with new lines used to divide paragraphs - append_to_last_message(messages, line); - } - else - { - LLSD item; - if (!LLChatLogParser::parse(line, item, load_params)) - { - item[LL_IM_TEXT] = line; - } - messages.push_back(item); - } - } - fclose(fptr); } +void LLLogChat::startChatHistoryThread(const std::string& file_name, const LLSD& load_params) +{ + + LLLoadHistoryThread* mThread = new LLLoadHistoryThread(); + mThread->start(); + mThread->setHistoryParams(file_name, load_params); +} // static std::string LLLogChat::oldLogFileName(std::string filename) { @@ -461,6 +470,13 @@ void LLLogChat::findTranscriptFiles(std::string pattern, std::vector<std::string LLFILE * filep = LLFile::fopen(fullname, "rb"); if (NULL != filep) { + if(makeLogFileName("chat")== fullname) + { + //Add Nearby chat history to the list of transcriptions + list_of_transcriptions.push_back(gDirUtilp->add(dirname, filename)); + LLFile::close(filep); + return; + } char buffer[LOG_RECALL_SIZE]; fseek(filep, 0, SEEK_END); // seek to end of file @@ -631,7 +647,7 @@ void LLLogChat::deleteTranscripts() } // static -bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id) +bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id, bool is_group) { std::vector<std::string> list_of_transcriptions; LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); @@ -641,20 +657,53 @@ bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id) LLAvatarName avatar_name; LLAvatarNameCache::get(avatar_id, &avatar_name); std::string avatar_user_name = avatar_name.getAccountName(); - std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_'); - - BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + if(!is_group) { - if (std::string::npos != transcript_file_name.find(avatar_user_name)) + std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_'); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) { - return true; + if (std::string::npos != transcript_file_name.find(avatar_user_name)) + { + return true; + } } } + else + { + std::string file_name; + gCacheName->getGroupName(avatar_id, file_name); + file_name = makeLogFileName(file_name); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + } + } return false; } +bool LLLogChat::isNearbyTranscriptExist() +{ + std::vector<std::string> list_of_transcriptions; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); + + std::string file_name; + file_name = makeLogFileName("chat"); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + return false; +} + //*TODO mark object's names in a special way so that they will be distinguishable form avatar name //which are more strict by its nature (only firstname and secondname) //Example, an object's name can be written like "Object <actual_object's_name>" @@ -795,3 +844,116 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params im[LL_IM_TEXT] = name_and_text[IDX_TEXT]; return true; //parsed name and message text, maybe have a timestamp too } + + + + LLLoadHistoryThread::LLLoadHistoryThread() : LLThread("load chat history") + { + mNewLoad = false; + } + + void LLLoadHistoryThread::run() + { + while (!LLApp::isQuitting()) + { + if(mNewLoad) + { + loadHistory(mFileName,mMessages,mLoadParams); + shutdown(); + } + } + } + void LLLoadHistoryThread::setHistoryParams(const std::string& file_name, const LLSD& load_params) + { + mFileName = file_name; + mLoadParams = load_params; + mNewLoad = true; + } + void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params) + { + + if (file_name.empty()) + { + LL_WARNS("LLLogChat::loadHistory") << "Session name is Empty!" << LL_ENDL; + return ; + } + + bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; + + LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + return; //No previous conversation with this name. + } + } + + char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ + char *bptr; + S32 len; + bool firstline = TRUE; + + if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) + { //We need to load the whole historyFile or it's smaller than recall size, so get it all. + firstline = FALSE; + if (fseek(fptr, 0, SEEK_SET)) + { + fclose(fptr); + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + return; + } + } + while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr)) + { + len = strlen(buffer) - 1; /*Flawfinder: ignore*/ + for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0'; + + if (firstline) + { + firstline = FALSE; + continue; + } + + std::string line(buffer); + + //updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message + if (' ' == line[0]) + { + line.erase(0, MULTI_LINE_PREFIX.length()); + append_to_last_message(messages, '\n' + line); + } + else if (0 == len && ('\n' == line[0] || '\r' == line[0])) + { + //to support old format's multilined messages with new lines used to divide paragraphs + append_to_last_message(messages, line); + } + else + { + LLSD item; + if (!LLChatLogParser::parse(line, item, load_params)) + { + item[LL_IM_TEXT] = line; + } + messages.push_back(item); + } + } + fclose(fptr); + mNewLoad = false; + (*mLoadEndSignal)(messages, file_name); + } + + //static + boost::signals2::connection LLLoadHistoryThread::setLoadEndSignal(const load_end_signal_t::slot_type& cb) + { + if (NULL == mLoadEndSignal) + { + mLoadEndSignal = new load_end_signal_t(); + } + + return mLoadEndSignal->connect(cb); + } diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index e819f00dd9..acee99afa2 100755 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -28,6 +28,24 @@ #define LL_LLLOGCHAT_H class LLChat; +class LLLoadHistoryThread : public LLThread +{ +private: + std::string mFileName; + std::list<LLSD> mMessages; + LLSD mLoadParams; + bool mNewLoad; +public: + LLLoadHistoryThread(); + + void setHistoryParams(const std::string& file_name, const LLSD& load_params); + virtual void loadHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params); + virtual void run(); + + typedef boost::signals2::signal<void (std::list<LLSD>& messages,const std::string& file_name)> load_end_signal_t; + static load_end_signal_t * mLoadEndSignal; + static boost::signals2::connection setLoadEndSignal(const load_end_signal_t::slot_type& cb); +}; class LLLogChat { @@ -39,6 +57,7 @@ public: LOG_LLSD, LOG_END }; + static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); /** @@ -54,6 +73,7 @@ public: static void getListOfTranscriptBackupFiles(std::vector<std::string>& list_of_transcriptions); static void loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params = LLSD()); + static void startChatHistoryThread(const std::string& file_name, const LLSD& load_params); typedef boost::signals2::signal<void ()> save_history_signal_t; static boost::signals2::connection setSaveHistorySignal(const save_history_signal_t::slot_type& cb); @@ -67,7 +87,8 @@ public: std::vector<std::string>& listOfFilesToMove); static void deleteTranscripts(); - static bool isTranscriptExist(const LLUUID& avatar_id); + static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false); + static bool isNearbyTranscriptExist(); private: static std::string cleanFileName(std::string filename); @@ -126,6 +147,7 @@ protected: virtual ~LLChatLogParser() {}; }; + // LLSD map lookup constants extern const std::string LL_IM_TIME; //("time"); extern const std::string LL_IM_TEXT; //("message"); diff --git a/indra/newview/llnotificationstorage.h b/indra/newview/llnotificationstorage.h index 7aabf7d09e..53fd898ea4 100755 --- a/indra/newview/llnotificationstorage.h +++ b/indra/newview/llnotificationstorage.h @@ -44,6 +44,7 @@ public: protected: bool writeNotifications(const LLSD& pNotificationData) const; bool readNotifications(LLSD& pNotificationData) const; + void setFileName(std::string pFileName) {mFileName = pFileName;} LLNotificationResponderInterface* createResponder(const std::string& pNotificationName, const LLSD& pParams) const; diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index a85335f1ba..4ca961c1f9 100755 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -83,13 +83,6 @@ bool LLTipHandler::processNotification(const LLNotificationPtr& notification) if (notification->canLogToChat()) { LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - - // don't show toast if Nearby Chat is opened - LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - if (nearby_chat->isChatVisible()) - { - return false; - } } std::string session_name = notification->getPayload()["SESSION_NAME"]; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index bcb90bcb56..911ecaad9d 100755 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -82,10 +82,6 @@ const S32 MAX_PASSWORD = 16; LLPanelLogin *LLPanelLogin::sInstance = NULL; BOOL LLPanelLogin::sCapslockDidNotification = FALSE; -// Helper for converting a user name into the canonical "Firstname Lastname" form. -// For new accounts without a last name "Resident" is added as a last name. -static std::string canonicalize_username(const std::string& name); - class LLLoginRefreshHandler : public LLCommandHandler { public: @@ -266,7 +262,6 @@ void LLPanelLogin::addFavoritesToStartLocation() // Load favorites into the combo. std::string user_defined_name = getChild<LLComboBox>("username_combo")->getSimple(); - std::string canonical_user_name = canonicalize_username(user_defined_name); std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); LLSD fav_llsd; llifstream file; @@ -279,7 +274,7 @@ void LLPanelLogin::addFavoritesToStartLocation() // The account name in stored_favorites.xml has Resident last name even if user has // a single word account name, so it can be compared case-insensitive with the // user defined "firstname lastname". - S32 res = LLStringUtil::compareInsensitive(canonical_user_name, iter->first); + S32 res = LLStringUtil::compareInsensitive(user_defined_name, iter->first); if (res != 0) { lldebugs << "Skipping favorites for " << iter->first << llendl; @@ -1012,29 +1007,3 @@ void LLPanelLogin::onLocationSLURL() LLStartUp::setStartSLURL(location); // calls onUpdateStartSLURL, above } - - -std::string canonicalize_username(const std::string& name) -{ - std::string cname = name; - LLStringUtil::trim(cname); - - // determine if the username is a first/last form or not. - size_t separator_index = cname.find_first_of(" ._"); - std::string first = cname.substr(0, separator_index); - std::string last; - if (separator_index != cname.npos) - { - last = cname.substr(separator_index+1, cname.npos); - LLStringUtil::trim(last); - } - else - { - // ...on Linden grids, single username users as considered to have - // last name "Resident" - last = "Resident"; - } - - // Username in traditional "firstname lastname" form. - return first + ' ' + last; -} diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index d6535c88e9..53deded2f2 100755 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -130,6 +130,8 @@ BOOL LLPanelMainInventory::postBuild() mFilterTabs = getChild<LLTabContainer>("inventory filter tabs"); mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this)); + mCounterCtrl = getChild<LLUICtrl>("ItemcountText"); + //panel->getFilter().markDefault(); // Set up the default inv. panel/filter settings. @@ -566,7 +568,7 @@ void LLPanelMainInventory::draw() void LLPanelMainInventory::updateItemcountText() { // *TODO: Calling setlocale() on each frame may be inefficient. - LLLocale locale(LLStringUtil::getLocale()); + //LLLocale locale(LLStringUtil::getLocale()); std::string item_count_string; LLResMgr::getInstance()->getIntegerString(item_count_string, gInventory.getItemCount()); @@ -589,8 +591,7 @@ void LLPanelMainInventory::updateItemcountText() text = getString("ItemcountUnknown"); } - // *TODO: Cache the LLUICtrl* for the ItemcountText control - getChild<LLUICtrl>("ItemcountText")->setValue(text); + mCounterCtrl->setValue(text); } void LLPanelMainInventory::onFocusReceived() diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 899931aa89..394b004e20 100755 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -121,6 +121,7 @@ private: LLFilterEditor* mFilterEditor; LLTabContainer* mFilterTabs; + LLUICtrl* mCounterCtrl; LLHandle<LLFloater> mFinderHandle; LLInventoryPanel* mActivePanel; bool mResortActivePanel; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index e2e7006773..5acc98904b 100755 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -36,6 +36,8 @@ #include "lltabcontainer.h" #include "llviewercontrol.h" #include "llviewernetwork.h" +#include "llmutelist.h" +#include "llpanelblockedlist.h" static const std::string PANEL_PICKS = "panel_picks"; @@ -137,6 +139,12 @@ public: return true; } + if (verb == "removefriend") + { + LLAvatarActions::removeFriendDialog(avatar_id); + return true; + } + if (verb == "mute") { if (! LLAvatarActions::isBlocked(avatar_id)) @@ -155,6 +163,18 @@ public: return true; } + if (verb == "block") + { + if (params.size() > 2) + { + const std::string object_name = params[2].asString(); + LLMute mute(avatar_id, object_name, LLMute::OBJECT); + LLMuteList::getInstance()->add(mute); + LLPanelBlockedList::showPanelAndSelect(mute.mID); + } + return true; + } + return false; } }; diff --git a/indra/newview/llpersistentnotificationstorage.cpp b/indra/newview/llpersistentnotificationstorage.cpp index 666f10df96..076c3e0235 100755 --- a/indra/newview/llpersistentnotificationstorage.cpp +++ b/indra/newview/llpersistentnotificationstorage.cpp @@ -38,7 +38,8 @@ LLPersistentNotificationStorage::LLPersistentNotificationStorage() : LLSingleton<LLPersistentNotificationStorage>() - , LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml")) + , LLNotificationStorage("") + , mLoaded(false) { } @@ -89,8 +90,13 @@ void LLPersistentNotificationStorage::loadNotifications() LL_INFOS("LLPersistentNotificationStorage") << "start loading notifications" << LL_ENDL; - LLNotifications::instance().getChannel("Persistent")-> - connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1)); + if (mLoaded) + { + LL_INFOS("LLPersistentNotificationStorage") << "notifications already loaded, exiting" << LL_ENDL; + return; + } + + mLoaded = true; LLSD input; if (!readNotifications(input) ||input.isUndefined()) @@ -135,8 +141,20 @@ void LLPersistentNotificationStorage::loadNotifications() LL_INFOS("LLPersistentNotificationStorage") << "finished loading notifications" << LL_ENDL; } +void LLPersistentNotificationStorage::initialize() +{ + setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml")); + LLNotifications::instance().getChannel("Persistent")-> + connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1)); +} + bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload) { + // In case we received channel changed signal but haven't yet loaded notifications, do it + if (!mLoaded) + { + loadNotifications(); + } // we ignore "load" messages, but rewrite the persistence file on any other const std::string sigtype = payload["sigtype"].asString(); if ("load" != sigtype) diff --git a/indra/newview/llpersistentnotificationstorage.h b/indra/newview/llpersistentnotificationstorage.h index 98a825d2c1..bf0306380e 100755 --- a/indra/newview/llpersistentnotificationstorage.h +++ b/indra/newview/llpersistentnotificationstorage.h @@ -53,10 +53,13 @@ public: void saveNotifications(); void loadNotifications(); + void initialize(); + protected: private: bool onPersistentChannelChanged(const LLSD& payload); + bool mLoaded; }; #endif // LL_LLPERSISTENTNOTIFICATIONSTORAGE_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cff3a7e02a..67a76460a7 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -51,6 +51,7 @@ #include "lllandmark.h" #include "llcachename.h" #include "lldir.h" +#include "lldonotdisturbnotificationstorage.h" #include "llerrorcontrol.h" #include "llfloaterreg.h" #include "llfocusmgr.h" @@ -68,6 +69,7 @@ #include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" +#include "llpersistentnotificationstorage.h" #include "llteleporthistory.h" #include "llregionhandle.h" #include "llsd.h" @@ -900,6 +902,10 @@ bool idle_startup() gDirUtilp->setLindenUserDir(userid); LLFile::mkdir(gDirUtilp->getLindenUserDir()); + // As soon as directories are ready initialize notification storages + LLPersistentNotificationStorage::getInstance()->initialize(); + LLDoNotDisturbNotificationStorage::getInstance()->initialize(); + // Set PerAccountSettingsFile to the default value. gSavedSettings.setString("PerAccountSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index e92bd766ca..1a3add2bfb 100755 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -423,9 +423,18 @@ void LLNotificationWellWindow::onItemClick(LLSysWellItem* item) void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) { LLUUID id = item->getID(); - removeItemByID(id); + if(mChannel) + { + // removeItemByID() is invoked from killToastByNotificationID() and item will removed; mChannel->killToastByNotificationID(id); + } + else + { + // removeItemByID() should be called one time for each item to remove it from notification well + removeItemByID(id); + } + } void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 75e6e3d13a..025ef3945d 100755 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -28,6 +28,7 @@ #include "lltoastimpanel.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llfloaterreg.h" #include "llgroupactions.h" #include "llgroupiconctrl.h" @@ -61,6 +62,15 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif style_params.font.name(font_name); style_params.font.size(font_size); + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(p.session_id); + mIsGroupMsg = (im_session->mSessionType == LLIMModel::LLIMSession::GROUP_SESSION); + if(mIsGroupMsg) + { + mAvatarName->setValue(im_session->mName); + LLAvatarName avatar_name; + LLAvatarNameCache::get(p.avatar_id, &avatar_name); + p.message = "[From " + avatar_name.getDisplayName() + "]\n" + p.message; + } //Handle IRC styled /me messages. std::string prefix = p.message.substr(0, 4); @@ -81,12 +91,17 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif mMessage->setText(p.message, style_params); } - mAvatarName->setValue(p.from); + if(!mIsGroupMsg) + { + mAvatarName->setValue(p.from); + } mTime->setValue(p.time); mSessionID = p.session_id; mAvatarID = p.avatar_id; mNotification = p.notification; + + initIcon(); S32 maxLinesCount; @@ -147,7 +162,14 @@ void LLToastIMPanel::spawnNameToolTip() LLToolTip::Params params; params.background_visible(false); - params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + if(!mIsGroupMsg) + { + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + } + else + { + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_group", LLSD().with("group_id", mSessionID), FALSE)); + } params.delay_time(0.0f); // spawn instantly on hover params.image(LLUI::getUIImage("Info_Small")); params.message(""); diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index 3eb11fb3bc..767617dabc 100755 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -73,6 +73,8 @@ private: LLTextBox* mAvatarName; LLTextBox* mTime; LLTextBox* mMessage; + + bool mIsGroupMsg; }; #endif // LLTOASTIMPANEL_H_ diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index b2318f9158..4f2c99fba8 100755 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -180,13 +180,13 @@ S32 LLToolBarView::stopCommandInProgress(const LLCommandId& commandId) return command_location; } -S32 LLToolBarView::flashCommand(const LLCommandId& commandId, bool flash) +S32 LLToolBarView::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */) { S32 command_location = hasCommand(commandId); if (command_location != TOOLBAR_NONE) { - mToolbars[command_location]->flashCommand(commandId, flash); + mToolbars[command_location]->flashCommand(commandId, flash, force_flashing); } return command_location; diff --git a/indra/newview/lltoolbarview.h b/indra/newview/lltoolbarview.h index 7125dd9990..dcc3862074 100755 --- a/indra/newview/lltoolbarview.h +++ b/indra/newview/lltoolbarview.h @@ -90,7 +90,7 @@ public: S32 removeCommand(const LLCommandId& commandId, int& rank); // Sets the rank the removed command was at, RANK_NONE if not found S32 enableCommand(const LLCommandId& commandId, bool enabled); S32 stopCommandInProgress(const LLCommandId& commandId); - S32 flashCommand(const LLCommandId& commandId, bool flash); + S32 flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing = false); // Loads the toolbars from the existing user or default settings bool loadToolbars(bool force_default = false); // return false if load fails diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index ace16396db..61353dba7d 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -259,11 +259,15 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) break; } - LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); - modified_form->setElementEnabled("Accept", false); - modified_form->setElementEnabled("Decline", false); - notification_ptr->updateForm(modified_form); - notification_ptr->repost(); + // TODO: this set of calls has undesirable behavior under Windows OS (CHUI-985): + // here appears three additional toasts instead one modified + // need investigation and fix + + // LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + // modified_form->setElementEnabled("Accept", false); + // modified_form->setElementEnabled("Decline", false); + // notification_ptr->updateForm(modified_form); + // notification_ptr->repost(); } return false; @@ -2649,7 +2653,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { send_do_not_disturb_message(msg, from_id); } - else + + if (!is_muted) { LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; // Read the binary bucket for more information. @@ -3673,6 +3678,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) LLSD msg_notify = LLSD(LLSD::emptyMap()); msg_notify["session_id"] = LLUUID(); msg_notify["from_id"] = chat.mFromID; + msg_notify["source_type"] = chat.mSourceType; on_new_message(msg_notify); } } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 65a906d3c0..4c1d19f2f2 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1811,7 +1811,6 @@ void LLViewerWindow::initBase() gFloaterView = main_view->getChild<LLFloaterView>("Floater View"); gFloaterView->setFloaterSnapView(main_view->getChild<LLView>("floater_snap_region")->getHandle()); gSnapshotFloaterView = main_view->getChild<LLSnapshotFloaterView>("Snapshot Floater View"); - // Console llassert( !gConsole ); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index a9176595c7..f53995732f 100755 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -884,4 +884,19 @@ <color name="blue" value="0 0 1 1"/> + + <!--Resize bar colors --> + + <color + name="ResizebarBorderLight" + value="0.231 0.231 0.231 1"/> + + <color + name="ResizebarBorderDark" + value="0.133 0.133 0.133 1"/> + + <color + name="ResizebarBody" + value="0.208 0.208 0.208 1"/> + </colors> diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index b0e4b71d21..54f60f4441 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -774,4 +774,7 @@ with the same filename but different name <texture name="Popup_Caution" file_name="icons/pop_up_caution.png"/> <texture name="Camera_Drag_Dot" file_name="world/CameraDragDot.png"/> <texture name="NavBar Separator" file_name="navbar/separator.png"/> + + <texture name="Horizontal Drag Handle" file_name="widgets/horizontal_drag_handle.png"/> + <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png"/> </textures> diff --git a/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png Binary files differnew file mode 100644 index 0000000000..642eac4065 --- /dev/null +++ b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png diff --git a/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png Binary files differnew file mode 100644 index 0000000000..b06b70cf36 --- /dev/null +++ b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png 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 65f623a47e..1215efb7f9 100755 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -4,6 +4,7 @@ can_minimize="true" can_resize="true" height="210" + min_height="210" layout="topleft" name="floater_im_box" help_topic="floater_im_box" @@ -24,24 +25,28 @@ value="Conv_toolbar_expand"/> <layout_stack animate="true" - bottom="-1" + bottom="-5" + drag_handle_gap="6" + drag_handle_first_indent="27" + drag_handle_second_indent="10" follows="all" layout="topleft" left="0" name="conversations_stack" orientation="horizontal" right="-1" + show_drag_handle="true" top="0"> <layout_panel auto_resize="false" user_resize="true" name="conversations_layout_panel" min_dim="38" - expanded_min_dim="156"> + expanded_min_dim="136"> <layout_stack animate="false" follows="left|top|right" - height="35" + height="27" layout="topleft" left="0" name="conversations_pane_buttons_stack" @@ -50,7 +55,6 @@ top="0"> <layout_panel auto_resize="true" - height="35" name="conversations_pane_buttons_expanded"> <menu_button follows="top|left" @@ -64,7 +68,7 @@ left="5" name="sort_btn" tool_tip="View/sort options" - top="5" + top="1" width="31" /> <button follows="top|left" @@ -74,7 +78,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="add_btn" tool_tip="Start a new conversation" @@ -87,7 +91,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="speak_btn" tool_tip="Speak with people using your microphone" @@ -95,9 +99,8 @@ </layout_panel> <layout_panel auto_resize="false" - height="35" name="conversations_pane_buttons_collapsed" - width="41"> + width="31"> <button follows="right|top" height="25" @@ -106,8 +109,8 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" - left="1" + top="1" + left="0" name="expand_collapse_btn" tool_tip="Collapse/Expand this list" width="31" /> @@ -119,7 +122,7 @@ layout="topleft" name="conversations_list_panel" opaque="true" - top="35" + top_pad="0" left="5" right="-1"/> </layout_panel> @@ -127,7 +130,7 @@ auto_resize="true" user_resize="true" name="messages_layout_panel" - expanded_min_dim="222"> + expanded_min_dim="212"> <panel_container bottom="-1" follows="all" @@ -136,44 +139,44 @@ name="im_box_tab_container" right="-1" top="0"> - <panel - bottom="-1" - follows="all" - layout="topleft" - name="stub_panel" - opaque="true" - top_pad="0" - left="0" - right="-1"> - <button - follows="right|top" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="Conv_toolbar_collapse" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" + <panel + bottom="-1" + follows="all" layout="topleft" - top="5" - right="-10" - name="stub_collapse_btn" - tool_tip="Collapse this pane" - width="31" /> - <text - type="string" - clip_partial="false" - follows="left|top|right" - layout="topleft" - left="15" - right="-15" - name="stub_textbox" - top="25" - height="40" - valign="center" - parse_urls="true" - wrap="true"> - This conversation is in a separate window. [secondlife:/// Bring it back.] - </text> - </panel> + name="stub_panel" + opaque="true" + top_pad="0" + left="0" + right="-1"> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="1" + right="-10" + name="stub_collapse_btn" + tool_tip="Collapse this pane" + width="31" /> + <text + type="string" + clip_partial="false" + follows="left|top|right" + layout="topleft" + left="15" + right="-15" + name="stub_textbox" + top="25" + height="40" + valign="center" + parse_urls="true" + wrap="true"> + This conversation is in a separate window. [secondlife:/// Bring it back.] + </text> + </panel> </panel_container> </layout_panel> </layout_stack> 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 2152a9f6e9..43d0f2fb18 100755 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -70,26 +70,23 @@ top="0" left="0" right="-1" - bottom="-3"> + bottom="-1"> <layout_stack animate="false" + bottom="-1" default_tab_group="2" follows="all" - right="-5" - bottom="-1" - top="0" - left="5" - border_size="0" + left="3" layout="topleft" - orientation="vertical" name="main_stack" - tab_group="1"> + right="-3" + orientation="vertical" + tab_group="1" + top="0"> <layout_panel auto_resize="false" name="toolbar_panel" - height="35" - right="-1" - left="1"> + height="25"> <menu_button menu_filename="menu_im_session_showmodes.xml" follows="top|left" @@ -102,7 +99,7 @@ left="5" name="view_options_btn" tool_tip="View/sort options" - top="5" + top="1" width="31" /> <menu_button menu_filename="menu_im_conversation.xml" @@ -113,7 +110,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="gear_btn" visible="false" @@ -128,7 +125,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="add_btn" tool_tip="Add someone to this conversation" @@ -141,23 +138,11 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="voice_call_btn" tool_tip="Open voice connection" - width="31"/> - <output_monitor - auto_update="true" - follows="top|left" - draw_border="false" - height="16" - layout="topleft" - top="10" - left_pad="10" - mouse_opaque="true" - name="speaking_indicator" - visible="false" - width="20" /> + width="31"/> <button follows="right|top" height="25" @@ -166,8 +151,8 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" - right="-67" + top="1" + right="-70" name="close_btn" tool_tip="End this conversation" width="31" /> @@ -179,7 +164,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="expand_collapse_btn" tool_tip="Collapse/Expand this pane" @@ -194,18 +179,21 @@ layout="topleft" left_pad="2" name="tear_off_btn" - top="5" + top="1" width="31" /> </layout_panel> <layout_panel name="body_panel" - top="1" - bottom="-1"> + height="235"> <layout_stack default_tab_group="2" + drag_handle_gap="6" + drag_handle_first_indent="0" + drag_handle_second_indent="1" follows="all" orientation="horizontal" name="im_panels" + show_drag_handle="true" tab_group="1" top="0" right="-1" @@ -217,14 +205,12 @@ min_dim="0" width="150" user_resize="true" - auto_resize="false" - bottom="-1" /> + auto_resize="false" /> <layout_panel default_tab_group="3" tab_group="2" name="right_part_holder" - min_width="221" - bottom="-1"> + min_width="172"> <layout_stack animate="true" default_tab_group="2" @@ -233,7 +219,7 @@ name="translate_and_chat_stack" tab_group="1" top="0" - left="0" + left="1" right="-1" bottom="-1"> <layout_panel @@ -259,7 +245,7 @@ parse_highlights="true" parse_urls="true" right="-1" - left="5" + left="0" top="0" bottom="-1" /> </layout_panel> @@ -268,10 +254,7 @@ </layout_stack> </layout_panel> <layout_panel - top_delta="0" - top="0" - height="26" - bottom="-1" + height="35" auto_resize="false" name="chat_layout_panel"> <layout_stack @@ -281,15 +264,11 @@ orientation="horizontal" name="input_panels" top="0" - bottom="-2" + bottom="-1" left="0" right="-1"> <layout_panel - name="input_editor_layout_panel" - auto_resize="true" - user_resize="false" - top="0" - bottom="-1"> + name="input_editor_layout_panel"> <chat_editor layout="topleft" expand_lines_count="5" @@ -302,32 +281,27 @@ max_length="1023" spellcheck="true" tab_group="3" - top="1" - bottom="-2" - left="4" - right="-4" + bottom="-8" + left="5" + right="-5" wrap="true" /> </layout_panel> <layout_panel auto_resize="false" - user_resize="false" name="input_button_layout_panel" - width="30" - top="0" - bottom="-1"> + width="32"> <button - layout="topleft" left="1" - right="-1" - top="1" - height="22" + top="4" + height="25" follows="left|right|top" image_hover_unselected="Toolbar_Middle_Over" image_overlay="Conv_expand_one_line" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" name="minz_btn" - tool_tip="Shows/hides message panel" /> + tool_tip="Shows/hides message panel" + width="28" /> </layout_panel> </layout_stack> </layout_panel> diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml index 5a13ef0a59..b3d28788da 100755 --- a/indra/newview/skins/default/xui/en/menu_conversation.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -11,7 +11,7 @@ layout="topleft" name="close_conversation"> <on_click function="Avatar.DoToSelected" parameter="close_conversation"/> - </menu_item_call> + </menu_item_call> <menu_item_call label="Open voice conversation" layout="topleft" @@ -25,6 +25,12 @@ <on_click function="Avatar.DoToSelected" parameter="disconnect_from_voice"/> </menu_item_call> <menu_item_separator layout="topleft" name="separator_disconnect_from_voice"/> + <menu_item_call + label="Close Selected" + layout="topleft" + name="close_selected_conversations"> + <on_click function="Avatar.DoToSelected" parameter="close_selected_conversations"/> + </menu_item_call> <menu_item_call label="View Profile" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml index dde9432867..8790fde7c5 100755 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml @@ -44,6 +44,8 @@ <menu_item_check.on_check function="Floater.Visible" parameter="conversation" /> + <menu_item_check.on_enable + function="Conversation.IsConversationLoggingAllowed" /> <menu_item_check.on_click function="Floater.Toggle" parameter="conversation" /> diff --git a/indra/newview/skins/default/xui/en/menu_url_agent.xml b/indra/newview/skins/default/xui/en/menu_url_agent.xml index 7cd56f257a..e8b6116026 100755 --- a/indra/newview/skins/default/xui/en/menu_url_agent.xml +++ b/indra/newview/skins/default/xui/en/menu_url_agent.xml @@ -21,8 +21,15 @@ layout="topleft" name="add_friend"> <menu_item_call.on_click - function="Url.AddFriend" /> + function="Url.AddFriend" /> </menu_item_call> + <menu_item_call + label="Remove Friend..." + layout="topleft" + name="remove_friend"> + <menu_item_call.on_click + function="Url.RemoveFriend" /> + </menu_item_call> <menu_item_separator layout="topleft" /> <menu_item_call diff --git a/indra/newview/skins/default/xui/en/menu_url_objectim.xml b/indra/newview/skins/default/xui/en/menu_url_objectim.xml index 87ab58e622..b9d003b841 100755 --- a/indra/newview/skins/default/xui/en/menu_url_objectim.xml +++ b/indra/newview/skins/default/xui/en/menu_url_objectim.xml @@ -9,6 +9,13 @@ <menu_item_call.on_click function="Url.Execute" /> </menu_item_call> + <menu_item_call + label="Block..." + layout="topleft" + name="block_object"> + <menu_item_call.on_click + function="Url.Block" /> + </menu_item_call> <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 b01c3067ff..f253ed3e06 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -874,6 +874,15 @@ function="Tools.EnableSelectNextPart" /> </menu_item_call> </menu> + <menu_item_call + label="Linksets..." + name="pathfinding_linkset_menu_item"> + <menu_item_call.on_click + function="Floater.ToggleOrBringToFront" + parameter="pathfinding_linksets" /> + <menu_item_call.on_enable + function="Tools.EnablePathfinding" /> + </menu_item_call> <menu_item_separator/> <menu_item_call @@ -896,7 +905,7 @@ <menu_item_call.on_enable function="Tools.SomethingSelectedNoHUD" /> </menu_item_call> - + <menu_item_separator/> <menu diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9e582cf0de..597f57eb0a 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3709,6 +3709,17 @@ Leave Group? </notification> <notification + icon="alertmodal.tga" + name="OwnerCannotLeaveGroup" + type="alertmodal"> + Unable to leave group. You cannot leave the group because you are the last owner of the group. Please assign another member to the owner role first. + <tag>group</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification icon="alert.tga" name="ConfirmKick" type="alert"> @@ -5943,9 +5954,7 @@ Your calling card was declined. icon="notifytip.tga" name="TeleportToPerson" type="notifytip"> - To contact Residents like '[NAME]', click on the "People" button , select a Resident from the window that opens, then click 'IM' at the - bottom of the window. - (You can also double-click on their name in the list, or right-click and choose 'IM'). + To open a private conversation with someone, right-click on their avatar and choose 'IM' from the menu. </notification> <notification @@ -6573,7 +6582,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th icon="notify.tga" name="JoinGroup" persist="true" - type="notify"> + type="offer"> <tag>group</tag> [MESSAGE] <form name="form"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index bd096ebb88..8e867259c5 100755 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -14,6 +14,7 @@ border="false" height="60" layout="topleft" + name="general_chat_settings" top="10" left="13" width="517"> @@ -97,20 +98,13 @@ border="false" height="165" layout="topleft" + name="im_notification_settings" left="13" width="517"> <text layout="topleft" height="12" - name="notifications" - left="0" - width="120"> - Notifications - </text> - <text - layout="topleft" - height="12" name="friend_ims" width="145" left="0" @@ -138,17 +132,27 @@ name="FlashToolbarButton" value="flash"/> <item - label="None" - name="None" - value="none"/> + label="No action" + name="NoAction" + value="noaction"/> </combo_box> + <check_box + control_name="PlaySoundFriendIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_friend_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> <text layout="topleft" height="12" name="non_friend_ims" width="145" left="0" - top_pad="9"> + top_pad="11"> Non-friend IMs: </text> <combo_box @@ -172,17 +176,27 @@ name="FlashToolbarButton" value="flash"/> <item - label="None" - name="None" - value="none"/> + label="No action" + name="NoAction" + value="noaction"/> </combo_box> + <check_box + control_name="PlaySoundNonFriendIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_non_friend_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> <text layout="topleft" left="0" height="13" name="conference_ims" width="145" - top_pad="9"> + top_pad="11"> Conference IMs: </text> <combo_box @@ -206,17 +220,27 @@ name="FlashToolbarButton" value="flash"/> <item - label="None" - name="None" - value="none"/> + label="No action" + name="NoAction" + value="noaction"/> </combo_box> + <check_box + control_name="PlaySoundConferenceIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_conference_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> <text layout="topleft" left="0" height="13" name="group_chat" width="145" - top_pad="9"> + top_pad="11"> Group chat: </text> <combo_box @@ -240,17 +264,27 @@ name="FlashToolbarButton" value="flash"/> <item - label="None" - name="None" - value="none"/> + label="No action" + name="NoAction" + value="noaction"/> </combo_box> + <check_box + control_name="PlaySoundGroupChatIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_group_chat_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> <text layout="topleft" left="0" height="12" name="nearby_chat" width="145" - top_pad="9"> + top_pad="11"> Nearby chat: </text> <combo_box @@ -274,10 +308,64 @@ name="FlashToolBarButton" value="flash"/> <item - label="None" - name="None" - value="none"/> + label="No action" + name="NoAction" + value="noaction"/> </combo_box> + <check_box + control_name="PlaySoundNearbyChatIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_nearby_chat_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> + <text + layout="topleft" + left="0" + height="12" + name="object_ims" + width="145" + top_pad="11"> + Object IMs: + </text> + <combo_box + control_name="NotificationObjectIMOptions" + height="23" + layout="topleft" + left_pad="5" + top_delta="-6" + name="ObjectIMOptions" + width="223"> + <item + label="Open Conversations window" + name="OpenConversationsWindow" + value="openconversations"/> + <item + label="Pop up the message" + name="PopUpMessage" + value="toast"/> + <item + label="Flash toolbar button" + name="FlashToolBarButton" + value="flash"/> + <item + label="No action" + name="NoAction" + value="noaction"/> + </combo_box> + <check_box + control_name="PlaySoundObjectIM" + height="23" + label="Play sound" + layout="topleft" + name="play_sound_object_im" + left_pad="7" + top_delta="-3" + width="28"> + </check_box> <text layout="topleft" left="0" @@ -296,6 +384,7 @@ border="false" height="50" layout="topleft" + name="play_sound_settings" left="13" top_pad="10" width="517"> @@ -358,6 +447,7 @@ <panel height="50" layout="topleft" + name="log_settings" left="13" top_pad="10" width="505"> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index f7b33b0a4a..f1e551695f 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -209,7 +209,8 @@ Please try logging in again in a minute.</string> <string name="SLappAgentIM">IM</string> <string name="SLappAgentPay">Pay</string> <string name="SLappAgentOfferTeleport">Offer Teleport to </string> - <string name="SLappAgentRequestFriend">Friend Request </string> + <string name="SLappAgentRequestFriend">Friend Request</string> + <string name="SLappAgentRemoveFriend">Friend Removal</string> <!-- ButtonToolTips, llfloater.cpp --> <string name="BUTTON_CLOSE_DARWIN">Close (⌘W)</string> @@ -3927,5 +3928,8 @@ Try enclosing path to the editor with double quotes. <string name="logging_calls_enabled_log_empty"> There are no logged conversations. After you contact someone, or someone contacts you, a log entry will be shown here. </string> + <string name="loading_chat_logs"> + Loading... + </string> </strings> |