summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/llstring.cpp9
-rw-r--r--indra/llui/llfloater.cpp2
-rw-r--r--indra/llui/llfloater.h2
-rw-r--r--indra/media_plugins/gstreamer010/CMakeLists.txt4
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llavatariconctrl.cpp4
-rw-r--r--indra/newview/llavatarlistitem.cpp23
-rw-r--r--indra/newview/llavatarlistitem.h23
-rw-r--r--indra/newview/llbottomtray.cpp93
-rw-r--r--indra/newview/llbottomtray.h4
-rw-r--r--indra/newview/llcallfloater.cpp96
-rw-r--r--indra/newview/llcallfloater.h10
-rw-r--r--indra/newview/llchathistory.cpp19
-rw-r--r--indra/newview/llchiclet.cpp119
-rw-r--r--indra/newview/llchiclet.h83
-rw-r--r--indra/newview/llfloaterpreference.cpp3
-rw-r--r--indra/newview/llgroupactions.cpp6
-rw-r--r--indra/newview/llimfloater.cpp180
-rw-r--r--indra/newview/llimfloater.h14
-rw-r--r--indra/newview/llimview.cpp48
-rw-r--r--indra/newview/llimview.h11
-rw-r--r--indra/newview/lllogchat.cpp250
-rw-r--r--indra/newview/lllogchat.h55
-rw-r--r--indra/newview/llnearbychat.cpp26
-rw-r--r--indra/newview/llnearbychat.h8
-rw-r--r--indra/newview/llnotificationhandlerutil.cpp5
-rw-r--r--indra/newview/llnotificationofferhandler.cpp49
-rw-r--r--indra/newview/llpanelavatar.cpp19
-rw-r--r--indra/newview/llpanelavatar.h4
-rw-r--r--indra/newview/llpanelgroupnotices.cpp3
-rw-r--r--indra/newview/llpanellandmarks.cpp149
-rw-r--r--indra/newview/llpanellandmarks.h21
-rw-r--r--indra/newview/llpanelpick.cpp5
-rw-r--r--indra/newview/llpanelpicks.cpp43
-rw-r--r--indra/newview/llpanelpicks.h3
-rw-r--r--indra/newview/llpanelplaces.cpp15
-rw-r--r--indra/newview/llpanelplaces.h4
-rw-r--r--indra/newview/llpanelplacestab.cpp9
-rw-r--r--indra/newview/llpanelplacestab.h7
-rw-r--r--indra/newview/llpanelteleporthistory.cpp9
-rw-r--r--indra/newview/llparticipantlist.cpp10
-rw-r--r--indra/newview/llparticipantlist.h5
-rw-r--r--indra/newview/llscriptfloater.cpp14
-rw-r--r--indra/newview/llstartup.cpp3
-rw-r--r--indra/newview/llviewerinventory.cpp5
-rw-r--r--indra/newview/llviewermessage.cpp55
-rw-r--r--indra/newview/llvoicechannel.cpp6
-rw-r--r--indra/newview/skins/default/xui/en/floater_im_container.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_nearby_chat.xml8
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_controls.xml43
-rw-r--r--indra/newview/skins/default/xui/en/menu_bottomtray.xml53
-rw-r--r--indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml19
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml7
-rw-r--r--indra/newview/skins/default/xui/en/panel_chat_header.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_picks.xml11
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_chat.xml33
56 files changed, 1369 insertions, 356 deletions
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index c027aa7bdd..5f3d9d6582 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -985,6 +985,15 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
return true;
}
replacement = datetime.toHTTPDateString(code);
+
+ // *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format
+ // to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738).
+ // We could have used '%l' format instead, but it's not supported by Windows.
+ if(code == "%I" && token == "hour12" && replacement.at(0) == '0')
+ {
+ replacement = replacement.at(1);
+ }
+
return !code.empty();
}
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 36a9e0a650..a63187678e 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1452,6 +1452,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
gFloaterView->adjustToFitScreen(self, FALSE);
// give focus to new window to keep continuity for the user
self->setFocus(TRUE);
+ self->setTornOff(true);
}
else //Attach to parent.
{
@@ -1463,6 +1464,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
// make sure host is visible
new_host->openFloater(new_host->getKey());
}
+ self->setTornOff(false);
}
}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index d7ec0aac00..b5c835cb47 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -259,6 +259,8 @@ public:
bool isDocked() const { return mDocked; }
virtual void setDocked(bool docked, bool pop_on_undock = true);
+ virtual void setTornOff(bool torn_off) {}
+
// Return a closeable floater, if any, given the current focus.
static LLFloater* getClosableFloaterFromFocus();
diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt
index a9f7938b41..3b73e04786 100644
--- a/indra/media_plugins/gstreamer010/CMakeLists.txt
+++ b/indra/media_plugins/gstreamer010/CMakeLists.txt
@@ -42,12 +42,12 @@ set(media_plugin_gstreamer010_HEADER_FILES
llmediaimplgstreamertriviallogging.h
)
-if (${CXX_VERSION} MATCHES "4[23].")
+if (${CXX_VERSION_NUMBER} MATCHES "4[23].")
# Work around a bad interaction between broken gstreamer headers and
# g++ 4.3's increased strictness.
set_source_files_properties(llmediaimplgstreamervidplug.cpp PROPERTIES
COMPILE_FLAGS -Wno-write-strings)
-endif (${CXX_VERSION} MATCHES "4[23].")
+endif (${CXX_VERSION_NUMBER} MATCHES "4[23].")
add_library(media_plugin_gstreamer010
SHARED
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 63e17058e8..6de98642b1 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1385,6 +1385,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>ChatWindow</key>
+ <map>
+ <key>Comment</key>
+ <string>Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>CheesyBeacon</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp
index 8f3eba98a6..44cbbbb6b2 100644
--- a/indra/newview/llavatariconctrl.cpp
+++ b/indra/newview/llavatariconctrl.cpp
@@ -303,5 +303,9 @@ void LLAvatarIconCtrl::nameUpdatedCallback(
{
setToolTip(mFirstName + " " + mLastName);
}
+ else
+ {
+ setToolTip(std::string(""));
+ }
}
}
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index 3bee5c353f..59ed391c06 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -42,7 +42,7 @@
#include "llavatariconctrl.h"
#include "llbutton.h"
-LLAvatarListItem::LLAvatarListItem()
+LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/)
: LLPanel(),
mAvatarIcon(NULL),
mAvatarName(NULL),
@@ -55,14 +55,12 @@ LLAvatarListItem::LLAvatarListItem()
mShowInfoBtn(true),
mShowProfileBtn(true)
{
- LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml");
- // Remember avatar icon width including its padding from the name text box,
- // so that we can hide and show the icon again later.
-
- mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft;
- mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight;
- mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight;
- mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight;
+ if (not_from_ui_factory)
+ {
+ LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml");
+ }
+ // *NOTE: mantipov: do not use any member here. They can be uninitialized here in case instance
+ // is created from the UICtrlFactory
}
LLAvatarListItem::~LLAvatarListItem()
@@ -87,6 +85,13 @@ BOOL LLAvatarListItem::postBuild()
mProfileBtn->setVisible(false);
mProfileBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onProfileBtnClick, this));
+ // Remember avatar icon width including its padding from the name text box,
+ // so that we can hide and show the icon again later.
+ mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft;
+ mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight;
+ mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight;
+ mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight;
+
/*
if(!p.buttons.profile)
{
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index 341f5a6bcf..a7b080098d 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -51,7 +51,16 @@ public:
virtual void show(LLView* spawning_view, const std::vector<LLUUID>& selected_uuids, S32 x, S32 y) = 0;
};
- LLAvatarListItem();
+ /**
+ * Creates an instance of LLAvatarListItem.
+ *
+ * It is not registered with LLDefaultChildRegistry. It is built via LLUICtrlFactory::buildPanel
+ * or via registered LLCallbackMap depend on passed parameter.
+ *
+ * @param not_from_ui_factory if true instance will be build with LLUICtrlFactory::buildPanel
+ * otherwise it should be registered via LLCallbackMap before creating.
+ */
+ LLAvatarListItem(bool not_from_ui_factory = true);
virtual ~LLAvatarListItem();
virtual BOOL postBuild();
@@ -82,8 +91,19 @@ public:
void setContextMenu(ContextMenu* menu) { mContextMenu = menu; }
+ /**
+ * This method was added to fix EXT-2364 (Items in group/ad-hoc IM participant list (avatar names) should be reshaped when adding/removing the "(Moderator)" label)
+ * But this is a *HACK. The real reason of it was in incorrect logic while hiding profile/info/speaker buttons
+ * *TODO: new reshape method should be provided in lieu of this one to be called when visibility if those buttons is changed
+ */
void reshapeAvatarName();
+protected:
+ /**
+ * Contains indicator to show voice activity.
+ */
+ LLOutputMonitorCtrl* mSpeakingIndicator;
+
private:
typedef enum e_online_status {
@@ -100,7 +120,6 @@ private:
LLTextBox* mAvatarName;
LLTextBox* mLastInteractionTime;
- LLOutputMonitorCtrl* mSpeakingIndicator;
LLButton* mInfoBtn;
LLButton* mProfileBtn;
ContextMenu* mContextMenu;
diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp
index c44b0b5331..96c06b1665 100644
--- a/indra/newview/llbottomtray.cpp
+++ b/indra/newview/llbottomtray.cpp
@@ -47,6 +47,7 @@
#include "llsplitbutton.h"
#include "llsyswellwindow.h"
#include "llfloatercamera.h"
+#include "lltexteditor.h"
// Build time optimization, generate extern template once in .cpp file
template class LLBottomTray* LLSingleton<class LLBottomTray>::getInstance();
@@ -259,7 +260,9 @@ void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask)
// We should show BottomTrayContextMenu in last turn
if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu())
{
- //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu
+ //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu
+
+ updateContextMenu(x, y, mask);
mBottomTrayContextMenu->buildDrawLabels();
mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer);
LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y);
@@ -267,6 +270,33 @@ void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask)
}
}
+void LLBottomTray::updateContextMenu(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl* edit_box = mNearbyChatBar->getChild<LLUICtrl>("chat_box");
+
+ S32 local_x = x - mNearbyChatBar->getRect().mLeft - edit_box->getRect().mLeft;
+ S32 local_y = y - mNearbyChatBar->getRect().mBottom - edit_box->getRect().mBottom;
+
+ bool in_edit_box = edit_box->pointInView(local_x, local_y);
+
+ LLMenuItemGL* menu_item;
+ menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Cut");
+ if(menu_item)
+ menu_item->setVisible(in_edit_box);
+ menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Copy");
+ if(menu_item)
+ menu_item->setVisible(in_edit_box);
+ menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Paste");
+ if(menu_item)
+ menu_item->setVisible(in_edit_box);
+ menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Delete");
+ if(menu_item)
+ menu_item->setVisible(in_edit_box);
+ menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Select_All");
+ if(menu_item)
+ menu_item->setVisible(in_edit_box);
+}
+
void LLBottomTray::showGestureButton(BOOL visible)
{
setTrayButtonVisibleIfPossible(RS_BUTTON_GESTURES, visible);
@@ -298,9 +328,14 @@ namespace
BOOL LLBottomTray::postBuild()
{
+
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("NearbyChatBar.Action", boost::bind(&LLBottomTray::onContextMenuItemClicked, this, _2));
+ LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("NearbyChatBar.EnableMenuItem", boost::bind(&LLBottomTray::onContextMenuItemEnabled, this, _2));
+
mBottomTrayContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
gMenuHolder->addChild(mBottomTrayContextMenu);
+
mNearbyChatBar = getChild<LLNearbyChatBar>("chat_bar");
mToolbarStack = getChild<LLLayoutStack>("toolbar_stack");
mMovementPanel = getChild<LLPanel>("movement_panel");
@@ -333,6 +368,62 @@ BOOL LLBottomTray::postBuild()
return TRUE;
}
+bool LLBottomTray::onContextMenuItemEnabled(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ LLLineEditor* edit_box = mNearbyChatBar->findChild<LLLineEditor>("chat_box");
+
+ if (item == "can_cut")
+ {
+ return edit_box->canCut();
+ }
+ else if (item == "can_copy")
+ {
+ return edit_box->canCopy();
+ }
+ else if (item == "can_paste")
+ {
+ return edit_box->canPaste();
+ }
+ else if (item == "can_delete")
+ {
+ return edit_box->canDoDelete();
+ }
+ else if (item == "can_select_all")
+ {
+ return edit_box->canSelectAll();
+ }
+ return true;
+}
+
+
+void LLBottomTray::onContextMenuItemClicked(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ LLLineEditor* edit_box = mNearbyChatBar->findChild<LLLineEditor>("chat_box");
+
+ if (item == "cut")
+ {
+ edit_box->cut();
+ }
+ else if (item == "copy")
+ {
+ edit_box->copy();
+ }
+ else if (item == "paste")
+ {
+ edit_box->paste();
+ }
+ else if (item == "delete")
+ {
+ edit_box->doDelete();
+ }
+ else if (item == "select_all")
+ {
+ edit_box->selectAll();
+ }
+}
+
void LLBottomTray::log(LLView* panel, const std::string& descr)
{
if (NULL == panel) return;
diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h
index 728a420324..91ec01654b 100644
--- a/indra/newview/llbottomtray.h
+++ b/indra/newview/llbottomtray.h
@@ -179,6 +179,10 @@ protected:
static void* createNearbyChatBar(void* userdata);
+ void updateContextMenu(S32 x, S32 y, MASK mask);
+ void onContextMenuItemClicked(const LLSD& userdata);
+ bool onContextMenuItemEnabled(const LLSD& userdata);
+
/**
* Creates IM Chiclet based on session type (IM chat or Group chat)
*/
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
index 1b929eca0e..ad59c780f3 100644
--- a/indra/newview/llcallfloater.cpp
+++ b/indra/newview/llcallfloater.cpp
@@ -42,18 +42,50 @@
#include "llspeakers.h"
+class LLNonAvatarCaller : public LLAvatarListItem
+{
+public:
+ LLNonAvatarCaller() : LLAvatarListItem(false)
+ {
+
+ }
+ BOOL postBuild()
+ {
+ BOOL rv = LLAvatarListItem::postBuild();
+
+ if (rv)
+ {
+ setOnline(true);
+ showLastInteractionTime(false);
+ setShowProfileBtn(false);
+ setShowInfoBtn(false);
+ }
+ return rv;
+ }
+
+ void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); }
+};
+
+
+static void* create_non_avatar_caller(void*)
+{
+ return new LLNonAvatarCaller;
+}
+
LLCallFloater::LLCallFloater(const LLSD& key)
: LLDockableFloater(NULL, key)
, mSpeakerManager(NULL)
, mPaticipants(NULL)
, mAvatarList(NULL)
+, mNonAvatarCaller(NULL)
, mVoiceType(VC_LOCAL_CHAT)
{
-
+ mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
}
LLCallFloater::~LLCallFloater()
{
+ mChannelChangedConnection.disconnect();
delete mPaticipants;
mPaticipants = NULL;
}
@@ -63,7 +95,9 @@ BOOL LLCallFloater::postBuild()
{
LLDockableFloater::postBuild();
mAvatarList = getChild<LLAvatarList>("speakers_list");
+ childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
+ mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller");
LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_panel");
@@ -77,7 +111,7 @@ BOOL LLCallFloater::postBuild()
updateSession();
// subscribe to to be notified Voice Channel is changed
- LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::onCurrentChannelChanged, this, _1));
+ mChannelChangedConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::onCurrentChannelChanged, this, _1));
return TRUE;
}
@@ -89,6 +123,16 @@ void LLCallFloater::onOpen(const LLSD& /*key*/)
//////////////////////////////////////////////////////////////////////////
/// PRIVATE SECTION
//////////////////////////////////////////////////////////////////////////
+
+void LLCallFloater::leaveCall()
+{
+ LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
+ if (voice_channel && voice_channel->isActive())
+ {
+ voice_channel->deactivate();
+ }
+}
+
void LLCallFloater::updateSession()
{
LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
@@ -138,25 +182,51 @@ void LLCallFloater::updateSession()
}
updateTitle();
+
+ //hide "Leave Call" button for nearby chat
+ bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
+ childSetVisible("leave_btn_panel", !is_local_chat);
+
refreshPartisipantList();
}
void LLCallFloater::refreshPartisipantList()
{
delete mPaticipants;
+ mPaticipants = NULL;
mAvatarList->clear();
- bool do_not_use_context_menu_in_local_chat = LLLocalSpeakerMgr::getInstance() != mSpeakerManager;
- mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, do_not_use_context_menu_in_local_chat);
+ bool non_avatar_caller = false;
+ if (VC_PEER_TO_PEER == mVoiceType)
+ {
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID());
+ non_avatar_caller = !session->mOtherParticipantIsAvatar;
+ if (non_avatar_caller)
+ {
+ mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
+ mNonAvatarCaller->setName(session->mName);
+ }
+ }
+
+ mNonAvatarCaller->setVisible(non_avatar_caller);
+ mAvatarList->setVisible(!non_avatar_caller);
- if (!do_not_use_context_menu_in_local_chat)
+ if (!non_avatar_caller)
{
- mAvatarList->setNoItemsCommentText(getString("no_one_near"));
+ bool do_not_use_context_menu_in_local_chat = LLLocalSpeakerMgr::getInstance() != mSpeakerManager;
+ mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, do_not_use_context_menu_in_local_chat);
+
+ if (!do_not_use_context_menu_in_local_chat)
+ {
+ mAvatarList->setNoItemsCommentText(getString("no_one_near"));
+ }
}
}
void LLCallFloater::onCurrentChannelChanged(const LLUUID& /*session_id*/)
{
+ // Forget speaker manager from the previous session to avoid using it after session was destroyed.
+ mSpeakerManager = NULL;
updateSession();
}
@@ -170,15 +240,21 @@ void LLCallFloater::updateTitle()
title = getString("title_nearby");
break;
case VC_PEER_TO_PEER:
- title = voice_channel->getSessionName();
+ {
+ LLStringUtil::format_map_t args;
+ args["[NAME]"] = voice_channel->getSessionName();
+ title = getString("title_peer_2_peer", args);
+ }
break;
case VC_AD_HOC_CHAT:
title = getString("title_adhoc");
break;
case VC_GROUP_CHAT:
- LLStringUtil::format_map_t args;
- args["[GROUP]"] = voice_channel->getSessionName();
- title = getString("title_group", args);
+ {
+ LLStringUtil::format_map_t args;
+ args["[GROUP]"] = voice_channel->getSessionName();
+ title = getString("title_group", args);
+ }
break;
}
diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h
index 8a440873ff..b615f57d5b 100644
--- a/indra/newview/llcallfloater.h
+++ b/indra/newview/llcallfloater.h
@@ -37,6 +37,7 @@
#include "lldockablefloater.h"
class LLAvatarList;
+class LLNonAvatarCaller;
class LLParticipantList;
class LLSpeakerMgr;
@@ -48,8 +49,8 @@ class LLSpeakerMgr;
* the Resident's own microphone input volume, the audible volume of each of the other participants,
* the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording.
*
- * When the Resident is engaged in Group Voice Chat, the Voice Control Panel also provides an
- * 'End Call' button to allow the Resident to leave that voice channel.
+ * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an
+ * 'Leave Call' button to allow the Resident to leave that voice channel.
*/
class LLCallFloater : public LLDockableFloater
{
@@ -69,6 +70,8 @@ private:
VC_PEER_TO_PEER
}EVoiceControls;
+ void leaveCall();
+
/**
* Updates mSpeakerManager and list according to current Voice Channel
*
@@ -89,7 +92,10 @@ private:
LLSpeakerMgr* mSpeakerManager;
LLParticipantList* mPaticipants;
LLAvatarList* mAvatarList;
+ LLNonAvatarCaller* mNonAvatarCaller;
EVoiceControls mVoiceType;
+
+ boost::signals2::connection mChannelChangedConnection;
};
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 6e1bb961a5..5e17770314 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -267,30 +267,13 @@ protected:
}
private:
- std::string appendTime(const LLChat& chat)
- {
- time_t utc_time;
- utc_time = time_corrected();
- std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:["
- +LLTrans::getString("TimeMin")+"] ";
-
- LLSD substitution;
-
- substitution["datetime"] = (S32) utc_time;
- LLStringUtil::format (timeStr, substitution);
-
- return timeStr;
- }
-
void setTimeField(const LLChat& chat)
{
LLTextBox* time_box = getChild<LLTextBox>("time_box");
LLRect rect_before = time_box->getRect();
- std::string time_value = appendTime(chat);
-
- time_box->setValue(time_value);
+ time_box->setValue(chat.mTimeStr);
// set necessary textbox width to fit all text
time_box->reshapeToFitText();
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 9aef02c5c8..588855d088 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -595,8 +595,49 @@ void LLAdHocChiclet::setCounter(S32 counter)
setShowNewMessagesIcon(counter);
}
+void LLAdHocChiclet::createPopupMenu()
+{
+ if(mPopupMenu)
+ {
+ llwarns << "Menu already exists" << llendl;
+ return;
+ }
+ if(getSessionId().isNull())
+ {
+ return;
+ }
+
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("IMChicletMenu.Action", boost::bind(&LLAdHocChiclet::onMenuItemClicked, this, _2));
+
+ mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
+ ("menu_imchiclet_adhoc.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+}
+
+void LLAdHocChiclet::onMenuItemClicked(const LLSD& user_data)
+{
+ std::string level = user_data.asString();
+ LLUUID group_id = getSessionId();
+
+ if("end" == level)
+ {
+ LLGroupActions::endIM(group_id);
+ }
+}
+
BOOL LLAdHocChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
+ if(!mPopupMenu)
+ {
+ createPopupMenu();
+ }
+
+ if (mPopupMenu)
+ {
+ mPopupMenu->arrangeAndClear();
+ LLMenuGL::showPopup(this, mPopupMenu, x, y);
+ }
+
return TRUE;
}
@@ -1415,6 +1456,28 @@ void LLChicletGroupIconCtrl::setValue(const LLSD& value )
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
+LLChicletInvOfferIconCtrl::LLChicletInvOfferIconCtrl(const Params& p)
+: LLChicletAvatarIconCtrl(p)
+ , mDefaultIcon(p.default_icon)
+{
+}
+
+void LLChicletInvOfferIconCtrl::setValue(const LLSD& value )
+{
+ if(value.asUUID().isNull())
+ {
+ LLIconCtrl::setValue(mDefaultIcon);
+ }
+ else
+ {
+ LLChicletAvatarIconCtrl::setValue(value);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p)
: LLOutputMonitorCtrl(p)
{
@@ -1466,4 +1529,60 @@ BOOL LLScriptChiclet::handleMouseDown(S32 x, S32 y, MASK mask)
return LLChiclet::handleMouseDown(x, y, mask);
}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+static const std::string INVENTORY_USER_OFFER ("UserGiveItem");
+
+LLInvOfferChiclet::Params::Params()
+{
+ // *TODO Vadim: Get rid of hardcoded values.
+ rect(CHICLET_RECT);
+ icon.rect(CHICLET_ICON_RECT);
+}
+
+LLInvOfferChiclet::LLInvOfferChiclet(const Params&p)
+ : LLIMChiclet(p)
+ , mChicletIconCtrl(NULL)
+{
+ LLChicletInvOfferIconCtrl::Params icon_params = p.icon;
+ mChicletIconCtrl = LLUICtrlFactory::create<LLChicletInvOfferIconCtrl>(icon_params);
+ // Let "new message" icon be on top, else it will be hidden behind chiclet icon.
+ addChildInBack(mChicletIconCtrl);
+}
+
+void LLInvOfferChiclet::setSessionId(const LLUUID& session_id)
+{
+ setShowNewMessagesIcon( getSessionId() != session_id );
+
+ LLIMChiclet::setSessionId(session_id);
+ LLUUID notification_id = LLScriptFloaterManager::getInstance()->findNotificationId(session_id);
+ LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id);
+ if(notification)
+ {
+ setToolTip(notification->getSubstitutions()["TITLE"].asString());
+ }
+
+ if ( notification && notification->getName() == INVENTORY_USER_OFFER )
+ {
+ mChicletIconCtrl->setValue(notification->getPayload()["from_id"]);
+ }
+ else
+ {
+ mChicletIconCtrl->setValue(LLUUID::null);
+ }
+}
+
+void LLInvOfferChiclet::onMouseDown()
+{
+ LLScriptFloaterManager::instance().toggleScriptFloater(getSessionId());
+}
+
+BOOL LLInvOfferChiclet::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ onMouseDown();
+ return LLChiclet::handleMouseDown(x, y, mask);
+}
+
// EOF
diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h
index 1ea141e6c4..c75ad2b546 100644
--- a/indra/newview/llchiclet.h
+++ b/indra/newview/llchiclet.h
@@ -148,6 +148,39 @@ protected:
};
/**
+ * Class for displaying icon in inventory offer chiclet.
+ */
+class LLChicletInvOfferIconCtrl : public LLChicletAvatarIconCtrl
+{
+public:
+
+ struct Params :
+ public LLInitParam::Block<Params, LLChicletAvatarIconCtrl::Params>
+ {
+ Optional<std::string> default_icon;
+
+ Params()
+ : default_icon("default_icon", "Generic_Object_Small")
+ {
+ avatar_id = LLUUID::null;
+ };
+ };
+
+ /**
+ * Sets icon, if value is LLUUID::null - default icon will be set.
+ */
+ virtual void setValue(const LLSD& value );
+
+protected:
+
+ LLChicletInvOfferIconCtrl(const Params& p);
+ friend class LLUICtrlFactory;
+
+private:
+ std::string mDefaultIcon;
+};
+
+/**
* Class for displaying of speaker's voice indicator
*/
class LLChicletSpeakerCtrl : public LLOutputMonitorCtrl
@@ -518,6 +551,17 @@ protected:
friend class LLUICtrlFactory;
/**
+ * Creates chiclet popup menu. Will create AdHoc Chat menu
+ * based on other participant's id.
+ */
+ virtual void createPopupMenu();
+
+ /**
+ * Processes clicks on chiclet popup menu.
+ */
+ virtual void onMenuItemClicked(const LLSD& user_data);
+
+ /**
* Displays popup menu.
*/
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
@@ -574,6 +618,45 @@ private:
};
/**
+ * Chiclet for inventory offer script floaters.
+ */
+class LLInvOfferChiclet: public LLIMChiclet
+{
+public:
+
+ struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params>
+ {
+ Optional<LLChicletInvOfferIconCtrl::Params> icon;
+
+ Params();
+ };
+
+ /*virtual*/ void setSessionId(const LLUUID& session_id);
+
+ /*virtual*/ void setCounter(S32 counter){}
+
+ /*virtual*/ S32 getCounter() { return 0; }
+
+ /**
+ * Toggle script floater
+ */
+ /*virtual*/ void onMouseDown();
+
+ /**
+ * Override default handler
+ */
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+
+
+protected:
+ LLInvOfferChiclet(const Params&);
+ friend class LLUICtrlFactory;
+
+private:
+ LLChicletInvOfferIconCtrl* mChicletIconCtrl;
+};
+
+/**
* Implements Group chat chiclet.
*/
class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 83c784c1f7..5128a7b861 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -106,6 +106,7 @@
#include "llviewermedia.h"
#include "llpluginclassmedia.h"
#include "llteleporthistorystorage.h"
+#include "llnearbychat.h"
#include <boost/regex.hpp>
@@ -361,6 +362,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
BOOL LLFloaterPreference::postBuild()
{
gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2));
+
+ gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2));
LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core");
if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab")))
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index 4adefdfcaf..fdb2b886a6 100644
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -100,9 +100,9 @@ public:
}
if (tokens[1].asString() == "inspect")
{
- LLSD key;
- key["group_id"] = group_id;
- LLFloaterReg::showInstance("inspect_group", key);
+ if (group_id.isNull())
+ return true;
+ LLGroupActions::show(group_id);
return true;
}
return false;
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp
index 5e9ffdf410..9de0b1f827 100644
--- a/indra/newview/llimfloater.cpp
+++ b/indra/newview/llimfloater.cpp
@@ -44,6 +44,7 @@
#include "llchiclet.h"
#include "llfloaterchat.h"
#include "llfloaterreg.h"
+#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container
#include "lllineeditor.h"
#include "lllogchat.h"
#include "llpanelimcontrolpanel.h"
@@ -55,10 +56,6 @@
#include "lltransientfloatermgr.h"
#include "llinventorymodel.h"
-#ifdef USE_IM_CONTAINER
- #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container
-#endif
-
LLIMFloater::LLIMFloater(const LLUUID& session_id)
@@ -263,11 +260,14 @@ BOOL LLIMFloater::postBuild()
//*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla"
//see LLFloaterIMPanel for how it is done (IB)
-#ifdef USE_IM_CONTAINER
- return LLFloater::postBuild();
-#else
- return LLDockableFloater::postBuild();
-#endif
+ if(isChatMultiTab())
+ {
+ return LLFloater::postBuild();
+ }
+ else
+ {
+ return LLDockableFloater::postBuild();
+ }
}
// virtual
@@ -328,59 +328,69 @@ void LLIMFloater::onSlide()
//static
LLIMFloater* LLIMFloater::show(const LLUUID& session_id)
{
-#ifdef USE_IM_CONTAINER
- LLIMFloater* target_floater = findInstance(session_id);
- bool not_existed = NULL == target_floater;
+ bool not_existed = true;
-#else
- //hide all
- LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
- for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
- iter != inst_list.end(); ++iter)
+ if(isChatMultiTab())
{
- LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter);
- if (floater && floater->isDocked())
+ LLIMFloater* target_floater = findInstance(session_id);
+ not_existed = NULL == target_floater;
+ }
+ else
+ {
+ //hide all
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
+ iter != inst_list.end(); ++iter)
{
- floater->setVisible(false);
+ LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter);
+ if (floater && floater->isDocked())
+ {
+ floater->setVisible(false);
+ }
}
}
-#endif
LLIMFloater* floater = LLFloaterReg::showTypedInstance<LLIMFloater>("impanel", session_id);
- floater->updateMessages();
- floater->mInputEditor->setFocus(TRUE);
-
-#ifdef USE_IM_CONTAINER
- // do not add existed floaters to avoid adding torn off instances
- if (not_existed)
+ if(isChatMultiTab())
{
- // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
- // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists
- LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END;
+ // do not add existed floaters to avoid adding torn off instances
+ if (not_existed)
+ {
+ // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
+ // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists
+ LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END;
- LLIMFloaterContainer* floater_container = LLFloaterReg::showTypedInstance<LLIMFloaterContainer>("im_container");
- floater_container->addFloater(floater, TRUE, i_pt);
+ LLIMFloaterContainer* floater_container = LLFloaterReg::showTypedInstance<LLIMFloaterContainer>("im_container");
+ floater_container->addFloater(floater, TRUE, i_pt);
+ }
}
-#else
- if (floater->getDockControl() == NULL)
+ else
{
- LLChiclet* chiclet =
- LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>(
- session_id);
- if (chiclet == NULL)
- {
- llerror("Dock chiclet for LLIMFloater doesn't exists", 0);
- }
- else
+ // Docking may move chat window, hide it before moving, or user will see how window "jumps"
+ floater->setVisible(false);
+
+ if (floater->getDockControl() == NULL)
{
- LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet);
+ LLChiclet* chiclet =
+ LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>(
+ session_id);
+ if (chiclet == NULL)
+ {
+ llerror("Dock chiclet for LLIMFloater doesn't exists", 0);
+ }
+ else
+ {
+ LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet);
+ }
+
+ floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(),
+ LLDockControl::TOP, boost::bind(&LLIMFloater::getAllowedRect, floater, _1)));
}
- floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(),
- LLDockControl::TOP, boost::bind(&LLIMFloater::getAllowedRect, floater, _1)));
+ // window is positioned, now we can show it.
+ floater->setVisible(true);
}
-#endif
return floater;
}
@@ -397,9 +407,10 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock)
(LLNotificationsUI::LLChannelManager::getInstance()->
findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
-#ifndef USE_IM_CONTAINER
- LLTransientDockableFloater::setDocked(docked, pop_on_undock);
-#endif
+ if(!isChatMultiTab())
+ {
+ LLTransientDockableFloater::setDocked(docked, pop_on_undock);
+ }
// update notification channel state
if(channel)
@@ -408,6 +419,16 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock)
}
}
+void LLIMFloater::setTornOff(bool torn_off)
+{
+ // When IM Floater isn't torn off, "close" button should be hidden.
+ // This call will just disables it, since there is a hack in LLFloater::updateButton,
+ // which prevents hiding of close button in that case.
+ setCanClose(torn_off);
+
+ LLTransientDockableFloater::setTornOff(torn_off);
+}
+
void LLIMFloater::setVisible(BOOL visible)
{
LLNotificationsUI::LLScreenChannel* channel = dynamic_cast<LLNotificationsUI::LLScreenChannel*>
@@ -420,33 +441,39 @@ void LLIMFloater::setVisible(BOOL visible)
{
channel->updateShowToastsState();
}
+
+ if (visible && mChatHistory && mInputEditor)
+ {
+ //only if floater was construced and initialized from xml
+ updateMessages();
+ mInputEditor->setFocus(TRUE);
+ }
}
//static
bool LLIMFloater::toggle(const LLUUID& session_id)
{
-#ifndef USE_IM_CONTAINER
- LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id);
- if (floater && floater->getVisible() && floater->isDocked())
- {
- // clicking on chiclet to close floater just hides it to maintain existing
- // scroll/text entry state
- floater->setVisible(false);
- return false;
- }
- else if(floater && !floater->isDocked())
- {
- floater->setVisible(TRUE);
- floater->setFocus(TRUE);
- return true;
- }
- else
-#endif
+ if(!isChatMultiTab())
{
- // ensure the list of messages is updated when floater is made visible
- show(session_id);
- return true;
+ LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id);
+ if (floater && floater->getVisible() && floater->isDocked())
+ {
+ // clicking on chiclet to close floater just hides it to maintain existing
+ // scroll/text entry state
+ floater->setVisible(false);
+ return false;
+ }
+ else if(floater && !floater->isDocked())
+ {
+ floater->setVisible(TRUE);
+ floater->setFocus(TRUE);
+ return true;
+ }
}
+
+ // ensure the list of messages is updated when floater is made visible
+ show(session_id);
+ return true;
}
//static
@@ -892,3 +919,18 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info)
}
}
+// static
+bool LLIMFloater::isChatMultiTab()
+{
+ // Restart is required in order to change chat window type.
+ static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1;
+ return is_single_window;
+}
+
+// static
+void LLIMFloater::initIMFloater()
+{
+ // This is called on viewer start up
+ // init chat window type before user changed it in preferences
+ isChatMultiTab();
+}
diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h
index 9e1330ff49..f90bc35c34 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llimfloater.h
@@ -33,11 +33,6 @@
#ifndef LL_IMFLOATER_H
#define LL_IMFLOATER_H
-// This variable is used to show floaters related to chiclets in a Multi Floater Container
-// So, this functionality does not require to have IM Floaters as Dockable & Transient
-// See EXT-2640.
-#define USE_IM_CONTAINER
-
#include "lltransientdockablefloater.h"
#include "lllogchat.h"
#include "lltooldraganddrop.h"
@@ -68,6 +63,7 @@ public:
// LLFloater overrides
/*virtual*/ void onClose(bool app_quitting);
/*virtual*/ void setDocked(bool docked, bool pop_on_undock = true);
+ /*virtual*/ void setTornOff(bool torn_off);
// Make IM conversion visible and update the message history
static LLIMFloater* show(const LLUUID& session_id);
@@ -105,6 +101,14 @@ public:
void *cargo_data, EAcceptance *accept,
std::string& tooltip_msg);
+ /**
+ * Returns true if chat is displayed in multi tabbed floater
+ * false if chat is displayed in multiple windows
+ */
+ static bool isChatMultiTab();
+
+ static void initIMFloater();
+
private:
// process focus events to set a currently active session
/* virtual */ void onFocusLost();
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index be719c0a78..5481ca97cd 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -83,19 +83,16 @@
#include "llfirstuse.h"
#include "llagentui.h"
+const static std::string IM_TIME("time");
+const static std::string IM_TEXT("message");
+const static std::string IM_FROM("from");
+const static std::string IM_FROM_ID("from_id");
+
//
// Globals
//
LLIMMgr* gIMMgr = NULL;
-//
-// Statics
-//
-// *FIXME: make these all either UIStrings or Strings
-
-const static std::string IM_SEPARATOR(": ");
-
-
void toast_callback(const LLSD& msg){
// do not show toast in busy mode or it goes from agent
if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
@@ -193,7 +190,13 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&
}
if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
- LLLogChat::loadHistory(mName, &chatFromLogFile, (void *)this);
+ {
+ std::list<LLSD> chat_history;
+
+ //involves parsing of a chat history
+ LLLogChat::loadAllHistory(mName, chat_history);
+ addMessagesFromHistory(chat_history);
+ }
}
void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
@@ -303,6 +306,30 @@ void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& f
}
}
+void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history)
+{
+ std::list<LLSD>::const_iterator it = history.begin();
+ while (it != history.end())
+ {
+ const LLSD& msg = *it;
+
+ std::string from = msg[IM_FROM];
+ LLUUID from_id = LLUUID::null;
+ if (msg[IM_FROM_ID].isUndefined())
+ {
+ gCacheName->getUUID(from, from_id);
+ }
+
+
+ std::string timestamp = msg[IM_TIME];
+ std::string text = msg[IM_TEXT];
+
+ addMessage(from, from_id, text, timestamp);
+
+ it++;
+ }
+}
+
void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)
{
if (!userdata) return;
@@ -1348,7 +1375,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response)
session_id = gIMMgr->addP2PSession(
mPayload["session_name"].asString(),
mPayload["caller_id"].asUUID(),
- mPayload["session_handle"].asString());
+ mPayload["session_handle"].asString(),
+ mPayload["session_uri"].asString());
if (voice)
{
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index 40e3a8fb69..66f92c83a5 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -60,6 +60,8 @@ public:
virtual ~LLIMSession();
void sessionInitReplyReceived(const LLUUID& new_session_id);
+
+ void addMessagesFromHistory(const std::list<LLSD>& history);
void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time);
void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state);
static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata);
@@ -277,11 +279,16 @@ public:
const LLUUID& other_participant_id,
const LLDynamicArray<LLUUID>& ids);
- // Creates a P2P session with the requisite handle for responding to voice calls
+ /**
+ * Creates a P2P session with the requisite handle for responding to voice calls.
+ *
+ * @param caller_uri - sip URI of caller. It should be always be passed into the method to avoid
+ * incorrect working of LLVoiceChannel instances. See EXT-2985.
+ */
LLUUID addP2PSession(const std::string& name,
const LLUUID& other_participant_id,
const std::string& voice_session_handle,
- const std::string& caller_uri = LLStringUtil::null);
+ const std::string& caller_uri);
/**
* Leave the session with session id. Send leave session notification
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
index 9caa863bd8..33fd3e3bf1 100644
--- a/indra/newview/lllogchat.cpp
+++ b/indra/newview/lllogchat.cpp
@@ -32,15 +32,59 @@
#include "llviewerprecompiledheaders.h"
+#include "llagent.h"
+#include "llagentui.h"
#include "lllogchat.h"
-#include "llappviewer.h"
#include "llfloaterchat.h"
#include "lltrans.h"
#include "llviewercontrol.h"
-#include "llsdserialize.h"
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/regex.hpp>
+#include <boost/regex/v4/match_results.hpp>
const S32 LOG_RECALL_SIZE = 2048;
+const static std::string IM_TIME("time");
+const static std::string IM_TEXT("message");
+const static std::string IM_FROM("from");
+const static std::string IM_FROM_ID("from_id");
+const static std::string IM_SEPARATOR(": ");
+
+const static std::string NEW_LINE("\n");
+const static std::string NEW_LINE_SPACE_PREFIX("\n ");
+const static std::string TWO_SPACES(" ");
+const static std::string MULTI_LINE_PREFIX(" ");
+
+//viewer 1.23 may have used "You" for Agent's entries
+const static std::string YOU("You");
+
+/**
+ * Chat log lines - timestamp and name are optional but message text is mandatory.
+ *
+ * Typical plain text chat log lines:
+ *
+ * SuperCar: You aren't the owner
+ * [2:59] SuperCar: You aren't the owner
+ * [2009/11/20 3:00] SuperCar: You aren't the owner
+ * Katar Ivercourt is Offline
+ * [3:00] Katar Ivercourt is Offline
+ * [2009/11/20 3:01] Corba ProductEngine is Offline
+ */
+const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$");
+
+/**
+ * Regular expression suitable to match names like
+ * "You", "Second Life", "Igor ProductEngine", "Object", "Mega House"
+ */
+const static boost::regex NAME_AND_TEXT("(You:|Second Life:|[^\\s:]+\\s*[:]{1}|\\S+\\s+[^\\s:]+[:]{1})?(\\s*)(.*)");
+
+const static int IDX_TIMESTAMP = 1;
+const static int IDX_STUFF = 2;
+const static int IDX_NAME = 1;
+const static int IDX_TEXT = 3;
+
//static
std::string LLLogChat::makeLogFileName(std::string filename)
{
@@ -118,7 +162,7 @@ void LLLogChat::saveHistory(const std::string& filename,
item["from_id"] = from_id;
item["message"] = line;
- file << LLSDOStreamer <LLSDNotationFormatter>(item) << std::endl;
+ file << LLChatLogFormatter(item) << std::endl;
file.close();
}
@@ -154,9 +198,6 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi
}
}
- // the parser's destructor is protected so we cannot create in the stack.
- LLPointer<LLSDParser> parser = new LLSDNotationParser();
-
while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) )
{
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
@@ -167,7 +208,8 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi
LLSD item;
std::string line(buffer);
std::istringstream iss(line);
- if (parser->parse(iss, item, line.length()) == LLSDParser::PARSE_FAILURE)
+
+ if (!LLChatLogParser::parse(line, item))
{
item["message"] = line;
callback(LOG_LINE, item, userdata);
@@ -187,3 +229,197 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi
fclose(fptr);
}
}
+
+void append_to_last_message(std::list<LLSD>& messages, const std::string& line)
+{
+ if (!messages.size()) return;
+
+ std::string im_text = messages.back()[IM_TEXT].asString();
+ im_text.append(line);
+ messages.back()[IM_TEXT] = im_text;
+}
+
+void LLLogChat::loadAllHistory(const std::string& session_name, std::list<LLSD>& messages)
+{
+ if (session_name.empty())
+ {
+ llwarns << "Session name is Empty!" << llendl;
+ return ;
+ }
+
+ LLFILE* fptr = LLFile::fopen(makeLogFileName(session_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;
+
+ if (fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END))
+ { //File is smaller than recall size. 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 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))
+ {
+ item[IM_TEXT] = line;
+ }
+ messages.push_back(item);
+ }
+ }
+ fclose(fptr);
+}
+
+//*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 writen like "Object <actual_object's_name>"
+void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
+{
+ if (!im.isMap())
+ {
+ llwarning("invalid LLSD type of an instant message", 0);
+ return;
+ }
+
+ if (im[IM_TIME].isDefined())
+ {
+ std::string timestamp = im[IM_TIME].asString();
+ boost::trim(timestamp);
+ ostr << '[' << timestamp << ']' << TWO_SPACES;
+ }
+
+ //*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 writen like "Object <actual_object's_name>"
+ if (im[IM_FROM].isDefined())
+ {
+ std::string from = im[IM_FROM].asString();
+ boost::trim(from);
+ if (from.size())
+ {
+ ostr << from << IM_SEPARATOR;
+ }
+ }
+
+ if (im[IM_TEXT].isDefined())
+ {
+ std::string im_text = im[IM_TEXT].asString();
+
+ //multilined text will be saved with prepended spaces
+ boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX);
+ ostr << im_text;
+ }
+}
+
+bool LLChatLogParser::parse(std::string& raw, LLSD& im)
+{
+ if (!raw.length()) return false;
+
+ im = LLSD::emptyMap();
+
+ //matching a timestamp
+ boost::match_results<std::string::const_iterator> matches;
+ if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false;
+
+ bool has_timestamp = matches[IDX_TIMESTAMP].matched;
+ if (has_timestamp)
+ {
+ //timestamp was successfully parsed
+ std::string timestamp = matches[IDX_TIMESTAMP];
+ boost::trim(timestamp);
+ timestamp.erase(0, 1);
+ timestamp.erase(timestamp.length()-1, 1);
+ im[IM_TIME] = timestamp;
+ }
+ else
+ {
+ //timestamp is optional
+ im[IM_TIME] = "";
+ }
+
+ bool has_stuff = matches[IDX_STUFF].matched;
+ if (!has_stuff)
+ {
+ return false; //*TODO should return false or not?
+ }
+
+ //matching a name and a text
+ std::string stuff = matches[IDX_STUFF];
+ boost::match_results<std::string::const_iterator> name_and_text;
+ if (!boost::regex_match(stuff, name_and_text, NAME_AND_TEXT)) return false;
+
+ bool has_name = name_and_text[IDX_NAME].matched;
+ std::string name = name_and_text[IDX_NAME];
+
+ //we don't need a name/text separator
+ if (has_name && name.length() && name[name.length()-1] == ':')
+ {
+ name.erase(name.length()-1, 1);
+ }
+
+ if (!has_name || name == SYSTEM_FROM)
+ {
+ //name is optional too
+ im[IM_FROM] = SYSTEM_FROM;
+ im[IM_FROM_ID] = LLUUID::null;
+ }
+
+ if (!has_name)
+ {
+ //text is mandatory
+ im[IM_TEXT] = stuff;
+ return true; //parse as a message from Second Life
+ }
+
+ bool has_text = name_and_text[IDX_TEXT].matched;
+ if (!has_text) return false;
+
+ //for parsing logs created in very old versions of a viewer
+ if (name == "You")
+ {
+ std::string agent_name;
+ LLAgentUI::buildFullname(agent_name);
+ im[IM_FROM] = agent_name;
+ im[IM_FROM_ID] = gAgentID;
+ }
+ else
+ {
+ im[IM_FROM] = name;
+ }
+
+
+ im[IM_TEXT] = name_and_text[IDX_TEXT];
+ return true; //parsed name and message text, maybe have a timestamp too
+}
diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h
index e252cd7d41..3d3f5c4458 100644
--- a/indra/newview/lllogchat.h
+++ b/indra/newview/lllogchat.h
@@ -51,11 +51,66 @@ public:
const LLUUID& from_id,
const std::string& line);
+ /** @deprecated @see loadAllHistory() */
static void loadHistory(const std::string& filename,
void (*callback)(ELogLineType, const LLSD&, void*),
void* userdata);
+
+ static void loadAllHistory(const std::string& session_name, std::list<LLSD>& messages);
private:
static std::string cleanFileName(std::string filename);
};
+/**
+ * Formatter for the plain text chat log files
+ */
+class LLChatLogFormatter
+{
+public:
+ LLChatLogFormatter(const LLSD& im) : mIM(im) {}
+ virtual ~LLChatLogFormatter() {};
+
+ friend std::ostream& operator<<(std::ostream& str, const LLChatLogFormatter& formatter)
+ {
+ formatter.format(formatter.mIM, str);
+ return str;
+ }
+
+protected:
+
+ /**
+ * Format an instant message to a stream
+ * Timestamps and sender names are required
+ * New lines of multilined messages are prepended with a space
+ */
+ void format(const LLSD& im, std::ostream& ostr) const;
+
+ LLSD mIM;
+};
+
+/**
+ * Parser for the plain text chat log files
+ */
+class LLChatLogParser
+{
+public:
+
+ /**
+ * Parse a line from the plain text chat log file
+ * General plain text log format is like: "[timestamp] [name]: [message]"
+ * [timestamp] and [name] are optional
+ * Examples of plain text chat log lines:
+ * "[2009/11/20 2:53] Igor ProductEngine: howdy"
+ * "Igor ProductEngine: howdy"
+ * "Dserduk ProductEngine is Online"
+ *
+ * @return false if failed to parse mandatory data - message text
+ */
+ static bool parse(std::string& raw, LLSD& im);
+
+protected:
+ LLChatLogParser();
+ virtual ~LLChatLogParser() {};
+};
+
#endif
diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp
index 8f1dec1431..ee3be0a5e3 100644
--- a/indra/newview/llnearbychat.cpp
+++ b/indra/newview/llnearbychat.cpp
@@ -57,6 +57,7 @@
#include "lltrans.h"
#include "llbottomtray.h"
#include "llnearbychatbar.h"
+#include "llfloaterreg.h"
static const S32 RESIZE_BAR_THICKNESS = 3;
@@ -145,7 +146,7 @@ std::string appendTime()
return timeStr;
}
-void LLNearbyChat::addMessage(const LLChat& chat)
+void LLNearbyChat::addMessage(const LLChat& chat,bool archive)
{
if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
{
@@ -207,6 +208,13 @@ void LLNearbyChat::addMessage(const LLChat& chat)
mChatHistory->appendMessage(chat,use_plain_text_chat_history);
}
}
+
+ if(archive)
+ {
+ mMessageArchive.push_back(chat);
+ if(mMessageArchive.size()>200)
+ mMessageArchive.erase(mMessageArchive.begin());
+ }
}
void LLNearbyChat::onNearbySpeakers()
@@ -256,3 +264,19 @@ void LLNearbyChat::getAllowedRect(LLRect& rect)
{
rect = gViewerWindow->getWorldViewRectScaled();
}
+
+void LLNearbyChat::updateChatHistoryStyle()
+{
+ mChatHistory->clear();
+ for(std::vector<LLChat>::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it)
+ {
+ addMessage(*it,false);
+ }
+}
+//static
+void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue)
+{
+ LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD());
+ if(nearby_chat)
+ nearby_chat->updateChatHistoryStyle();
+}
diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h
index efcaf4263b..1cbc2a3478 100644
--- a/indra/newview/llnearbychat.h
+++ b/indra/newview/llnearbychat.h
@@ -47,7 +47,7 @@ public:
~LLNearbyChat();
BOOL postBuild ();
- void addMessage (const LLChat& message);
+ void addMessage (const LLChat& message,bool archive = true);
void onNearbyChatContextMenuItemClicked(const LLSD& userdata);
bool onNearbyChatCheckContextMenuItem(const LLSD& userdata);
@@ -57,6 +57,10 @@ public:
virtual void setRect (const LLRect &rect);
+ virtual void updateChatHistoryStyle();
+
+ static void processChatHistoryStyleUpdate(const LLSD& newvalue);
+
private:
virtual void applySavedVariables();
@@ -68,6 +72,8 @@ private:
private:
LLHandle<LLView> mPopupMenuHandle;
LLChatHistory* mChatHistory;
+
+ std::vector<LLChat> mMessageArchive;
};
#endif
diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp
index 05da338513..857b7e9796 100644
--- a/indra/newview/llnotificationhandlerutil.cpp
+++ b/indra/newview/llnotificationhandlerutil.cpp
@@ -43,13 +43,14 @@ using namespace LLNotificationsUI;
const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"),
REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), OBJECT_GIVE_ITEM(
"ObjectGiveItem"), OBJECT_GIVE_ITEM_UNKNOWN_USER(
- "ObjectGiveItemUnknownUser");
+ "ObjectGiveItemUnknownUser"), PAYMENT_RECIVED("PaymentRecived");
// static
bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification)
{
return GRANTED_MODIFY_RIGHTS == notification->getName()
- || REVOKED_MODIFY_RIGHTS == notification->getName();
+ || REVOKED_MODIFY_RIGHTS == notification->getName()
+ || PAYMENT_RECIVED == notification->getName();
}
// static
diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp
index c179a2cf90..4f353bf6a5 100644
--- a/indra/newview/llnotificationofferhandler.cpp
+++ b/indra/newview/llnotificationofferhandler.cpp
@@ -39,6 +39,7 @@
#include "llviewerwindow.h"
#include "llnotificationmanager.h"
#include "llnotifications.h"
+#include "llscriptfloater.h"
using namespace LLNotificationsUI;
@@ -92,24 +93,42 @@ bool LLOfferHandler::processNotification(const LLSD& notify)
{
LLHandlerUtil::logToIMP2P(notification);
- LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification);
-
- LLToast::Params p;
- p.notif_id = notification->getID();
- p.notification = notification;
- p.panel = notify_box;
- p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1);
-
- LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel);
- if(channel)
- channel->addToast(p);
-
- // send a signal to the counter manager
- mNewNotificationSignal();
+ if( notification->getPayload().has("give_inventory_notification")
+ && !notification->getPayload()["give_inventory_notification"] )
+ {
+ // This is an original inventory offer, so add a script floater
+ LLScriptFloaterManager::instance().onAddNotification(notification->getID());
+ }
+ else
+ {
+ LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification);
+
+ LLToast::Params p;
+ p.notif_id = notification->getID();
+ p.notification = notification;
+ p.panel = notify_box;
+ p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1);
+
+ LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel);
+ if(channel)
+ channel->addToast(p);
+
+ // send a signal to the counter manager
+ mNewNotificationSignal();
+ }
}
else if (notify["sigtype"].asString() == "delete")
{
- mChannel->killToastByNotificationID(notification->getID());
+ if( notification->getPayload().has("give_inventory_notification")
+ && !notification->getPayload()["give_inventory_notification"] )
+ {
+ // Remove original inventory offer script floater
+ LLScriptFloaterManager::instance().onRemoveNotification(notification->getID());
+ }
+ else
+ {
+ mChannel->killToastByNotificationID(notification->getID());
+ }
}
return true;
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 97c1e96175..eb9cb10d56 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -254,11 +254,19 @@ void LLPanelAvatarNotes::onCommitRights()
const LLRelationship* buddy_relationship =
LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
bool allow_modify_objects = childGetValue("objects_check").asBoolean();
+
+ // if modify objects checkbox clicked
if (buddy_relationship->isRightGrantedTo(
LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)
{
confirmModifyRights(allow_modify_objects, rights);
}
+ // only one checkbox can trigger commit, so store the rest of rights
+ else
+ {
+ LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(
+ getAvatarId(), rights);
+ }
}
void LLPanelAvatarNotes::processProperties(void* data, EAvatarProcessorType type)
@@ -522,20 +530,19 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g
for(; it_end != it; ++it)
{
LLAvatarGroups::LLGroupData group_data = *it;
-
- // Check if there is no duplicates for this group
- if (std::find(mGroups.begin(), mGroups.end(), group_data.group_name) == mGroups.end())
- mGroups.push_back(group_data.group_name);
+ mGroups[group_data.group_name] = group_data.group_id;
}
// Creating string, containing group list
std::string groups = "";
- for (group_list_t::const_iterator it = mGroups.begin(); it != mGroups.end(); ++it)
+ for (group_map_t::iterator it = mGroups.begin(); it != mGroups.end(); ++it)
{
if (it != mGroups.begin())
groups += ", ";
- groups += *it;
+
+ std::string group_url="[secondlife:///app/group/" + it->second.asString() + "/about " + it->first + "]";
+ groups += group_url;
}
childSetValue("sl_groups", groups);
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
index f54aeee4eb..b19c5cca49 100644
--- a/indra/newview/llpanelavatar.h
+++ b/indra/newview/llpanelavatar.h
@@ -192,8 +192,8 @@ protected:
private:
- typedef std::list<std::string> group_list_t;
- group_list_t mGroups;
+ typedef std::map< std::string,LLUUID> group_map_t;
+ group_map_t mGroups;
LLToggleableMenu* mProfileMenu;
};
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
index e04c830036..5834c50fbb 100644
--- a/indra/newview/llpanelgroupnotices.cpp
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -510,6 +510,9 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg)
S32 i=0;
S32 count = msg->getNumberOfBlocks("Data");
+
+ mNoticesList->setEnabled(TRUE);
+
for (;i<count;++i)
{
msg->getUUID("Data","NoticeID",id,i);
diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp
index 3af18bb751..4ce6d14faa 100644
--- a/indra/newview/llpanellandmarks.cpp
+++ b/indra/newview/llpanellandmarks.cpp
@@ -67,28 +67,6 @@ static const std::string TRASH_BUTTON_NAME = "trash_btn";
// helper functions
static void filter_list(LLInventorySubTreePanel* inventory_list, const std::string& string);
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLLandmarksPanelObserver
-//
-// Bridge to support knowing when the inventory has changed to update
-// landmarks accordions visibility.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLLandmarksPanelObserver : public LLInventoryObserver
-{
-public:
- LLLandmarksPanelObserver(LLLandmarksPanel* lp) : mLP(lp) {}
- virtual ~LLLandmarksPanelObserver() {}
- /*virtual*/ void changed(U32 mask);
-
-private:
- LLLandmarksPanel* mLP;
-};
-
-void LLLandmarksPanelObserver::changed(U32 mask)
-{
- mLP->updateFilteredAccordions();
-}
-
LLLandmarksPanel::LLLandmarksPanel()
: LLPanelPlacesTab()
, mFavoritesInventoryPanel(NULL)
@@ -99,18 +77,12 @@ LLLandmarksPanel::LLLandmarksPanel()
, mListCommands(NULL)
, mGearFolderMenu(NULL)
, mGearLandmarkMenu(NULL)
- , mDirtyFilter(false)
{
- mInventoryObserver = new LLLandmarksPanelObserver(this);
- gInventory.addObserver(mInventoryObserver);
-
LLUICtrlFactory::getInstance()->buildPanel(this, "panel_landmarks.xml");
}
LLLandmarksPanel::~LLLandmarksPanel()
{
- if (gInventory.containsObserver(mInventoryObserver))
- gInventory.removeObserver(mInventoryObserver);
}
BOOL LLLandmarksPanel::postBuild()
@@ -137,39 +109,34 @@ BOOL LLLandmarksPanel::postBuild()
// virtual
void LLLandmarksPanel::onSearchEdit(const std::string& string)
{
- static std::string prev_string("");
-
- if (prev_string == string) return;
-
// show all folders in Landmarks Accordion for empty filter
- mLandmarksInventoryPanel->setShowFolderState(string.empty() ?
- LLInventoryFilter::SHOW_ALL_FOLDERS :
- LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS
- );
-
- filter_list(mFavoritesInventoryPanel, string);
- filter_list(mLandmarksInventoryPanel, string);
- filter_list(mMyInventoryPanel, string);
- filter_list(mLibraryInventoryPanel, string);
-
- prev_string = string;
- mDirtyFilter = true;
+ if (mLandmarksInventoryPanel->getFilter())
+ {
+ mLandmarksInventoryPanel->setShowFolderState(string.empty() ?
+ LLInventoryFilter::SHOW_ALL_FOLDERS :
+ LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS
+ );
+ }
// give FolderView a chance to be refreshed. So, made all accordions visible
for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter)
{
LLAccordionCtrlTab* tab = *iter;
- tab->setVisible(true);
+ if (tab && !tab->getVisible())
+ tab->setVisible(TRUE);
// expand accordion to see matched items in each one. See EXT-2014.
tab->changeOpenClose(false);
- // refresh all accordions to display their contents in case of less restrictive filter
LLInventorySubTreePanel* inventory_list = dynamic_cast<LLInventorySubTreePanel*>(tab->getAccordionView());
if (NULL == inventory_list) continue;
- LLFolderView* fv = inventory_list->getRootFolder();
- fv->refresh();
+
+ if (inventory_list->getFilter())
+ filter_list(inventory_list, string);
}
+
+ if (sFilterSubString != string)
+ sFilterSubString = string;
}
// virtual
@@ -255,34 +222,6 @@ void LLLandmarksPanel::onSelectorButtonClicked()
}
}
-void LLLandmarksPanel::updateFilteredAccordions()
-{
- LLInventoryPanel* inventory_list = NULL;
- LLAccordionCtrlTab* accordion_tab = NULL;
- for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter)
- {
- accordion_tab = *iter;
- inventory_list = dynamic_cast<LLInventorySubTreePanel*> (accordion_tab->getAccordionView());
- if (NULL == inventory_list) continue;
- // This doesn't seem to work correctly. Disabling for now. -Seraph
- /*
- LLFolderView* fv = inventory_list->getRootFolder();
- bool has_descendants = fv->hasFilteredDescendants();
-
- accordion_tab->setVisible(has_descendants);
- */
- accordion_tab->setVisible(TRUE);
- }
-
- // we have to arrange accordion tabs for cases when filter string is less restrictive but
- // all items are still filtered.
- static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion");
- accordion->arrange();
-
- // now filter state is applied to accordion tabs
- mDirtyFilter = false;
-}
-
//////////////////////////////////////////////////////////////////////////
// PROTECTED METHODS
//////////////////////////////////////////////////////////////////////////
@@ -394,7 +333,8 @@ void LLLandmarksPanel::initLandmarksInventoryPanel()
initLandmarksPanel(mLandmarksInventoryPanel);
- mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);
+ if (mLandmarksInventoryPanel->getFilter())
+ mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);
// subscribe to have auto-rename functionality while creating New Folder
mLandmarksInventoryPanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mLandmarksInventoryPanel, _1, _2));
@@ -422,6 +362,9 @@ void LLLandmarksPanel::initLibraryInventoryPanel()
void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_list)
{
+ if (!inventory_list->getFilter())
+ return;
+
inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK);
inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2));
@@ -435,6 +378,8 @@ void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_lis
root_folder->setupMenuHandle(LLInventoryType::IT_LANDMARK, mGearLandmarkMenu->getHandle());
}
+ root_folder->setParentLandmarksPanel(this);
+
// save initial folder state to avoid incorrect work while switching between Landmarks & Teleport History tabs
// See EXT-1609.
inventory_list->saveFolderState();
@@ -443,6 +388,9 @@ void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_lis
void LLLandmarksPanel::initAccordion(const std::string& accordion_tab_name, LLInventorySubTreePanel* inventory_list)
{
LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>(accordion_tab_name);
+ if (!accordion_tab)
+ return;
+
mAccordionTabs.push_back(accordion_tab);
accordion_tab->setDropDownStateChangedCallback(
boost::bind(&LLLandmarksPanel::onAccordionExpandedCollapsed, this, _2, inventory_list));
@@ -787,6 +735,46 @@ void LLLandmarksPanel::onCustomAction(const LLSD& userdata)
}
}
+void LLLandmarksPanel::updateFilteredAccordions()
+{
+ LLInventoryPanel* inventory_list = NULL;
+ LLAccordionCtrlTab* accordion_tab = NULL;
+// bool needs_arrange = false;
+
+ for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter)
+ {
+ accordion_tab = *iter;
+ if (accordion_tab && !accordion_tab->getVisible())
+ accordion_tab->setVisible(TRUE);
+
+ inventory_list = dynamic_cast<LLInventorySubTreePanel*> (accordion_tab->getAccordionView());
+ if (NULL == inventory_list) continue;
+
+ // This doesn't seem to work correctly. Disabling for now. -Seraph
+ /*
+ LLFolderView* fv = inventory_list->getRootFolder();
+
+ // arrange folder view contents to draw its descendants if it has any
+ fv->arrangeFromRoot();
+
+ bool has_descendants = fv->hasFilteredDescendants();
+ if (!has_descendants)
+ needs_arrange = true;
+
+ accordion_tab->setVisible(has_descendants);
+ */
+ accordion_tab->setVisible(TRUE);
+ }
+
+ // we have to arrange accordion tabs for cases when filter string is less restrictive but
+ // all items are still filtered.
+// if (needs_arrange)
+// {
+ static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion");
+ accordion->arrange();
+// }
+}
+
/*
Processes such actions: cut/rename/delete/paste actions
@@ -899,12 +887,7 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg
void LLLandmarksPanel::doIdle(void* landmarks_panel)
{
LLLandmarksPanel* panel = (LLLandmarksPanel* ) landmarks_panel;
-
- if (panel->mDirtyFilter)
- {
- panel->updateFilteredAccordions();
- }
-
+ panel->updateFilteredAccordions();
}
void LLLandmarksPanel::doShowOnMap(LLLandmark* landmark)
diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h
index c65abc178b..bee141d051 100644
--- a/indra/newview/llpanellandmarks.h
+++ b/indra/newview/llpanellandmarks.h
@@ -46,7 +46,6 @@ class LLAccordionCtrlTab;
class LLFolderViewItem;
class LLMenuGL;
class LLInventoryPanel;
-class LLInventoryObserver;
class LLInventorySubTreePanel;
class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver
@@ -63,13 +62,10 @@ public:
void onSelectionChange(LLInventorySubTreePanel* inventory_list, const std::deque<LLFolderViewItem*> &items, BOOL user_action);
void onSelectorButtonClicked();
-
- /**
- * Updates accordions according to filtered items in lists.
- *
- * It hides accordion for empty lists
- */
- void updateFilteredAccordions();
+ void setCurrentSelectedList(LLInventorySubTreePanel* inventory_list)
+ {
+ mCurrentSelectedList = inventory_list;
+ }
protected:
/**
@@ -111,6 +107,13 @@ private:
void onCustomAction(const LLSD& command_name);
/**
+ * Updates accordions according to filtered items in lists.
+ *
+ * It hides accordion for empty lists
+ */
+ void updateFilteredAccordions();
+
+ /**
* Determines if selected item can be modified via context/gear menu.
*
* It validates Places Landmarks rules first. And then LLFolderView permissions.
@@ -148,11 +151,9 @@ private:
LLMenuGL* mGearFolderMenu;
LLMenuGL* mMenuAdd;
LLInventorySubTreePanel* mCurrentSelectedList;
- LLInventoryObserver* mInventoryObserver;
LLPanel* mListCommands;
bool mSortByDate;
- bool mDirtyFilter;
typedef std::vector<LLAccordionCtrlTab*> accordion_tabs_t;
accordion_tabs_t mAccordionTabs;
diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp
index da0c8d5020..541361324a 100644
--- a/indra/newview/llpanelpick.cpp
+++ b/indra/newview/llpanelpick.cpp
@@ -558,6 +558,11 @@ void LLPanelPickEdit::initTexturePickerMouseEvents()
text_icon = getChild<LLIconCtrl>(XML_BTN_ON_TXTR);
mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseEnter, this, _1));
mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseLeave, this, _1));
+
+ // *WORKAROUND: Needed for EXT-1625: enabling save button each time when picker is opened, even if
+ // texture wasn't changed (see Steve's comment).
+ mSnapshotCtrl->setMouseDownCallback(boost::bind(&LLPanelPickEdit::enableSaveButton, this, true));
+
text_icon->setVisible(FALSE);
}
diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp
index 4abb60dded..cc6e88a9d2 100644
--- a/indra/newview/llpanelpicks.cpp
+++ b/indra/newview/llpanelpicks.cpp
@@ -61,7 +61,6 @@ static const std::string XML_BTN_DELETE = "trash_btn";
static const std::string XML_BTN_INFO = "info_btn";
static const std::string XML_BTN_TELEPORT = "teleport_btn";
static const std::string XML_BTN_SHOW_ON_MAP = "show_on_map_btn";
-static const std::string XML_BTN_OVERFLOW = "overflow_btn";
static const std::string PICK_ID("pick_id");
static const std::string PICK_CREATOR_ID("pick_creator_id");
@@ -113,7 +112,6 @@ LLPanelPicks::LLPanelPicks()
mClassifiedsList(NULL),
mPanelPickInfo(NULL),
mPanelPickEdit(NULL),
- mOverflowMenu(NULL),
mPlusMenu(NULL),
mPicksAccTab(NULL),
mClassifiedsAccTab(NULL),
@@ -273,7 +271,6 @@ BOOL LLPanelPicks::postBuild()
childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this));
childSetAction(XML_BTN_SHOW_ON_MAP, boost::bind(&LLPanelPicks::onClickMap, this));
childSetAction(XML_BTN_INFO, boost::bind(&LLPanelPicks::onClickInfo, this));
- childSetAction(XML_BTN_OVERFLOW, boost::bind(&LLPanelPicks::onOverflowButtonClicked, this));
mPicksAccTab = getChild<LLAccordionCtrlTab>("tab_picks");
mPicksAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mPicksAccTab));
@@ -291,10 +288,6 @@ BOOL LLPanelPicks::postBuild()
registar.add("Pick.Delete", boost::bind(&LLPanelPicks::onClickDelete, this));
mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_picks.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar overflow_registar;
- overflow_registar.add("PicksList.Overflow", boost::bind(&LLPanelPicks::onOverflowMenuItemClicked, this, _2));
- mOverflowMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar;
plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2));
mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
@@ -302,24 +295,6 @@ BOOL LLPanelPicks::postBuild()
return TRUE;
}
-void LLPanelPicks::onOverflowMenuItemClicked(const LLSD& param)
-{
- std::string value = param.asString();
-
- if("info" == value)
- {
- onClickInfo();
- }
- else if("teleport" == value)
- {
- onClickTeleport();
- }
- else if("map" == value)
- {
- onClickMap();
- }
-}
-
void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param)
{
std::string value = param.asString();
@@ -348,23 +323,6 @@ void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab)
updateButtons();
}
-void LLPanelPicks::onOverflowButtonClicked()
-{
- if (!mOverflowMenu->toggleVisibility())
- return;
-
- LLView* btn = getChild<LLView>(XML_BTN_OVERFLOW);
-
- if (mOverflowMenu->getButtonRect().isEmpty())
- {
- mOverflowMenu->setButtonRect(btn);
- }
- mOverflowMenu->updateParent(LLMenuGL::sMenuContainer);
-
- LLRect rect = btn->getRect();
- LLMenuGL::showPopup(this, mOverflowMenu, rect.mRight, rect.mTop);
-}
-
void LLPanelPicks::onOpen(const LLSD& key)
{
const LLUUID id(key.asUUID());
@@ -568,7 +526,6 @@ void LLPanelPicks::updateButtons()
childSetEnabled(XML_BTN_INFO, has_selected);
childSetEnabled(XML_BTN_TELEPORT, has_selected);
childSetEnabled(XML_BTN_SHOW_ON_MAP, has_selected);
- childSetEnabled(XML_BTN_OVERFLOW, has_selected);
}
void LLPanelPicks::setProfilePanel(LLPanelProfile* profile_panel)
diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h
index b21b1c64b1..21794d56b2 100644
--- a/indra/newview/llpanelpicks.h
+++ b/indra/newview/llpanelpicks.h
@@ -94,8 +94,6 @@ private:
void onClickTeleport();
void onClickMap();
- void onOverflowMenuItemClicked(const LLSD& param);
- void onOverflowButtonClicked();
void onPlusMenuItemClicked(const LLSD& param);
void onListCommit(const LLFlatListView* f_list);
@@ -149,7 +147,6 @@ private:
LLPanelClassifiedInfo* mPanelClassifiedInfo;
LLPanelClassifiedEdit* mPanelClassifiedEdit;
LLPanelPickEdit* mPanelPickEdit;
- LLToggleableMenu* mOverflowMenu;
LLToggleableMenu* mPlusMenu;
LLAccordionCtrlTab* mPicksAccTab;
diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp
index 839b2ec45e..cd4bcb6c0a 100644
--- a/indra/newview/llpanelplaces.cpp
+++ b/indra/newview/llpanelplaces.cpp
@@ -118,7 +118,6 @@ static LLRegisterPanelClassWrapper<LLPanelPlaces> t_places("panel_places");
LLPanelPlaces::LLPanelPlaces()
: LLPanel(),
- mFilterSubString(LLStringUtil::null),
mActivePanel(NULL),
mFilterEditor(NULL),
mPlaceProfile(NULL),
@@ -385,16 +384,16 @@ void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)
void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter)
{
- if (force_filter || mFilterSubString != search_string)
+ if (force_filter || LLPanelPlacesTab::sFilterSubString != search_string)
{
- mFilterSubString = search_string;
+ std::string string = search_string;
// Searches are case-insensitive
- LLStringUtil::toUpper(mFilterSubString);
- LLStringUtil::trimHead(mFilterSubString);
+ LLStringUtil::toUpper(string);
+ LLStringUtil::trimHead(string);
if (mActivePanel)
- mActivePanel->onSearchEdit(mFilterSubString);
+ mActivePanel->onSearchEdit(string);
}
}
@@ -404,7 +403,7 @@ void LLPanelPlaces::onTabSelected()
if (!mActivePanel)
return;
- onFilterEdit(mFilterSubString, true);
+ onFilterEdit(LLPanelPlacesTab::sFilterSubString, true);
mActivePanel->updateVerbs();
}
@@ -815,7 +814,7 @@ void LLPanelPlaces::changedInventory(U32 mask)
// Filter applied to show all items.
if (mActivePanel)
- mActivePanel->onSearchEdit(mFilterSubString);
+ mActivePanel->onSearchEdit(LLPanelPlacesTab::sFilterSubString);
// we don't need to monitor inventory changes anymore,
// so remove the observer
diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h
index 0d97353b66..5f9aed6357 100644
--- a/indra/newview/llpanelplaces.h
+++ b/indra/newview/llpanelplaces.h
@@ -120,10 +120,6 @@ private:
// be available (hence zero)
LLVector3d mPosGlobal;
- // Search string for filtering landmarks and teleport
- // history locations
- std::string mFilterSubString;
-
// Information type currently shown in Place Information panel
std::string mPlaceInfoType;
diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp
index b7669fd63d..9806b8c64d 100644
--- a/indra/newview/llpanelplacestab.cpp
+++ b/indra/newview/llpanelplacestab.cpp
@@ -33,14 +33,17 @@
#include "llpanelplacestab.h"
-#include "llwindow.h"
-
+#include "llbutton.h"
#include "llnotificationsutil.h"
-#include "llbutton.h"
+#include "llwindow.h"
+
+#include "llpanelplaces.h"
#include "llslurl.h"
#include "llworldmap.h"
+std::string LLPanelPlacesTab::sFilterSubString = LLStringUtil::null;
+
bool LLPanelPlacesTab::isTabVisible()
{
LLUICtrl* parent = getParentUICtrl();
diff --git a/indra/newview/llpanelplacestab.h b/indra/newview/llpanelplacestab.h
index 458694d766..b4d839452e 100644
--- a/indra/newview/llpanelplacestab.h
+++ b/indra/newview/llpanelplacestab.h
@@ -34,7 +34,7 @@
#include "llpanel.h"
-#include "llpanelplaces.h"
+class LLPanelPlaces;
class LLPanelPlacesTab : public LLPanel
{
@@ -55,6 +55,11 @@ public:
const std::string& url,
const LLUUID& snapshot_id,
bool teleport);
+
+public:
+ // Search string for filtering landmarks and teleport history locations
+ static std::string sFilterSubString;
+
protected:
LLButton* mTeleportBtn;
LLButton* mShowOnMapBtn;
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp
index 327048d4f3..523487fa14 100644
--- a/indra/newview/llpanelteleporthistory.cpp
+++ b/indra/newview/llpanelteleporthistory.cpp
@@ -220,7 +220,6 @@ void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard()
LLTeleportHistoryPanel::LLTeleportHistoryPanel()
: LLPanelPlacesTab(),
- mFilterSubString(LLStringUtil::null),
mDirty(true),
mCurrentItem(0),
mTeleportHistory(NULL),
@@ -317,9 +316,9 @@ void LLTeleportHistoryPanel::draw()
// virtual
void LLTeleportHistoryPanel::onSearchEdit(const std::string& string)
{
- if (mFilterSubString != string)
+ if (sFilterSubString != string)
{
- mFilterSubString = string;
+ sFilterSubString = string;
showTeleportHistory();
}
}
@@ -482,8 +481,8 @@ void LLTeleportHistoryPanel::refresh()
std::string landmark_title = items[mCurrentItem].mTitle;
LLStringUtil::toUpper(landmark_title);
- std::string::size_type match_offset = mFilterSubString.size() ? landmark_title.find(mFilterSubString) : std::string::npos;
- bool passed = mFilterSubString.size() == 0 || match_offset != std::string::npos;
+ std::string::size_type match_offset = sFilterSubString.size() ? landmark_title.find(sFilterSubString) : std::string::npos;
+ bool passed = sFilterSubString.size() == 0 || match_offset != std::string::npos;
if (!passed)
{
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index 07a1214b4f..13f195a1be 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -64,10 +64,10 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av
mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator");
mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
- mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
- mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2));
+ mAvatarListDoubleClickConnection = mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
+ mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2));
// Set onAvatarListDoubleClicked as default on_return action.
- mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
+ mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
if (use_context_menu)
{
@@ -99,6 +99,10 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av
LLParticipantList::~LLParticipantList()
{
+ mAvatarListDoubleClickConnection.disconnect();
+ mAvatarListRefreshConnection.disconnect();
+ mAvatarListReturnConnection.disconnect();
+
delete mParticipantListMenu;
mParticipantListMenu = NULL;
}
diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h
index 460cf4b9ef..86b38f5f1e 100644
--- a/indra/newview/llparticipantlist.h
+++ b/indra/newview/llparticipantlist.h
@@ -151,4 +151,9 @@ class LLParticipantList
LLParticipantListMenu* mParticipantListMenu;
EParticipantSortOrder mSortOrder;
+
+ // boost::connections
+ boost::signals2::connection mAvatarListDoubleClickConnection;
+ boost::signals2::connection mAvatarListRefreshConnection;
+ boost::signals2::connection mAvatarListReturnConnection;
};
diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp
index c58caf9c60..155172128b 100644
--- a/indra/newview/llscriptfloater.cpp
+++ b/indra/newview/llscriptfloater.cpp
@@ -41,6 +41,7 @@
#include "llscreenchannel.h"
#include "lltoastnotifypanel.h"
#include "llviewerwindow.h"
+#include "llimfloater.h"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -211,7 +212,18 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
LLNotificationData nd = {notification_id};
mNotifications.insert(std::make_pair(object_id, nd));
- LLBottomTray::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(object_id);
+ // Create inventory offer chiclet for offer type notifications
+ LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id);
+ if( notification && notification->getType() == "offer" )
+ {
+ LLBottomTray::instance().getChicletPanel()->createChiclet<LLInvOfferChiclet>(object_id);
+ }
+ else
+ {
+ LLBottomTray::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(object_id);
+ }
+
+ toggleScriptFloater(object_id);
}
void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id)
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 50c20bc98f..58df2ffb19 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -59,6 +59,7 @@
#include "llfloaterreg.h"
#include "llfocusmgr.h"
#include "llhttpsender.h"
+#include "llimfloater.h"
#include "lllocationhistory.h"
#include "llimageworker.h"
#include "llloginflags.h"
@@ -2085,6 +2086,8 @@ bool idle_startup()
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
+ LLIMFloater::initIMFloater();
+
return TRUE;
}
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index d0ae5d1e38..f9e1a94def 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -509,7 +509,10 @@ bool LLViewerInventoryCategory::fetchDescendents()
// This comes from LLInventoryFilter from llfolderview.h
U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
- std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents");
+ // *NOTE
+ // Temporary workaround for bug EXT-2879, see ticket for details.
+ // Commented gAgent.getRegion()->getCapability in order to use the old system.
+ std::string url;//= gAgent.getRegion()->getCapability("WebFetchInventoryDescendents");
if (!url.empty()) //Capability found. Build up LLSD and use it.
{
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 9fc818e1ff..23d02af73d 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -1240,6 +1240,10 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
gInventory.addObserver(opener);
}
+ // Remove script dialog because there is no need in it no more.
+ LLUUID object_id = notification["payload"]["object_id"].asUUID();
+ LLScriptFloaterManager::instance().removeNotificationByObjectId(object_id);
+
delete this;
return false;
}
@@ -1414,7 +1418,11 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const
{
gInventory.addObserver(opener);
}
-
+
+ // Remove script dialog because there is no need in it no more.
+ LLUUID object_id = notification["payload"]["object_id"].asUUID();
+ LLScriptFloaterManager::instance().removeNotificationByObjectId(object_id);
+
delete this;
return false;
}
@@ -1502,7 +1510,18 @@ void inventory_offer_handler(LLOfferInfo* info)
}
}
+ // If mObjectID is null then generate the object_id based on msg to prevent
+ // multiple creation of chiclets for same object.
+ LLUUID object_id = info->mObjectID;
+ if (object_id.isNull())
+ object_id.generate(msg);
+
payload["from_id"] = info->mFromID;
+ // Needed by LLScriptFloaterManager to bind original notification with
+ // faked for toast one.
+ payload["object_id"] = object_id;
+ // Flag indicating that this notification is faked for toast.
+ payload["give_inventory_notification"] = FALSE;
args["OBJECTFROMNAME"] = info->mFromName;
args["NAME"] = info->mFromName;
args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about");
@@ -1543,9 +1562,16 @@ void inventory_offer_handler(LLOfferInfo* info)
// In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages).
info->send_auto_receive_response();
}
-
+
// Pop up inv offer notification and let the user accept (keep), or reject (and silently delete) the inventory.
- LLNotifications::instance().add(p);
+ LLNotifications::instance().add(p);
+
+ // Inform user that there is a script floater via toast system
+ {
+ payload["give_inventory_notification"] = TRUE;
+ LLNotificationPtr notification = LLNotifications::instance().add(p.payload(payload));
+ LLScriptFloaterManager::getInstance()->setNotificationToastId(object_id, notification->getID());
+ }
}
bool lure_callback(const LLSD& notification, const LLSD& response)
@@ -4347,7 +4373,28 @@ void process_money_balance_reply( LLMessageSystem* msg, void** )
// *TODO: Translate
LLSD args;
args["MESSAGE"] = desc;
- LLNotificationsUtil::add("SystemMessage", args);
+
+ // this is a marker to retrieve avatar name from server message:
+ // "<avatar name> paid you L$"
+ const std::string marker = "paid you L$";
+
+ // extract avatar name from system message
+ std::string name = desc.substr(0, desc.find(marker, 0));
+ LLStringUtil::trim(name);
+
+ // if name extracted and name cache contains avatar id send loggable notification
+ LLUUID from_id;
+ if(name.size() > 0 && gCacheName->getUUID(name, from_id))
+ {
+ args["NAME"] = name;
+ LLSD payload;
+ payload["from_id"] = from_id;
+ LLNotificationsUtil::add("PaymentRecived", args, payload);
+ }
+ else
+ {
+ LLNotificationsUtil::add("SystemMessage", args);
+ }
// Once the 'recent' container gets large enough, chop some
// off the beginning.
diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp
index 3554528d19..a0396bc790 100644
--- a/indra/newview/llvoicechannel.cpp
+++ b/indra/newview/llvoicechannel.cpp
@@ -750,6 +750,8 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string
mReceivedCall(FALSE)
{
// make sure URI reflects encoded version of other user's agent id
+ // *NOTE: in case of Avaline call generated SIP URL will be incorrect.
+ // But it will be overridden in LLVoiceChannelP2P::setSessionHandle() called when agent accepts call
setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
}
@@ -867,6 +869,10 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s
}
else
{
+ LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL;
+ // In case of incoming AvaLine call generated URI will be differ from original one.
+ // This is because Avatar-2-Avatar URI is based on avatar UUID but Avaline is not.
+ // See LLVoiceClient::sessionAddedEvent() -> setUUIDFromStringHash()
setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
}
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 cf6a4e45bd..cd297af99d 100644
--- a/indra/newview/skins/default/xui/en/floater_im_container.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_container.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<multi_floater
-background_visible="true"
-bg_color="yellow"
+ can_minimize="false"
can_resize="true"
height="390"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml
index d8534bfe0b..0856049374 100644
--- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml
@@ -22,13 +22,15 @@
bg_readonly_color="ChatHistoryBgColor"
bg_writeable_color="ChatHistoryBgColor"
follows="all"
- left="1"
+ left="5"
top="20"
layout="topleft"
- height="280"
+ height="275"
name="chat_history"
parse_highlights="true"
text_color="ChatHistoryTextColor"
text_readonly_color="ChatHistoryTextColor"
- width="320" />
+ right_widget_pad="5"
+ left_widget_pad="0"
+ width="315" />
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
index 04696ca2e7..1ebc51f504 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
can_resize="true"
- height="300"
+ height="270"
layout="topleft"
+ min_height="146"
+ min_width="190"
name="floater_voice_controls"
title="Voice Controls"
save_visibility="true"
@@ -21,6 +23,10 @@
Conference Call
</string>
<string
+ name="title_peer_2_peer">
+ Call with [NAME]
+ </string>
+ <string
name="no_one_near">
No one near
</string>
@@ -74,6 +80,7 @@
width="20" />
</panel>
<layout_stack
+ animate="false"
bottom="10"
clip="false"
follows="left|right|top"
@@ -82,9 +89,11 @@
orientation="horizontal"
width="262">
<layout_panel
+ auto_resize="false"
follows="left"
layout="topleft"
min_width="24"
+ name="microphone_icon_panel"
top="0"
user_resize="false"
width="24">
@@ -98,9 +107,10 @@
</layout_panel>
<layout_panel
layout="topleft"
+ name="volume_slider_panel"
top="0"
user_resize="false"
- width="258">
+ width="138">
<slider_bar
control_name="AudioLevelMic"
follows="left|right|top"
@@ -113,7 +123,25 @@
tool_tip="Master Volume"
top="0"
value="0.75"
- width="258" />
+ width="138" />
+ </layout_panel>
+ <layout_panel
+ auto_resize="false"
+ layout="topleft"
+ min_width="100"
+ name="leave_btn_panel"
+ top="0"
+ user_resize="false"
+ visible="false"
+ width="100">
+ <button
+ follows="left|right|top"
+ height="24"
+ label="Leave Call"
+ left="0"
+ name="leave_call_btn"
+ top="0"
+ width="100" />
</layout_panel>
</layout_stack>
</panel>
@@ -126,4 +154,13 @@
multi_select="true"
name="speakers_list"
width="282" />
+ <panel
+ filename="panel_avatar_list_item.xml"
+ follows="left|right|top"
+ height="24"
+ layout="topleft"
+ left="0"
+ name="non_avatar_caller"
+ top="70"
+ width="282" />
</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_bottomtray.xml b/indra/newview/skins/default/xui/en/menu_bottomtray.xml
index a7abb223ba..7ef91a1d85 100644
--- a/indra/newview/skins/default/xui/en/menu_bottomtray.xml
+++ b/indra/newview/skins/default/xui/en/menu_bottomtray.xml
@@ -52,4 +52,57 @@
function="CheckControl"
parameter="ShowSnapshotButton" />
</menu_item_check>
+ <menu_item_separator
+ name="Separator" />
+ <menu_item_call
+ label="Cut"
+ name="NearbyChatBar_Cut">
+ <menu_item_call.on_click
+ function="NearbyChatBar.Action"
+ parameter="cut" />
+ <menu_item_call.on_enable
+ function="NearbyChatBar.EnableMenuItem"
+ parameter="can_cut" />
+ </menu_item_call>
+ <menu_item_call
+ label="Copy"
+ name="NearbyChatBar_Copy">
+ <menu_item_call.on_click
+ function="NearbyChatBar.Action"
+ parameter="copy" />
+ <menu_item_call.on_enable
+ function="NearbyChatBar.EnableMenuItem"
+ parameter="can_copy" />
+ </menu_item_call>
+ <menu_item_call
+ label="Paste"
+ name="NearbyChatBar_Paste">
+ <menu_item_call.on_click
+ function="NearbyChatBar.Action"
+ parameter="paste" />
+ <menu_item_call.on_enable
+ function="NearbyChatBar.EnableMenuItem"
+ parameter="can_paste" />
+ </menu_item_call>
+ <menu_item_call
+ label="Delete"
+ name="NearbyChatBar_Delete">
+ <menu_item_call.on_click
+ function="NearbyChatBar.Action"
+ parameter="delete" />
+ <menu_item_call.on_enable
+ function="NearbyChatBar.EnableMenuItem"
+ parameter="can_delete" />
+ </menu_item_call>
+ <menu_item_call
+ label="Select All"
+ name="NearbyChatBar_Select_All">
+ <menu_item_call.on_click
+ function="NearbyChatBar.Action"
+ parameter="select_all" />
+ <menu_item_call.on_enable
+ function="NearbyChatBar.EnableMenuItem"
+ parameter="can_select_all" />
+ </menu_item_call>
+
</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml b/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml
new file mode 100644
index 0000000000..eb5e31b57d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<menu
+ height="101"
+ layout="topleft"
+ left="100"
+ mouse_opaque="false"
+ name="IMChiclet AdHoc Menu"
+ top="724"
+ visible="false"
+ width="128">
+ <menu_item_call
+ label="End Session"
+ layout="topleft"
+ name="End Session">
+ <menu_item_call.on_click
+ function="IMChicletMenu.Action"
+ parameter="end" />
+ </menu_item_call>
+</menu>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 895df62926..f48cc6d4bf 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4598,6 +4598,13 @@ Please select at least one type of content to search (PG, Mature, or Adult).
<notification
icon="notify.tga"
+ name="PaymentRecived"
+ type="notify">
+[MESSAGE]
+ </notification>
+
+ <notification
+ icon="notify.tga"
name="EventNotification"
type="notify">
Event Notification:
diff --git a/indra/newview/skins/default/xui/en/panel_chat_header.xml b/indra/newview/skins/default/xui/en/panel_chat_header.xml
index 95d8b9cb1e..3e6ea84bf2 100644
--- a/indra/newview/skins/default/xui/en/panel_chat_header.xml
+++ b/indra/newview/skins/default/xui/en/panel_chat_header.xml
@@ -8,7 +8,7 @@
label="im_header"
layout="topleft"
name="im_header"
- width="300">
+ width="310">
<avatar_icon
follows="left"
height="18"
diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml
index 4f0d155876..4c2bd67337 100644
--- a/indra/newview/skins/default/xui/en/panel_picks.xml
+++ b/indra/newview/skins/default/xui/en/panel_picks.xml
@@ -162,16 +162,5 @@
tab_stop="false"
top="0"
width="50" />
- <button
- enabled="false"
- follows="bottom|right"
- height="25"
- label="▼"
- layout="topleft"
- name="overflow_btn"
- right="-10"
- tab_stop="false"
- top="0"
- width="30" />
</panel>
</panel>
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 fac0d5c60f..3aa5d3fae4 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
@@ -318,4 +318,37 @@
name="plain_text_chat_history"
top_pad="5"
width="400" />
+ <text
+ left="30"
+ height="20"
+ width="300"
+ top_pad="20">
+ Show IMs in:
+ </text>
+ <radio_group
+ height="30"
+ layout="topleft"
+ left="30"
+ control_name="ChatWindow"
+ name="chat_window"
+ top_pad="10"
+ tool_tip="Show chat in multiple windows(by default) or in one multi-tabbed window (requires restart)"
+ width="331">
+ <radio_item
+ height="16"
+ label="Multiple windows"
+ layout="topleft"
+ left="0"
+ name="radio"
+ top="0"
+ width="150" />
+ <radio_item
+ height="16"
+ label="One window"
+ layout="topleft"
+ left_delta="145"
+ name="radio2"
+ top_delta="0"
+ width="150" />
+ </radio_group>
</panel>