summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rwxr-xr-xindra/newview/CMakeLists.txt6
-rwxr-xr-xindra/newview/app_settings/commands.xml10
-rwxr-xr-xindra/newview/app_settings/settings.xml51
-rwxr-xr-xindra/newview/app_settings/toolbars.xml1
-rwxr-xr-xindra/newview/llagent.cpp13
-rwxr-xr-xindra/newview/llagentui.cpp10
-rwxr-xr-xindra/newview/llagentui.h1
-rwxr-xr-xindra/newview/llappviewer.cpp28
-rwxr-xr-xindra/newview/llconversationmodel.cpp4
-rwxr-xr-xindra/newview/llconversationmodel.h9
-rwxr-xr-xindra/newview/llconversationview.cpp21
-rwxr-xr-xindra/newview/llconversationview.h3
-rwxr-xr-xindra/newview/lldonotdisturbnotificationstorage.cpp16
-rw-r--r--indra/newview/llfacebookconnect.cpp581
-rw-r--r--indra/newview/llfacebookconnect.h106
-rwxr-xr-xindra/newview/llfloaterimcontainer.cpp135
-rwxr-xr-xindra/newview/llfloaterimcontainer.h8
-rwxr-xr-xindra/newview/llfloaterimnearbychat.cpp5
-rwxr-xr-xindra/newview/llfloaterimsession.cpp18
-rwxr-xr-xindra/newview/llfloaterimsessiontab.cpp44
-rwxr-xr-xindra/newview/llfloaterimsessiontab.h3
-rwxr-xr-xindra/newview/llfloatersnapshot.cpp1004
-rwxr-xr-xindra/newview/llfloatersnapshot.h1
-rw-r--r--indra/newview/llfloatersocial.cpp908
-rw-r--r--indra/newview/llfloatersocial.h165
-rwxr-xr-xindra/newview/llfloaterwebcontent.cpp88
-rwxr-xr-xindra/newview/llfloaterwebcontent.h6
-rwxr-xr-xindra/newview/llfolderviewmodelinventory.cpp69
-rwxr-xr-xindra/newview/llfolderviewmodelinventory.h3
-rwxr-xr-xindra/newview/llgroupactions.cpp125
-rwxr-xr-xindra/newview/llgroupactions.h8
-rwxr-xr-xindra/newview/llimview.cpp232
-rwxr-xr-xindra/newview/llinventoryfilter.cpp42
-rwxr-xr-xindra/newview/llinventoryfilter.h17
-rwxr-xr-xindra/newview/llinventorypanel.cpp3
-rwxr-xr-xindra/newview/lllogchat.cpp45
-rwxr-xr-xindra/newview/lllogchat.h3
-rwxr-xr-xindra/newview/llnotificationscripthandler.cpp3
-rwxr-xr-xindra/newview/llpanelmaininventory.cpp7
-rwxr-xr-xindra/newview/llpanelmaininventory.h1
-rwxr-xr-xindra/newview/llpanelpeople.cpp124
-rwxr-xr-xindra/newview/llpanelpeople.h11
-rwxr-xr-xindra/newview/llpanelpeoplemenus.cpp33
-rwxr-xr-xindra/newview/llpanelpeoplemenus.h13
-rwxr-xr-xindra/newview/llpanelprimmediacontrols.cpp18
-rwxr-xr-xindra/newview/llpanelprimmediacontrols.h1
-rwxr-xr-xindra/newview/llparticipantlist.cpp18
-rwxr-xr-xindra/newview/llparticipantlist.h1
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp874
-rw-r--r--indra/newview/llsnapshotlivepreview.h164
-rwxr-xr-xindra/newview/lltoastimpanel.cpp26
-rwxr-xr-xindra/newview/lltoastimpanel.h2
-rwxr-xr-xindra/newview/llurlhistory.cpp23
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp4
-rwxr-xr-xindra/newview/llviewermedia.cpp21
-rwxr-xr-xindra/newview/llviewermenu.cpp2
-rwxr-xr-xindra/newview/llviewermessage.cpp3
-rwxr-xr-xindra/newview/llviewerregion.cpp2
-rwxr-xr-xindra/newview/llviewerwindow.cpp1
-rwxr-xr-xindra/newview/llwebprofile.cpp5
-rwxr-xr-xindra/newview/skins/default/colors.xml15
-rw-r--r--indra/newview/skins/default/textures/icons/Facebook.pngbin0 -> 365 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/map_placeholder.pngbin0 -> 7694 bytes
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml9
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/facebook.pngbin0 -> 2974 bytes
-rw-r--r--indra/newview/skins/default/textures/widgets/horizontal_drag_handle.pngbin0 -> 197 bytes
-rw-r--r--indra/newview/skins/default/textures/widgets/vertical_drag_handle.pngbin0 -> 196 bytes
-rw-r--r--indra/newview/skins/default/xui/en/floater_fbc_web.xml9
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_im_container.xml102
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_im_session.xml86
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_snapshot.xml18
-rw-r--r--indra/newview/skins/default/xui/en/floater_social.xml115
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_web_content.xml4
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml15
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml20
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_people.xml17
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_prim_media_controls.xml26
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_snapshot_options.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_photo.xml152
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_place.xml132
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_status.xml67
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml13
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_tab_view.xml13
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_view.xml127
-rwxr-xr-xindra/newview/tests/lltranslate_test.cpp4
86 files changed, 4715 insertions, 1457 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 89add3e21f..2a969e0d40 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -190,6 +190,7 @@ set(viewer_SOURCE_FILES
llexpandabletextbox.cpp
llexternaleditor.cpp
llface.cpp
+ llfacebookconnect.cpp
llfasttimerview.cpp
llfavoritesbar.cpp
llfeaturemanager.cpp
@@ -271,6 +272,7 @@ set(viewer_SOURCE_FILES
llfloatersettingsdebug.cpp
llfloatersidepanelcontainer.cpp
llfloatersnapshot.cpp
+ llfloatersocial.cpp
llfloatersounddevices.cpp
llfloaterspellchecksettings.cpp
llfloatertelehub.cpp
@@ -506,6 +508,7 @@ set(viewer_SOURCE_FILES
llsidetraypanelcontainer.cpp
llsky.cpp
llslurl.cpp
+ llsnapshotlivepreview.cpp
llspatialpartition.cpp
llspeakers.cpp
llspeakingindicatormanager.cpp
@@ -773,6 +776,7 @@ set(viewer_HEADER_FILES
llexpandabletextbox.h
llexternaleditor.h
llface.h
+ llfacebookconnect.h
llfasttimerview.h
llfavoritesbar.h
llfeaturemanager.h
@@ -854,6 +858,7 @@ set(viewer_HEADER_FILES
llfloatersettingsdebug.h
llfloatersidepanelcontainer.h
llfloatersnapshot.h
+ llfloatersocial.h
llfloatersounddevices.h
llfloaterspellchecksettings.h
llfloatertelehub.h
@@ -1078,6 +1083,7 @@ set(viewer_HEADER_FILES
llsidetraypanelcontainer.h
llsky.h
llslurl.h
+ llsnapshotlivepreview.h
llspatialpartition.h
llspeakers.h
llspeakingindicatormanager.h
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 4659673333..60c942094a 100755
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -216,6 +216,16 @@
is_running_function="Floater.IsOpen"
is_running_parameters="snapshot"
/>
+ <command name="social"
+ available_in_toybox="true"
+ icon="Command_Social_Icon"
+ label_ref="Command_Social_Label"
+ tooltip_ref="Command_Social_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="social"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="social"
+ />
<command name="speak"
available_in_toybox="true"
icon="Command_Speak_Icon"
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 72916ccf8c..7d6fb6c061 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3456,6 +3456,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SLShareHost</key>
+ <map>
+ <key>Comment</key>
+ <string>Host for contacting SL FB services</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string></string>
+ </map>
<key>FastCacheFetchEnabled</key>
<map>
<key>Comment</key>
@@ -3489,16 +3500,27 @@
<key>Value</key>
<real>10.0</real>
</map>
- <key>FilterItemsPerFrame</key>
+ <key>FilterItemsMaxTimePerFrameVisible</key>
<map>
- <key>Comment</key>
- <string>Maximum number of inventory items to match against search filter every frame (lower to increase framerate while searching, higher to improve search speed)</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>S32</string>
- <key>Value</key>
- <integer>500</integer>
+ <key>Comment</key>
+ <string>Max time devoted to items filtering per frame for visible inventory listings (in milliseconds)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>10</integer>
+ </map>
+ <key>FilterItemsMaxTimePerFrameUnvisible</key>
+ <map>
+ <key>Comment</key>
+ <string>Max time devoted to items filtering per frame for non visible inventory listings (in milliseconds)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1</integer>
</map>
<key>FindLandArea</key>
<map>
@@ -12757,6 +12779,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SocialPhotoResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Default resolution when sharing photo using the social floater</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>[i800,i600]</string>
+ </map>
<key>sourceid</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml
index 29c019719d..86f9912815 100755
--- a/indra/newview/app_settings/toolbars.xml
+++ b/indra/newview/app_settings/toolbars.xml
@@ -6,6 +6,7 @@
<command name="speak"/>
<command name="destinations"/>
<command name="people"/>
+ <command name="social"/>
<command name="profile"/>
<command name="move"/>
<command name="view"/>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 3e94c5edf7..f960b31cc7 100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -46,6 +46,7 @@
#include "llenvmanager.h"
#include "llfirstuse.h"
#include "llfloatercamera.h"
+#include "llfloaterimcontainer.h"
#include "llfloaterreg.h"
#include "llfloatertools.h"
#include "llgroupactions.h"
@@ -91,6 +92,7 @@
#include "llworld.h"
#include "llworldmap.h"
#include "stringize.h"
+#include "boost/foreach.hpp"
using namespace LLAvatarAppearanceDefines;
@@ -2037,7 +2039,16 @@ void LLAgent::endAnimationUpdateUI()
{
skip_list.insert(LLFloaterReg::findInstance("mini_map"));
}
-
+
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMContainer::floater_list_t conversations;
+ im_box->getDetachedConversationFloaters(conversations);
+ BOOST_FOREACH(LLFloater* conversation, conversations)
+ {
+ llinfos << "skip_list.insert(session_floater): " << conversation->getTitle() << llendl;
+ skip_list.insert(conversation);
+ }
+
gFloaterView->popVisibleAll(skip_list);
#endif
mViewsPushed = FALSE;
diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp
index b9ec304b7e..3410a37890 100755
--- a/indra/newview/llagentui.cpp
+++ b/indra/newview/llagentui.cpp
@@ -112,6 +112,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const
case LOCATION_FORMAT_NORMAL:
buffer = llformat("%s", region_name.c_str());
break;
+ case LOCATION_FORMAT_NORMAL_COORDS:
+ buffer = llformat("%s (%d, %d, %d)",
+ region_name.c_str(),
+ pos_x, pos_y, pos_z);
+ break;
case LOCATION_FORMAT_NO_COORDS:
buffer = llformat("%s%s%s",
region_name.c_str(),
@@ -143,6 +148,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const
case LOCATION_FORMAT_NORMAL:
buffer = llformat("%s, %s", parcel_name.c_str(), region_name.c_str());
break;
+ case LOCATION_FORMAT_NORMAL_COORDS:
+ buffer = llformat("%s (%d, %d, %d)",
+ parcel_name.c_str(),
+ pos_x, pos_y, pos_z);
+ break;
case LOCATION_FORMAT_NO_MATURITY:
buffer = llformat("%s, %s (%d, %d, %d)",
parcel_name.c_str(),
diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h
index dda5dc1fd1..bb48dad14c 100755
--- a/indra/newview/llagentui.h
+++ b/indra/newview/llagentui.h
@@ -35,6 +35,7 @@ public:
enum ELocationFormat
{
LOCATION_FORMAT_NORMAL, // Parcel
+ LOCATION_FORMAT_NORMAL_COORDS, // Parcel (x, y, z)
LOCATION_FORMAT_LANDMARK, // Parcel, Region
LOCATION_FORMAT_NO_MATURITY, // Parcel, Region (x, y, z)
LOCATION_FORMAT_NO_COORDS, // Parcel, Region - Maturity
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index ffb168b43b..fe357ed8b3 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2654,20 +2654,38 @@ bool LLAppViewer::initConfiguration()
// What can happen is that someone can use IE (or potentially
// other browsers) and do the rough equivalent of command
// injection and steal passwords. Phoenix. SL-55321
+
+ LLSLURL option_slurl;
+
if(clp.hasOption("url"))
{
- LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0]));
+ option_slurl = LLSLURL(clp.getOption("url")[0]);
+ LLStartUp::setStartSLURL(option_slurl);
if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION)
{
LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid());
-
- }
+ }
}
else if(clp.hasOption("slurl"))
{
- LLSLURL start_slurl(clp.getOption("slurl")[0]);
- LLStartUp::setStartSLURL(start_slurl);
+ option_slurl = LLSLURL(clp.getOption("slurl")[0]);
+ LLStartUp::setStartSLURL(option_slurl);
}
+
+ //RN: if we received a URL, hand it off to the existing instance.
+ // don't call anotherInstanceRunning() when doing URL handoff, as
+ // it relies on checking a marker file which will not work when running
+ // out of different directories
+
+ if (option_slurl.isValid() &&
+ (gSavedSettings.getBOOL("SLURLPassToOtherInstance")))
+ {
+ if (sendURLToOtherInstance(option_slurl.getSLURLString()))
+ {
+ // successfully handed off URL to existing instance, exit
+ return false;
+ }
+ }
const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");
if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString())
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp
index c74ce24872..6e95df8383 100755
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -386,6 +386,10 @@ void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags)
addVoiceOptions(items);
items.push_back(std::string("chat_history"));
}
+ else if(this->getType() == CONV_SESSION_NEARBY)
+ {
+ items.push_back(std::string("chat_history"));
+ }
hide_context_entries(menu, items, disabled_items);
}
diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h
index 8766585049..d8cdcdfc97 100755
--- a/indra/newview/llconversationmodel.h
+++ b/indra/newview/llconversationmodel.h
@@ -252,11 +252,10 @@ public:
const std::string& getName() const { return mEmpty; }
const std::string& getFilterText() { return mEmpty; }
void setModified(EFilterModified behavior = FILTER_RESTART) { }
-
- void setFilterCount(S32 count) { }
- S32 getFilterCount() const { return 0; }
- void decrementFilterCount() { }
-
+
+ void resetTime(S32 timeout) { }
+ bool isTimedOut() { return false; }
+
bool isDefault() const { return true; }
bool isNotDefault() const { return false; }
void markDefault() { }
diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp
index b6c53e5e30..42104ea20a 100755
--- a/indra/newview/llconversationview.cpp
+++ b/indra/newview/llconversationview.cpp
@@ -118,6 +118,13 @@ void LLConversationViewSession::setFlashState(bool flash_state)
mFlashTimer->stopFlashing();
}
+void LLConversationViewSession::setHighlightState(bool hihglight_state)
+{
+ mFlashStateOn = hihglight_state;
+ mFlashStarted = true;
+ mFlashTimer->stopFlashing();
+}
+
void LLConversationViewSession::startFlashing()
{
if (isInVisibleChain() && mFlashStateOn && !mFlashStarted)
@@ -340,16 +347,20 @@ void LLConversationViewSession::setVisibleIfDetached(BOOL visible)
{
// Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized
// Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here
- LLFolderViewModelItem* item = mViewModelItem;
- LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID();
- LLFloater* session_floater = LLFloaterIMSessionTab::getConversation(session_uuid);
-
- if (session_floater && !session_floater->getHost() && !session_floater->isMinimized())
+ LLFloater* session_floater = getSessionFloater();
+ if (session_floater && session_floater->isDetachedAndNotMinimized())
{
session_floater->setVisible(visible);
}
}
+LLFloater* LLConversationViewSession::getSessionFloater()
+{
+ LLFolderViewModelItem* item = mViewModelItem;
+ LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID();
+ return LLFloaterIMSessionTab::getConversation(session_uuid);
+}
+
LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id)
{
// This is *not* a general tree parsing algorithm. We search only in the mItems list
diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h
index 3eb2e63792..879d496dc7 100755
--- a/indra/newview/llconversationview.h
+++ b/indra/newview/llconversationview.h
@@ -86,6 +86,9 @@ public:
virtual void refresh();
/*virtual*/ void setFlashState(bool flash_state);
+ void setHighlightState(bool hihglight_state);
+
+ LLFloater* getSessionFloater();
private:
diff --git a/indra/newview/lldonotdisturbnotificationstorage.cpp b/indra/newview/lldonotdisturbnotificationstorage.cpp
index 82affcf068..71bc4f15d2 100755
--- a/indra/newview/lldonotdisturbnotificationstorage.cpp
+++ b/indra/newview/lldonotdisturbnotificationstorage.cpp
@@ -115,7 +115,8 @@ void LLDoNotDisturbNotificationStorage::saveNotifications()
{
LLNotificationPtr notificationPtr = historyIter->second;
- if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired())
+ if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() &&
+ !notificationPtr->isExpired() && !notificationPtr->isPersistent())
{
data.append(notificationPtr->asLLSD(true));
}
@@ -210,12 +211,8 @@ void LLDoNotDisturbNotificationStorage::loadNotifications()
}
- if(imToastExists)
- {
- LLFloaterReg::showInstance("im_container");
- }
-
- if(group_ad_hoc_toast_exists)
+ bool isConversationLoggingAllowed = gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
+ if(group_ad_hoc_toast_exists && isConversationLoggingAllowed)
{
LLFloaterReg::showInstance("conversation");
}
@@ -266,11 +263,6 @@ void LLDoNotDisturbNotificationStorage::updateNotifications()
}
}
- if(imToastExists)
- {
- LLFloaterReg::showInstance("im_container");
- }
-
if(imToastExists || offerExists)
{
make_ui_sound("UISndNewIncomingIMSession");
diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp
new file mode 100644
index 0000000000..abfacdb93c
--- /dev/null
+++ b/indra/newview/llfacebookconnect.cpp
@@ -0,0 +1,581 @@
+/**
+ * @file llfacebookconnect.h
+ * @author Merov, Cho, Gil
+ * @brief Connection to Facebook Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfacebookconnect.h"
+
+#include "llagent.h"
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcommandhandler.h"
+#include "llcontrol.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llurlaction.h"
+#include "llimagepng.h"
+#include "llimagejpeg.h"
+#include "lltrans.h"
+#include "llevents.h"
+#include "llviewerregion.h"
+
+#include "llfloaterwebcontent.h"
+#include "llfloaterreg.h"
+
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sStateWatcher(new LLEventStream("FacebookConnectState"));
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sInfoWatcher(new LLEventStream("FacebookConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sContentWatcher(new LLEventStream("FacebookConnectContent"));
+
+extern LLControlGroup gSavedSettings;
+
+// Local functions
+void log_facebook_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
+{
+ // Note: 302 (redirect) is *not* an error that warrants logging
+ if (status != 302)
+ {
+ LL_WARNS("FacebookConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("facebook_post_success");
+ LLNotificationsUtil::add("FacebookConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectHandler : public LLCommandHandler
+{
+public:
+ LLFacebookConnectHandler() : LLCommandHandler("fbc", UNTRUSTED_THROTTLE) { }
+
+ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
+ {
+ if (tokens.size() > 0)
+ {
+ if (tokens[0].asString() == "connect")
+ {
+ // this command probably came from the fbc_web browser, so close it
+ LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
+ if (fbc_web)
+ {
+ fbc_web->closeFloater();
+ }
+
+ // connect to facebook
+ if (query_map.has("code"))
+ {
+ LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+};
+LLFacebookConnectHandler gFacebookConnectHandler;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookConnectResponder);
+public:
+
+ LLFacebookConnectResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED);
+ }
+ else if (status != 302)
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ log_facebook_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookShareResponder);
+public:
+
+ LLFacebookShareResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTING);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ toast_user_for_success();
+ LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED);
+ }
+ else if (status == 404)
+ {
+ LLFacebookConnect::instance().connectToFacebook();
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POST_FAILED);
+ log_facebook_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookDisconnectResponder);
+public:
+
+ LLFacebookDisconnectResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECTING);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Disconnect successful. content: " << content << LL_ENDL;
+
+ // Clear data
+ LLFacebookConnect::instance().clearInfo();
+ LLFacebookConnect::instance().clearContent();
+ //Notify state change
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED);
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED);
+ log_facebook_connect_error("Disconnect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookConnectedResponder);
+public:
+
+ LLFacebookConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED);
+ }
+ else
+ {
+ // show the facebook login page if not connected yet
+ if (status == 404)
+ {
+ if (mAutoConnect)
+ {
+ LLFacebookConnect::instance().connectToFacebook();
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ log_facebook_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookInfoResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& info)
+ {
+ if (isGoodStatus(status))
+ {
+ llinfos << "Facebook: Info received" << llendl;
+ LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. info: " << info << LL_ENDL;
+ LLFacebookConnect::instance().storeInfo(info);
+ }
+ else
+ {
+ log_facebook_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookFriendsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookFriendsResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Getting Facebook friends successful. content: " << content << LL_ENDL;
+ LLFacebookConnect::instance().storeContent(content);
+ }
+ else
+ {
+ log_facebook_connect_error("Friends", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLFacebookConnect::LLFacebookConnect()
+: mConnectionState(FB_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mContent(),
+ mRefreshInfo(false),
+ mRefreshContent(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLFacebookConnect::openFacebookWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url).show_chrome(true);
+ p.url(url).allow_address_entry(false);
+ p.url(url).allow_back_forward_navigation(false);
+ p.url(url).trusted_content(true);
+ LLFloater *floater = LLFloaterReg::showInstance("fbc_web", p);
+ //the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
+ //So when showing the internal web browser, set focus to it's containing floater "fbc_web". When a mouse event
+ //occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
+ //fbc_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, bool include_read_from_master)
+{
+ static std::string sFacebookConnectUrl = gAgent.getRegion()->getCapability("FacebookConnect");
+
+ //*TODO : Remove this code along with extern and llcontrol.h header
+ std::string host = gSavedSettings.getString("SLShareHost");
+ if (!host.empty())
+ {
+ sFacebookConnectUrl = host + "/fbc/agent/" + gAgentID.asString(); // TEMPORARY HACK FOR FB DEMO - Cho
+ }
+ else
+ {
+ sFacebookConnectUrl = gAgent.getRegion()->getCapability("FacebookConnect");
+ }
+ //End removable part
+
+ std::string url = sFacebookConnectUrl + route;
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ return url;
+}
+
+void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const std::string& auth_state)
+{
+ LLSD body;
+ if (!auth_code.empty())
+ body["code"] = auth_code;
+ if (!auth_state.empty())
+ body["state"] = auth_state;
+
+ LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder());
+}
+
+void LLFacebookConnect::disconnectFromFacebook()
+{
+ LLHTTPClient::del(getFacebookConnectURL("/connection"), new LLFacebookDisconnectResponder());
+}
+
+void LLFacebookConnect::checkConnectionToFacebook(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/connection", true), new LLFacebookConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLFacebookConnect::loadFacebookInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/info", true), new LLFacebookInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFacebookConnect::loadFacebookFriends()
+{
+ if(mRefreshContent)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/friends", true), new LLFacebookFriendsResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFacebookConnect::postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& image, const std::string& message)
+{
+ LLSD body;
+ if (!location.empty())
+ body["location"] = location;
+ if (!name.empty())
+ body["name"] = name;
+ if (!description.empty())
+ body["description"] = description;
+ if (!image.empty())
+ body["image"] = image;
+ if (!message.empty())
+ body["message"] = message;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/checkin", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::sharePhoto(const std::string& image_url, const std::string& caption)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["caption"] = caption;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/photo", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption)
+{
+ std::string imageFormat;
+ if (dynamic_cast<LLImagePNG*>(image.get()))
+ {
+ imageFormat = "png";
+ }
+ else if (dynamic_cast<LLImageJPEG*>(image.get()))
+ {
+ imageFormat = "jpg";
+ }
+ else
+ {
+ llwarns << "Image to upload is not a PNG or JPEG" << llendl;
+ return;
+ }
+
+ // All this code is mostly copied from LLWebProfile::post()
+ const std::string boundary = "----------------------------0123abcdefab";
+
+ LLSD headers;
+ headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+
+ std::ostringstream body;
+
+ // *NOTE: The order seems to matter.
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"caption\"\r\n\r\n"
+ << caption << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
+ << "Content-Type: image/" << imageFormat << "\r\n\r\n";
+
+ // Insert the image data.
+ // *FIX: Treating this as a string will probably screw it up ...
+ U8* image_data = image->getData();
+ for (S32 i = 0; i < image->getDataSize(); ++i)
+ {
+ body << image_data[i];
+ }
+
+ body << "\r\n--" << boundary << "--\r\n";
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = body.str().size();
+ U8 *data = new U8[size];
+ memcpy(data, body.str().data(), size);
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::postRaw(getFacebookConnectURL("/share/photo", true), data, size, new LLFacebookShareResponder(), headers);
+}
+
+void LLFacebookConnect::updateStatus(const std::string& message)
+{
+ LLSD body;
+ body["message"] = message;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/wall", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLFacebookConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLFacebookConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLFacebookConnect::storeContent(const LLSD& content)
+{
+ mContent = content;
+ mRefreshContent = false;
+
+ sContentWatcher->post(content);
+}
+
+const LLSD& LLFacebookConnect::getContent() const
+{
+ return mContent;
+}
+
+void LLFacebookConnect::clearContent()
+{
+ mContent = LLSD();
+}
+
+void LLFacebookConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+ mRefreshContent = true;
+}
+
+void LLFacebookConnect::setConnectionState(LLFacebookConnect::EConnectionState connection_state)
+{
+ if(connection_state == FB_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == FB_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == FB_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+
+ mConnectionState = connection_state;
+}
+
+void LLFacebookConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h
new file mode 100644
index 0000000000..a77ac24167
--- /dev/null
+++ b/indra/newview/llfacebookconnect.h
@@ -0,0 +1,106 @@
+/**
+ * @file llfacebookconnect.h
+ * @author Merov, Cho, Gil
+ * @brief Connection to Facebook Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFACEBOOKCONNECT_H
+#define LL_LLFACEBOOKCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLFacebookConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to get Facebook OpenGraph data.
+ */
+class LLFacebookConnect : public LLSingleton<LLFacebookConnect>
+{
+ LOG_CLASS(LLFacebookConnect);
+public:
+ enum EConnectionState
+ {
+ FB_NOT_CONNECTED = 0,
+ FB_CONNECTION_IN_PROGRESS = 1,
+ FB_CONNECTED = 2,
+ FB_CONNECTION_FAILED = 3,
+ FB_POSTING = 4,
+ FB_POSTED = 5,
+ FB_POST_FAILED = 6,
+ FB_DISCONNECTING = 7,
+ FB_DISCONNECT_FAILED = 8
+ };
+
+ void connectToFacebook(const std::string& auth_code = "", const std::string& auth_state = ""); // Initiate the complete FB connection. Please use checkConnectionToFacebook() in normal use.
+ void disconnectFromFacebook(); // Disconnect from the FBC service.
+ void checkConnectionToFacebook(bool auto_connect = false); // Check if an access token is available on the FBC service. If not, call connectToFacebook().
+
+ void loadFacebookInfo();
+ void loadFacebookFriends();
+ void postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& picture, const std::string& message);
+ void sharePhoto(const std::string& image_url, const std::string& caption);
+ void sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption);
+ void updateStatus(const std::string& message);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void storeContent(const LLSD& content);
+ const LLSD& getContent() const;
+ void clearContent();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == FB_CONNECTION_IN_PROGRESS) || (mConnectionState == FB_POSTING) || (mConnectionState == FB_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openFacebookWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLFacebookConnect>;
+
+ LLFacebookConnect();
+ ~LLFacebookConnect() {};
+ std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ LLSD mContent;
+ bool mRefreshInfo;
+ bool mRefreshContent;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLFACEBOOKCONNECT_H
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index b40789db9c..4131ff314c 100755
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -54,6 +54,7 @@
#include "llworld.h"
#include "llsdserialize.h"
#include "llviewerobjectlist.h"
+#include "boost/foreach.hpp"
//
// LLFloaterIMContainer
@@ -63,7 +64,8 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param
mExpandCollapseBtn(NULL),
mConversationsRoot(NULL),
mConversationsEventStream("ConversationsEvents"),
- mInitialized(false)
+ mInitialized(false),
+ mIsFirstLaunch(true)
{
mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2));
mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2));
@@ -204,6 +206,7 @@ BOOL LLFloaterIMContainer::postBuild()
// a scroller for folder view
LLRect scroller_view_rect = mConversationsListPanel->getRect();
scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ scroller_view_rect.mBottom += getChild<LLLayoutStack>("conversations_pane_buttons_stack")->getRect().getHeight();
LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>());
scroller_params.rect(scroller_view_rect);
@@ -221,7 +224,8 @@ BOOL LLFloaterIMContainer::postBuild()
mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this));
mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn");
mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this));
- getChild<LLButton>("speak_btn")->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this));
+ mSpeakBtn = getChild<LLButton>("speak_btn");
+ mSpeakBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this));
childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this));
@@ -342,8 +346,11 @@ void LLFloaterIMContainer::onStubCollapseButtonClicked()
void LLFloaterIMContainer::onSpeakButtonClicked()
{
- LLAgent::toggleMicrophone("speak");
- updateSpeakBtnState();
+ //LLAgent::toggleMicrophone("speak");
+ //updateSpeakBtnState();
+
+ LLParticipantList* session_model = dynamic_cast<LLParticipantList*>(mConversationsItems[LLUUID(NULL)]);
+ session_model->addTestAvatarAgents();
}
void LLFloaterIMContainer::onExpandCollapseButtonClicked()
{
@@ -659,10 +666,32 @@ void LLFloaterIMContainer::setVisible(BOOL visible)
LLMultiFloater::setVisible(visible);
}
+void LLFloaterIMContainer::getDetachedConversationFloaters(floater_list_t& floaters)
+{
+ typedef conversations_widgets_map::value_type conv_pair;
+ BOOST_FOREACH(conv_pair item, mConversationsWidgets)
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(item.second);
+ if (widget)
+ {
+ LLFloater* session_floater = widget->getSessionFloater();
+ if (session_floater && session_floater->isDetachedAndNotMinimized())
+ {
+ floaters.push_back(session_floater);
+ }
+ }
+ }
+}
+
void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key)
{
LLMultiFloater::setVisibleAndFrontmost(take_focus, key);
selectConversationPair(getSelectedSession(), false, take_focus);
+ if (mInitialized && mIsFirstLaunch)
+ {
+ collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed"));
+ mIsFirstLaunch = false;
+ }
}
void LLFloaterIMContainer::updateResizeLimits()
@@ -779,13 +808,6 @@ void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(bool collapse, S32 d
setCanMinimize(at_least_one_panel_is_expanded);
assignResizeLimits();
-
- // force set correct size for the title after show/hide minimize button
- LLRect cur_rect = getRect();
- LLRect force_rect = cur_rect;
- force_rect.mRight = cur_rect.mRight + 1;
- setRect(force_rect);
- setRect(cur_rect);
}
void LLFloaterIMContainer::assignResizeLimits()
@@ -793,15 +815,12 @@ void LLFloaterIMContainer::assignResizeLimits()
bool is_conv_pane_expanded = !mConversationsPane->isCollapsed();
bool is_msg_pane_expanded = !mMessagesPane->isCollapsed();
- // With two panels visible number of borders is three, because the borders
- // between the panels are merged into one
- S32 number_of_visible_borders = llmin((is_conv_pane_expanded? 2 : 0) + (is_msg_pane_expanded? 2 : 0), 3);
- S32 summary_width_of_visible_borders = number_of_visible_borders * LLPANEL_BORDER_WIDTH;
- S32 conv_pane_target_width = is_conv_pane_expanded?
- (is_msg_pane_expanded?
- mConversationsPane->getRect().getWidth()
- : mConversationsPane->getExpandedMinDim())
- : mConversationsPane->getMinDim();
+ S32 summary_width_of_visible_borders = (is_msg_pane_expanded ? mConversationsStack->getPanelSpacing() : 0) + 1;
+
+ S32 conv_pane_target_width = is_conv_pane_expanded
+ ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() )
+ : mConversationsPane->getMinDim();
+
S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0;
S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders;
@@ -1143,7 +1162,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command,
}
else if("chat_history" == command)
{
- if (selectedIDS.size() > 0)
+ if (selectedIDS.size() > 0)
{
LLAvatarActions::viewChatHistory(selectedIDS.front());
}
@@ -1156,6 +1175,17 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command,
}
}
}
+ //if there is no LLFloaterIMSession* instance for selected conversation it might be Nearby chat
+ else
+ {
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_NEARBY)
+ {
+ if("chat_history" == command)
+ {
+ LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true);
+ }
+ }
+ }
}
void LLFloaterIMContainer::doToSelected(const LLSD& userdata)
@@ -1211,7 +1241,19 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
//Enable Chat history item for ad-hoc and group conversations
if ("can_chat_history" == item && uuids.size() > 0)
{
- return LLLogChat::isTranscriptExist(uuids.front());
+ //Disable menu item if selected participant is user agent
+ if(uuids.front() != gAgentID)
+ {
+ if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_NEARBY)
+ {
+ return LLLogChat::isNearbyTranscriptExist();
+ }
+ else
+ {
+ bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP);
+ return LLLogChat::isTranscriptExist(uuids.front(),is_group);
+ }
+ }
}
// If nothing is selected(and selected item is not group chat), everything needs to be disabled
@@ -1904,7 +1946,6 @@ void LLFloaterIMContainer::reSelectConversation()
void LLFloaterIMContainer::updateSpeakBtnState()
{
- LLButton* mSpeakBtn = getChild<LLButton>("speak_btn");
mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState());
mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak"));
}
@@ -1925,6 +1966,17 @@ void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id,
}
}
+void LLFloaterIMContainer::highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted)
+{
+ //Finds the conversation line item to highlight using the session_id
+ LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id));
+
+ if (widget)
+ {
+ widget->setHighlightState(is_highlighted);
+ }
+}
+
bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget)
{
llassert(conversation_item_widget != NULL);
@@ -1940,23 +1992,28 @@ bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conve
BOOL LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask )
{
+ BOOL handled = FALSE;
+
if(mask == MASK_ALT)
{
if (KEY_RETURN == key )
{
expandConversation();
+ handled = TRUE;
}
if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
{
selectNextorPreviousConversation(true);
+ handled = TRUE;
}
if ((KEY_UP == key) || (KEY_LEFT == key))
{
selectNextorPreviousConversation(false);
+ handled = TRUE;
}
}
- return TRUE;
+ return handled;
}
bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected)
@@ -2013,7 +2070,9 @@ void LLFloaterIMContainer::expandConversation()
}
}
-void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/)
+// For conversations, closeFloater() (linked to Ctrl-W) does not actually close the floater but the active conversation.
+// This is intentional so it doesn't confuse the user. onClickCloseBtn() closes the whole floater.
+void LLFloaterIMContainer::onClickCloseBtn()
{
// Always unminimize before trying to close.
// Most of the time the user will never see this state.
@@ -2022,7 +2081,31 @@ void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/)
LLMultiFloater::setMinimized(FALSE);
}
- LLFloater::closeFloater(app_quitting);
+ LLFloater::closeFloater();
+}
+
+void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/)
+{
+ // Check for currently active session
+ LLUUID session_id = getSelectedSession();
+ // If current session is Nearby Chat or there is only one session remaining, close the floater
+ if (mConversationsItems.size() == 1 || session_id == LLUUID() || app_quitting)
+ {
+ onClickCloseBtn();
+ }
+
+ // Otherwise, close current conversation
+ LLFloaterIMSessionTab* active_conversation = LLFloaterIMSessionTab::getConversation(session_id);
+ if (active_conversation)
+ {
+ active_conversation->closeFloater();
+ }
+}
+
+void LLFloaterIMContainer::handleReshape(const LLRect& rect, bool by_user)
+{
+ LLMultiFloater::handleReshape(rect, by_user);
+ storeRectControl();
}
// EOF
diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h
index e39d20ec35..74c3640bad 100755
--- a/indra/newview/llfloaterimcontainer.h
+++ b/indra/newview/llfloaterimcontainer.h
@@ -63,6 +63,8 @@ public:
/*virtual*/ void setVisible(BOOL visible);
/*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD());
/*virtual*/ void updateResizeLimits();
+ /*virtual*/ void handleReshape(const LLRect& rect, bool by_user);
+
void onCloseFloater(LLUUID& id);
/*virtual*/ void addFloater(LLFloater* floaterp,
@@ -130,6 +132,7 @@ private:
void onStubCollapseButtonClicked();
void processParticipantsStyleUpdate();
void onSpeakButtonClicked();
+ /*virtual*/ void onClickCloseBtn();
void collapseConversationsPane(bool collapse, bool save_is_allowed=true);
@@ -169,6 +172,7 @@ private:
LLButton* mExpandCollapseBtn;
LLButton* mStubCollapseBtn;
+ LLButton* mSpeakBtn;
LLPanel* mStubPanel;
LLTextBox* mStubTextBox;
LLLayoutPanel* mMessagesPane;
@@ -176,6 +180,7 @@ private:
LLLayoutStack* mConversationsStack;
bool mInitialized;
+ bool mIsFirstLaunch;
LLUUID mSelectedSession;
std::string mGeneralTitle;
@@ -190,9 +195,12 @@ public:
void updateSpeakBtnState();
static bool isConversationLoggingAllowed();
void flashConversationItemWidget(const LLUUID& session_id, bool is_flashes);
+ void highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted);
bool isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget);
boost::signals2::connection mMicroChangedSignal;
S32 getConversationListItemSize() { return mConversationsWidgets.size(); }
+ typedef std::list<LLFloater*> floater_list_t;
+ void getDetachedConversationFloaters(floater_list_t& floaters);
private:
LLConversationViewSession* createConversationItemWidget(LLConversationItem* item);
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index 56b0c15cb9..b3a43f6ac5 100755
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -568,7 +568,10 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type )
if (0 == channel)
{
// discard returned "found" boolean
- LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text);
+ if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text))
+ {
+ utf8_revised_text = utf8text;
+ }
}
else
{
diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp
index 8ec85e1160..848d5c34d2 100755
--- a/indra/newview/llfloaterimsession.cpp
+++ b/indra/newview/llfloaterimsession.cpp
@@ -442,8 +442,11 @@ void LLFloaterIMSession::addSessionParticipants(const uuid_vec_t& uuids)
}
else
{
- // remember whom we have invited, to notify others later, when the invited ones actually join
- mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end());
+ if(findInstance(mSessionID))
+ {
+ // remember whom we have invited, to notify others later, when the invited ones actually join
+ mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end());
+ }
inviteToSession(uuids);
}
@@ -469,13 +472,18 @@ void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, con
temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end());
// then we can close the current session
- onClose(false);
+ if(findInstance(mSessionID))
+ {
+ onClose(false);
+
+ // remember whom we have invited, to notify others later, when the invited ones actually join
+ mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end());
+ }
// we start a new session so reset the initialization flag
mSessionInitialized = false;
- // remember whom we have invited, to notify others later, when the invited ones actually join
- mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end());
+
// Start a new ad hoc voice call if we invite new participants to a P2P call,
// or start a text chat otherwise.
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index ce6e639305..cc2859c099 100755
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -212,7 +212,7 @@ void LLFloaterIMSessionTab::assignResizeLimits()
mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed);
S32 participants_pane_target_width = is_participants_pane_collapsed?
- 0 : (mParticipantListPanel->getRect().getWidth() + LLPANEL_BORDER_WIDTH);
+ 0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing());
S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth;
@@ -241,7 +241,10 @@ BOOL LLFloaterIMSessionTab::postBuild()
mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this));
mGearBtn = getChild<LLButton>("gear_btn");
-
+ mAddBtn = getChild<LLButton>("add_btn");
+ mVoiceButton = getChild<LLButton>("voice_call_btn");
+ mTranslationCheckBox = getChild<LLUICtrl>("translate_chat_checkbox_lp");
+
mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel");
mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder");
@@ -372,7 +375,7 @@ void LLFloaterIMSessionTab::draw()
void LLFloaterIMSessionTab::enableDisableCallBtn()
{
- getChildView("voice_call_btn")->setEnabled(
+ mVoiceButton->setEnabled(
mSessionID.notNull()
&& mSession
&& mSession->mSessionInitialized
@@ -758,7 +761,7 @@ void LLFloaterIMSessionTab::reshapeChatLayoutPanel()
void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show)
{
- getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE);
+ mTranslationCheckBox->setVisible(mIsNearbyChat && show);
}
// static
@@ -805,15 +808,10 @@ void LLFloaterIMSessionTab::reloadEmptyFloaters()
void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive)
{
- LLButton* voiceButton = getChild<LLButton>("voice_call_btn");
- voiceButton->setImageOverlay(
- callIsActive? getString("call_btn_stop") : getString("call_btn_start"));
-
- voiceButton->setToolTip(
- callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip"));
+ mVoiceButton->setImageOverlay(callIsActive? getString("call_btn_stop") : getString("call_btn_start"));
+ mVoiceButton->setToolTip(callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip"));
enableDisableCallBtn();
-
}
void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self)
@@ -898,6 +896,7 @@ void LLFloaterIMSessionTab::restoreFloater()
mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon"));
setMessagePaneExpanded(true);
saveCollapsedState();
+ mInputEditor->enableSingleLineMode(false);
enableResizeCtrls(true, true, true);
}
}
@@ -953,8 +952,8 @@ void LLFloaterIMSessionTab::updateGearBtn()
if(prevVisibility != mGearBtn->getVisible())
{
LLRect gear_btn_rect = mGearBtn->getRect();
- LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
- LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
+ LLRect add_btn_rect = mAddBtn->getRect();
+ LLRect call_btn_rect = mVoiceButton->getRect();
S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
S32 right_shift = gear_btn_rect.getWidth() + gap_width;
if(mGearBtn->getVisible())
@@ -968,24 +967,24 @@ void LLFloaterIMSessionTab::updateGearBtn()
add_btn_rect.translate(-right_shift,0);
call_btn_rect.translate(-right_shift,0);
}
- getChild<LLButton>("add_btn")->setRect(add_btn_rect);
- getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
+ mAddBtn->setRect(add_btn_rect);
+ mVoiceButton->setRect(call_btn_rect);
}
}
void LLFloaterIMSessionTab::initBtns()
{
LLRect gear_btn_rect = mGearBtn->getRect();
- LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
- LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
+ LLRect add_btn_rect = mAddBtn->getRect();
+ LLRect call_btn_rect = mVoiceButton->getRect();
S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
S32 right_shift = gear_btn_rect.getWidth() + gap_width;
add_btn_rect.translate(-right_shift,0);
call_btn_rect.translate(-right_shift,0);
- getChild<LLButton>("add_btn")->setRect(add_btn_rect);
- getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
+ mAddBtn->setRect(add_btn_rect);
+ mVoiceButton->setRect(call_btn_rect);
}
// static
@@ -1083,21 +1082,26 @@ void LLFloaterIMSessionTab::saveCollapsedState()
}
BOOL LLFloaterIMSessionTab::handleKeyHere(KEY key, MASK mask )
{
+ BOOL handled = FALSE;
+
if(mask == MASK_ALT)
{
LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
if (KEY_RETURN == key && !isTornOff())
{
floater_container->expandConversation();
+ handled = TRUE;
}
if ((KEY_UP == key) || (KEY_LEFT == key))
{
floater_container->selectNextorPreviousConversation(false);
+ handled = TRUE;
}
if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
{
floater_container->selectNextorPreviousConversation(true);
+ handled = TRUE;
}
}
- return TRUE;
+ return handled;
}
diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h
index 302d5a8066..ba80d2369a 100755
--- a/indra/newview/llfloaterimsessiontab.h
+++ b/indra/newview/llfloaterimsessiontab.h
@@ -182,6 +182,9 @@ protected:
LLButton* mTearOffBtn;
LLButton* mCloseBtn;
LLButton* mGearBtn;
+ LLButton* mAddBtn;
+ LLButton* mVoiceButton;
+ LLUICtrl* mTranslationCheckBox;
private:
// Handling selection and contextual menu
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index d8d62e5bbb..ea385d7baf 100755
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -28,60 +28,23 @@
#include "llfloatersnapshot.h"
-#include "llfloaterreg.h"
-
-// Viewer includes
#include "llagent.h"
-#include "llagentcamera.h"
-#include "llcallbacklist.h"
-#include "llcriticaldamp.h"
-#include "llfloaterperms.h"
-#include "llui.h"
-#include "llfocusmgr.h"
-#include "llbutton.h"
+#include "llfacebookconnect.h"
+#include "llfloaterreg.h"
+#include "llfloatersocial.h"
+#include "llcheckboxctrl.h"
#include "llcombobox.h"
-#include "lleconomy.h"
-#include "lllandmarkactions.h"
-#include "llpanelsnapshot.h"
+#include "llpostcard.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
#include "llsidetraypanelcontainer.h"
-#include "llsliderctrl.h"
+#include "llsnapshotlivepreview.h"
#include "llspinctrl.h"
#include "llviewercontrol.h"
-#include "lluictrlfactory.h"
-#include "llviewerstats.h"
-#include "llviewercamera.h"
-#include "llviewerwindow.h"
-#include "llviewermenufile.h" // upload_new_resource()
-#include "llcheckboxctrl.h"
-#include "llslurl.h"
#include "lltoolfocus.h"
#include "lltoolmgr.h"
-#include "llwebsharing.h"
-#include "llworld.h"
-#include "llagentui.h"
-
-// Linden library includes
-#include "llfontgl.h"
-#include "llsys.h"
-#include "llrender.h"
-#include "v3dmath.h"
-#include "llmath.h"
-#include "lldir.h"
-#include "llsdserialize.h"
-#include "llgl.h"
-#include "llglheaders.h"
-#include "llimagejpeg.h"
-#include "llimagepng.h"
-#include "llimagebmp.h"
-#include "llimagej2c.h"
-#include "lllocalcliprect.h"
-#include "llnotificationsutil.h"
-#include "llpostcard.h"
-#include "llresmgr.h" // LLLocale
-#include "llvfile.h"
-#include "llvfs.h"
#include "llwebprofile.h"
-#include "llwindow.h"
+#include "llwebsharing.h"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
@@ -91,949 +54,12 @@ LLSnapshotFloaterView* gSnapshotFloaterView = NULL;
const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f;
-F32 SHINE_TIME = 0.5f;
-F32 SHINE_WIDTH = 0.6f;
-F32 SHINE_OPACITY = 0.3f;
-F32 FALL_TIME = 0.6f;
-S32 BORDER_WIDTH = 6;
-
const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512
static LLDefaultChildRegistry::Register<LLSnapshotFloaterView> r("snapshot_floater_view");
-///----------------------------------------------------------------------------
-/// Class LLSnapshotLivePreview
-///----------------------------------------------------------------------------
-class LLSnapshotLivePreview : public LLView
-{
- LOG_CLASS(LLSnapshotLivePreview);
-public:
- enum ESnapshotType
- {
- SNAPSHOT_POSTCARD,
- SNAPSHOT_TEXTURE,
- SNAPSHOT_LOCAL,
- SNAPSHOT_WEB
- };
-
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Params()
- {
- name = "snapshot_live_preview";
- mouse_opaque = false;
- }
- };
-
-
- LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
- ~LLSnapshotLivePreview();
-
- /*virtual*/ void draw();
- /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
-
- void setSize(S32 w, S32 h);
- void setWidth(S32 w) { mWidth[mCurImageIndex] = w; }
- void setHeight(S32 h) { mHeight[mCurImageIndex] = h; }
- void getSize(S32& w, S32& h) const;
- S32 getWidth() const { return mWidth[mCurImageIndex]; }
- S32 getHeight() const { return mHeight[mCurImageIndex]; }
- S32 getDataSize() const { return mDataSize; }
- void setMaxImageSize(S32 size) ;
- S32 getMaxImageSize() {return mMaxImageSize ;}
-
- ESnapshotType getSnapshotType() const { return mSnapshotType; }
- LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; }
- BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
- BOOL isSnapshotActive() { return mSnapshotActive; }
- LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; }
- S32 getThumbnailWidth() const { return mThumbnailWidth ; }
- S32 getThumbnailHeight() const { return mThumbnailHeight ; }
- BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
- BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
- LLViewerTexture* getCurrentImage();
- F32 getImageAspect();
- F32 getAspect() ;
- const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; }
- BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; }
- void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; }
- const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
-
- void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
- void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
- void setSnapshotQuality(S32 quality);
- void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
- void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
- void saveWeb();
- void saveTexture();
- BOOL saveLocal();
-
- LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
- LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
-
- /// Sets size of preview thumbnail image and thhe surrounding rect.
- BOOL setThumbnailImageSize() ;
- void generateThumbnailImage(BOOL force_update = FALSE) ;
- void resetThumbnailImage() { mThumbnailImage = NULL ; }
- void drawPreviewRect(S32 offset_x, S32 offset_y) ;
-
- // Returns TRUE when snapshot generated, FALSE otherwise.
- static BOOL onIdle( void* snapshot_preview );
-
- // callback for region name resolve
- void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z);
-
-private:
- LLColor4 mColor;
- LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
- LLRect mImageRect[2];
- S32 mWidth[2];
- S32 mHeight[2];
- BOOL mImageScaled[2];
- S32 mMaxImageSize ;
-
- //thumbnail image
- LLPointer<LLViewerTexture> mThumbnailImage ;
- S32 mThumbnailWidth ;
- S32 mThumbnailHeight ;
- LLRect mPreviewRect ;
- BOOL mThumbnailUpdateLock ;
- BOOL mThumbnailUpToDate ;
-
- S32 mCurImageIndex;
- LLPointer<LLImageRaw> mPreviewImage;
- LLPointer<LLImageRaw> mPreviewImageEncoded;
- LLPointer<LLImageFormatted> mFormattedImage;
- LLFrameTimer mSnapshotDelayTimer;
- S32 mShineCountdown;
- LLFrameTimer mShineAnimTimer;
- F32 mFlashAlpha;
- BOOL mNeedsFlash;
- LLVector3d mPosTakenGlobal;
- S32 mSnapshotQuality;
- S32 mDataSize;
- ESnapshotType mSnapshotType;
- LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat;
- BOOL mSnapshotUpToDate;
- LLFrameTimer mFallAnimTimer;
- LLVector3 mCameraPos;
- LLQuaternion mCameraRot;
- BOOL mSnapshotActive;
- LLViewerWindow::ESnapshotType mSnapshotBufferType;
-
-public:
- static std::set<LLSnapshotLivePreview*> sList;
- BOOL mKeepAspectRatio ;
-};
-
-std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
-
-LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p)
-: LLView(p),
- mColor(1.f, 0.f, 0.f, 0.5f),
- mCurImageIndex(0),
- mPreviewImage(NULL),
- mThumbnailImage(NULL) ,
- mThumbnailWidth(0),
- mThumbnailHeight(0),
- mPreviewImageEncoded(NULL),
- mFormattedImage(NULL),
- mShineCountdown(0),
- mFlashAlpha(0.f),
- mNeedsFlash(TRUE),
- mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
- mDataSize(0),
- mSnapshotType(SNAPSHOT_POSTCARD),
- mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))),
- mSnapshotUpToDate(FALSE),
- mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
- mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
- mSnapshotActive(FALSE),
- mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
-{
- setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
- mSnapshotDelayTimer.setTimerExpirySec(0.0f);
- mSnapshotDelayTimer.start();
-// gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
- sList.insert(this);
- setFollowsAll();
- mWidth[0] = gViewerWindow->getWindowWidthRaw();
- mWidth[1] = gViewerWindow->getWindowWidthRaw();
- mHeight[0] = gViewerWindow->getWindowHeightRaw();
- mHeight[1] = gViewerWindow->getWindowHeightRaw();
- mImageScaled[0] = FALSE;
- mImageScaled[1] = FALSE;
-
- mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
- mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
- mThumbnailUpdateLock = FALSE ;
- mThumbnailUpToDate = FALSE ;
-}
-
-LLSnapshotLivePreview::~LLSnapshotLivePreview()
-{
- // delete images
- mPreviewImage = NULL;
- mPreviewImageEncoded = NULL;
- mFormattedImage = NULL;
-
-// gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
- sList.erase(this);
-}
-
-void LLSnapshotLivePreview::setMaxImageSize(S32 size)
-{
- if(size < MAX_SNAPSHOT_IMAGE_SIZE)
- {
- mMaxImageSize = size;
- }
- else
- {
- mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
- }
-}
-
-LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
-{
- return mViewerImage[mCurImageIndex];
-}
-
-F32 LLSnapshotLivePreview::getAspect()
-{
- F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
- F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
-
- if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
- {
- return image_aspect_ratio;
- }
- else
- {
- return window_aspect_ratio;
- }
-}
-
-F32 LLSnapshotLivePreview::getImageAspect()
-{
- if (!getCurrentImage())
- {
- return 0.f;
- }
-
- return getAspect() ;
-}
-
-void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
-{
- // Invalidate current image.
- lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
- if (getSnapshotUpToDate())
- {
- S32 old_image_index = mCurImageIndex;
- mCurImageIndex = (mCurImageIndex + 1) % 2;
- setSize(mWidth[old_image_index], mHeight[old_image_index]);
- mFallAnimTimer.start();
- }
- mSnapshotUpToDate = FALSE;
-
- // Update snapshot source rect depending on whether we keep the aspect ratio.
- LLRect& rect = mImageRect[mCurImageIndex];
- rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
-
- F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
- F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
-
- if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
- {
- if (image_aspect_ratio > window_aspect_ratio)
- {
- // trim off top and bottom
- S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
- rect.mBottom += (getRect().getHeight() - new_height) / 2;
- rect.mTop -= (getRect().getHeight() - new_height) / 2;
- }
- else if (image_aspect_ratio < window_aspect_ratio)
- {
- // trim off left and right
- S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
- rect.mLeft += (getRect().getWidth() - new_width) / 2;
- rect.mRight -= (getRect().getWidth() - new_width) / 2;
- }
- }
-
- // Stop shining animation.
- mShineAnimTimer.stop();
-
- // Update snapshot if requested.
- if (new_snapshot)
- {
- mSnapshotDelayTimer.start();
- mSnapshotDelayTimer.setTimerExpirySec(delay);
- LLFloaterSnapshot::preUpdate();
- }
-
- // Update thumbnail if requested.
- if(new_thumbnail)
- {
- mThumbnailUpToDate = FALSE ;
- }
-}
-
-void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
-{
- llclamp(quality, 0, 100);
- if (quality != mSnapshotQuality)
- {
- mSnapshotQuality = quality;
- gSavedSettings.setS32("SnapshotQuality", quality);
- mSnapshotUpToDate = FALSE;
- }
-}
-
-void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
-{
- F32 line_width ;
- glGetFloatv(GL_LINE_WIDTH, &line_width) ;
- glLineWidth(2.0f * line_width) ;
- LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ;
- gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y,
- mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ;
- glLineWidth(line_width) ;
-
- //draw four alpha rectangles to cover areas outside of the snapshot image
- if(!mKeepAspectRatio)
- {
- LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ;
- S32 dwl = 0, dwr = 0 ;
- if(mThumbnailWidth > mPreviewRect.getWidth())
- {
- dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ;
- dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ;
-
- gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y,
- mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
- gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
- }
-
- if(mThumbnailHeight > mPreviewRect.getHeight())
- {
- S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ;
- gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y ,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ;
-
- dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ;
- gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ;
- }
- }
-}
-
-//called when the frame is frozen.
-void LLSnapshotLivePreview::draw()
-{
- if (getCurrentImage() &&
- mPreviewImageEncoded.notNull() &&
- getSnapshotUpToDate())
- {
- LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
- gl_rect_2d(getRect(), bg_color);
- const LLRect& rect = getImageRect();
- LLRect shadow_rect = rect;
- shadow_rect.stretch(BORDER_WIDTH);
- gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
-
- LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
- gGL.color4fv(image_color.mV);
- gGL.getTexUnit(0)->bind(getCurrentImage());
- // calculate UV scale
- F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f);
- F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f);
- gGL.pushMatrix();
- {
- gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
- gGL.begin(LLRender::QUADS);
- {
- gGL.texCoord2f(uv_width, uv_height);
- gGL.vertex2i(rect.getWidth(), rect.getHeight() );
-
- gGL.texCoord2f(0.f, uv_height);
- gGL.vertex2i(0, rect.getHeight() );
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
-
- gGL.texCoord2f(uv_width, 0.f);
- gGL.vertex2i(rect.getWidth(), 0);
- }
- gGL.end();
- }
- gGL.popMatrix();
-
- gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha);
- gl_rect_2d(getRect());
- if (mNeedsFlash)
- {
- if (mFlashAlpha < 1.f)
- {
- mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
- }
- else
- {
- mNeedsFlash = FALSE;
- }
- }
- else
- {
- mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
- }
-
- // Draw shining animation if appropriate.
- if (mShineCountdown > 0)
- {
- mShineCountdown--;
- if (mShineCountdown == 0)
- {
- mShineAnimTimer.start();
- }
- }
- else if (mShineAnimTimer.getStarted())
- {
- lldebugs << "Drawing shining animation" << llendl;
- F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
-
- // draw "shine" effect
- LLLocalClipRect clip(getLocalRect());
- {
- // draw diagonal stripe with gradient that passes over screen
- S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
- S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
- S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
- S32 y1 = 0;
- S32 y2 = gViewerWindow->getWindowHeightScaled();
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin(LLRender::QUADS);
- {
- gGL.color4f(1.f, 1.f, 1.f, 0.f);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
- gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.vertex2i(x2, y1);
-
- gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
- gGL.vertex2i(x2, y1);
- gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.color4f(1.f, 1.f, 1.f, 0.f);
- gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.vertex2i(x3, y1);
- }
- gGL.end();
- }
-
- // if we're at the end of the animation, stop
- if (shine_interp >= 1.f)
- {
- mShineAnimTimer.stop();
- }
- }
- }
-
- // draw framing rectangle
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.color4f(1.f, 1.f, 1.f, 1.f);
- const LLRect& outline_rect = getImageRect();
- gGL.begin(LLRender::QUADS);
- {
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
-
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
-
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
-
- gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- }
- gGL.end();
- }
-
- // draw old image dropping away
- if (mFallAnimTimer.getStarted())
- {
- S32 old_image_index = (mCurImageIndex + 1) % 2;
- if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
- {
- lldebugs << "Drawing fall animation" << llendl;
- F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
- F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
- LLColor4 image_color(1.f, 1.f, 1.f, alpha);
- gGL.color4fv(image_color.mV);
- gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]);
- // calculate UV scale
- // *FIX get this to work with old image
- BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
- F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
- F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
- gGL.pushMatrix();
- {
- LLRect& rect = mImageRect[old_image_index];
- gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
- gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
- gGL.begin(LLRender::QUADS);
- {
- gGL.texCoord2f(uv_width, uv_height);
- gGL.vertex2i(rect.getWidth(), rect.getHeight() );
-
- gGL.texCoord2f(0.f, uv_height);
- gGL.vertex2i(0, rect.getHeight() );
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
- gGL.texCoord2f(uv_width, 0.f);
- gGL.vertex2i(rect.getWidth(), 0);
- }
- gGL.end();
- }
- gGL.popMatrix();
- }
- }
-}
-
-/*virtual*/
-void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- LLRect old_rect = getRect();
- LLView::reshape(width, height, called_from_parent);
- if (old_rect.getWidth() != width || old_rect.getHeight() != height)
- {
- lldebugs << "window reshaped, updating thumbnail" << llendl;
- updateSnapshot(FALSE, TRUE);
- }
-}
-
-BOOL LLSnapshotLivePreview::setThumbnailImageSize()
-{
- if(getWidth() < 10 || getHeight() < 10)
- {
- return FALSE ;
- }
- S32 window_width = gViewerWindow->getWindowWidthRaw() ;
- S32 window_height = gViewerWindow->getWindowHeightRaw() ;
-
- F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
-
- // UI size for thumbnail
- // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h.
- const LLRect& thumbnail_rect = LLFloaterSnapshot::getThumbnailPlaceholderRect();
- S32 max_width = thumbnail_rect.getWidth();
- S32 max_height = thumbnail_rect.getHeight();
-
- if (window_aspect_ratio > (F32)max_width / max_height)
- {
- // image too wide, shrink to width
- mThumbnailWidth = max_width;
- mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
- }
- else
- {
- // image too tall, shrink to height
- mThumbnailHeight = max_height;
- mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
- }
-
- if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
- {
- return FALSE ;//if the window is too small, ignore thumbnail updating.
- }
-
- S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
- if(!mKeepAspectRatio)
- {
- F32 ratio_x = (F32)getWidth() / window_width ;
- F32 ratio_y = (F32)getHeight() / window_height ;
-
- //if(getWidth() > window_width ||
- // getHeight() > window_height )
- {
- if(ratio_x > ratio_y)
- {
- top = (S32)(top * ratio_y / ratio_x) ;
- }
- else
- {
- right = (S32)(right * ratio_x / ratio_y) ;
- }
- }
- //else
- //{
- // right = (S32)(right * ratio_x) ;
- // top = (S32)(top * ratio_y) ;
- //}
- left = (S32)((mThumbnailWidth - right) * 0.5f) ;
- bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
- top += bottom ;
- right += left ;
- }
- mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ;
-
- return TRUE ;
-}
-
-void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
-{
- if(mThumbnailUpdateLock) //in the process of updating
- {
- return ;
- }
- if(getThumbnailUpToDate() && !force_update)//already updated
- {
- return ;
- }
- if(getWidth() < 10 || getHeight() < 10)
- {
- return ;
- }
-
- ////lock updating
- mThumbnailUpdateLock = TRUE ;
-
- if(!setThumbnailImageSize())
- {
- mThumbnailUpdateLock = FALSE ;
- mThumbnailUpToDate = TRUE ;
- return ;
- }
-
- if(mThumbnailImage)
- {
- resetThumbnailImage() ;
- }
-
- LLPointer<LLImageRaw> raw = new LLImageRaw;
- if(!gViewerWindow->thumbnailSnapshot(raw,
- mThumbnailWidth, mThumbnailHeight,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE,
- mSnapshotBufferType) )
- {
- raw = NULL ;
- }
-
- if(raw)
- {
- raw->expandToPowerOfTwo();
- mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
- mThumbnailUpToDate = TRUE ;
- }
-
- //unlock updating
- mThumbnailUpdateLock = FALSE ;
-}
-
-
-// Called often. Checks whether it's time to grab a new snapshot and if so, does it.
-// Returns TRUE if new snapshot generated, FALSE otherwise.
-//static
-BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
-{
- LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
- if (previewp->getWidth() == 0 || previewp->getHeight() == 0)
- {
- llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl;
- return FALSE;
- }
-
- // If we're in freeze-frame mode and camera has moved, update snapshot.
- LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
- LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
- if (gSavedSettings.getBOOL("FreezeTime") &&
- (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
- {
- previewp->mCameraPos = new_camera_pos;
- previewp->mCameraRot = new_camera_rot;
- // request a new snapshot whenever the camera moves, with a time delay
- BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
- lldebugs << "camera moved, updating thumbnail" << llendl;
- previewp->updateSnapshot(
- autosnap, // whether a new snapshot is needed or merely invalidate the existing one
- FALSE, // or if 1st arg is false, whether to produce a new thumbnail image.
- autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true.
- }
-
- // see if it's time yet to snap the shot and bomb out otherwise.
- previewp->mSnapshotActive =
- (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
- && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
- if ( ! previewp->mSnapshotActive)
- {
- return FALSE;
- }
-
- // time to produce a snapshot
- previewp->setThumbnailImageSize();
-
- lldebugs << "producing snapshot" << llendl;
- if (!previewp->mPreviewImage)
- {
- previewp->mPreviewImage = new LLImageRaw;
- }
-
- if (!previewp->mPreviewImageEncoded)
- {
- previewp->mPreviewImageEncoded = new LLImageRaw;
- }
-
- previewp->setVisible(FALSE);
- previewp->setEnabled(FALSE);
-
- previewp->getWindow()->incBusyCount();
- previewp->setImageScaled(FALSE);
-
- // grab the raw image and encode it into desired format
- if(gViewerWindow->rawSnapshot(
- previewp->mPreviewImage,
- previewp->getWidth(),
- previewp->getHeight(),
- previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
- previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE,
- previewp->mSnapshotBufferType,
- previewp->getMaxImageSize()))
- {
- previewp->mPreviewImageEncoded->resize(
- previewp->mPreviewImage->getWidth(),
- previewp->mPreviewImage->getHeight(),
- previewp->mPreviewImage->getComponents());
-
- if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
- {
- lldebugs << "Encoding new image of format J2C" << llendl;
- LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
- LLPointer<LLImageRaw> scaled = new LLImageRaw(
- previewp->mPreviewImage->getData(),
- previewp->mPreviewImage->getWidth(),
- previewp->mPreviewImage->getHeight(),
- previewp->mPreviewImage->getComponents());
-
- scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
- previewp->setImageScaled(TRUE);
- if (formatted->encode(scaled, 0.f))
- {
- previewp->mDataSize = formatted->getDataSize();
- formatted->decode(previewp->mPreviewImageEncoded, 0);
- }
- }
- else
- {
- // delete any existing image
- previewp->mFormattedImage = NULL;
- // now create the new one of the appropriate format.
- LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
- lldebugs << "Encoding new image of format " << format << llendl;
-
- switch(format)
- {
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
- previewp->mFormattedImage = new LLImagePNG();
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
- previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
- previewp->mFormattedImage = new LLImageBMP();
- break;
- }
- if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0))
- {
- previewp->mDataSize = previewp->mFormattedImage->getDataSize();
- // special case BMP to copy instead of decode otherwise decode will crash.
- if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
- {
- previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage);
- }
- else
- {
- previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0);
- }
- }
- }
-
- LLPointer<LLImageRaw> scaled = new LLImageRaw(
- previewp->mPreviewImageEncoded->getData(),
- previewp->mPreviewImageEncoded->getWidth(),
- previewp->mPreviewImageEncoded->getHeight(),
- previewp->mPreviewImageEncoded->getComponents());
-
- if(!scaled->isBufferInvalid())
- {
- // leave original image dimensions, just scale up texture buffer
- if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
- {
- // go ahead and shrink image to appropriate power of 2 for display
- scaled->biasedScaleToPowerOfTwo(1024);
- previewp->setImageScaled(TRUE);
- }
- else
- {
- // expand image but keep original image data intact
- scaled->expandToPowerOfTwo(1024, FALSE);
- }
-
- previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
- LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
- gGL.getTexUnit(0)->bind(curr_preview_image);
- if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE)
- {
- curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT);
- }
- else
- {
- curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- }
- curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
-
- previewp->mSnapshotUpToDate = TRUE;
- previewp->generateThumbnailImage(TRUE) ;
-
- previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
- previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
- }
- }
- previewp->getWindow()->decBusyCount();
- // only show fullscreen preview when in freeze frame mode
- previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
- previewp->mSnapshotDelayTimer.stop();
- previewp->mSnapshotActive = FALSE;
-
- if(!previewp->getThumbnailUpToDate())
- {
- previewp->generateThumbnailImage() ;
- }
- lldebugs << "done creating snapshot" << llendl;
- LLFloaterSnapshot::postUpdate();
-
- return TRUE;
-}
-
-void LLSnapshotLivePreview::setSize(S32 w, S32 h)
-{
- lldebugs << "setSize(" << w << ", " << h << ")" << llendl;
- setWidth(w);
- setHeight(h);
-}
-
-void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
-{
- w = getWidth();
- h = getHeight();
-}
-
-void LLSnapshotLivePreview::saveTexture()
-{
- lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl;
- // gen a new uuid for this asset
- LLTransactionID tid;
- tid.generate();
- LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
-
- LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
- LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(),
- mPreviewImage->getWidth(),
- mPreviewImage->getHeight(),
- mPreviewImage->getComponents());
-
- scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
- lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl;
-
- if (formatted->encode(scaled, 0.0f))
- {
- LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
- std::string pos_string;
- LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL);
- std::string who_took_it;
- LLAgentUI::buildFullname(who_took_it);
- LLAssetStorage::LLStoreAssetCallback callback = NULL;
- S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
- void *userdata = NULL;
- upload_new_resource(tid, // tid
- LLAssetType::AT_TEXTURE,
- "Snapshot : " + pos_string,
- "Taken by " + who_took_it + " at " + pos_string,
- 0,
- LLFolderType::FT_SNAPSHOT_CATEGORY,
- LLInventoryType::IT_SNAPSHOT,
- PERM_ALL, // Note: Snapshots to inventory is a special case of content upload
- LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads
- LLFloaterPerms::getEveryonePerms(),
- "Snapshot : " + pos_string,
- callback, expected_upload_cost, userdata);
- gViewerWindow->playSnapshotAnimAndSound();
- }
- else
- {
- LLNotificationsUtil::add("ErrorEncodingSnapshot");
- llwarns << "Error encoding snapshot" << llendl;
- }
-
- LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
-
- mDataSize = 0;
-}
-
-BOOL LLSnapshotLivePreview::saveLocal()
-{
- BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
-
- if(success)
- {
- gViewerWindow->playSnapshotAnimAndSound();
- }
- return success;
-}
-
-void LLSnapshotLivePreview::saveWeb()
-{
- // *FIX: Will break if the window closes because of CloseSnapshotOnKeep!
- // Needs to pass on ownership of the image.
- LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get());
- if(!jpg)
- {
- llwarns << "Formatted image not a JPEG" << llendl;
- return;
- }
-
- LLSD metadata;
- metadata["description"] = getChild<LLLineEditor>("description")->getText();
-
- LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(),
- boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4));
-
- gViewerWindow->playSnapshotAnimAndSound();
-}
-
-void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z)
-{
- metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString();
-
- LLWebSharing::instance().shareSnapshot(snapshot, metadata);
-}
///----------------------------------------------------------------------------
/// Class LLFloaterSnapshot::Impl
@@ -2037,7 +1063,7 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot"));
childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
-
+
LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1));
LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1));
@@ -2070,6 +1096,9 @@ BOOL LLFloaterSnapshot::postBuild()
impl.updateControls(this);
impl.updateLayout(this);
+
+ previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
+
return TRUE;
}
@@ -2235,7 +1264,9 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
void LLFloaterSnapshot::update()
{
LLFloaterSnapshot* inst = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
- if (!inst)
+ LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+
+ if (!inst && !floater_social)
return;
BOOL changed = FALSE;
@@ -2245,7 +1276,8 @@ void LLFloaterSnapshot::update()
{
changed |= LLSnapshotLivePreview::onIdle(*iter);
}
- if(changed)
+
+ if (inst && changed)
{
lldebugs << "changed" << llendl;
inst->impl.updateControls(inst);
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
index afe135fa40..82af8c7a9d 100755
--- a/indra/newview/llfloatersnapshot.h
+++ b/indra/newview/llfloatersnapshot.h
@@ -27,7 +27,6 @@
#ifndef LL_LLFLOATERSNAPSHOT_H
#define LL_LLFLOATERSNAPSHOT_H
-#include "llimage.h"
#include "llfloater.h"
class LLSpinCtrl;
diff --git a/indra/newview/llfloatersocial.cpp b/indra/newview/llfloatersocial.cpp
new file mode 100644
index 0000000000..4c17d9e40d
--- /dev/null
+++ b/indra/newview/llfloatersocial.cpp
@@ -0,0 +1,908 @@
+/**
+* @file llfloatersocial.cpp
+* @brief Implementation of llfloatersocial
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersocial.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfacebookconnect.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llplugincookiestore.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llsnapshotlivepreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+
+static LLRegisterPanelClassWrapper<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel");
+static LLRegisterPanelClassWrapper<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel");
+static LLRegisterPanelClassWrapper<LLSocialCheckinPanel> t_panel_checkin("llsocialcheckinpanel");
+static LLRegisterPanelClassWrapper<LLSocialAccountPanel> t_panel_account("llsocialaccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_CHECKIN_LOCATION_URL = "http://maps.secondlife.com/";
+const std::string DEFAULT_CHECKIN_ICON_URL = "http://map.secondlife.com.s3.amazonaws.com/map_placeholder.png";
+
+std::string get_map_url()
+{
+ LLVector3d center_agent;
+ if (gAgent.getRegion())
+ {
+ center_agent = gAgent.getRegion()->getCenterGlobal();
+ }
+ int x_pos = center_agent[0] / 256.0;
+ int y_pos = center_agent[1] / 256.0;
+ std::string map_url = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-1-%d-%d-objects.jpg", x_pos, y_pos);
+ return map_url;
+}
+
+///////////////////////////
+//LLSocialStatusPanel//////
+///////////////////////////
+
+LLSocialStatusPanel::LLSocialStatusPanel() :
+ mMessageTextEditor(NULL),
+ mPostButton(NULL),
+ mCancelButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLSocialStatusPanel::onSend, this));
+}
+
+BOOL LLSocialStatusPanel::postBuild()
+{
+ mMessageTextEditor = getChild<LLUICtrl>("status_message");
+ mPostButton = getChild<LLUICtrl>("post_status_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_status_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialStatusPanel::draw()
+{
+ if (mMessageTextEditor && mPostButton && mCancelButton)
+ {
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ std::string message = mMessageTextEditor->getValue().asString();
+ mMessageTextEditor->setEnabled(no_ongoing_connection);
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mPostButton->setEnabled(no_ongoing_connection && !message.empty());
+ }
+
+ LLPanel::draw();
+}
+
+void LLSocialStatusPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialStatusPanel", boost::bind(&LLSocialStatusPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendStatus();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendStatus();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialStatusPanel::sendStatus()
+{
+ std::string message = mMessageTextEditor->getValue().asString();
+ if (!message.empty())
+ {
+ LLFacebookConnect::instance().updateStatus(message);
+ }
+}
+
+void LLSocialStatusPanel::clearAndClose()
+{
+ mMessageTextEditor->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+///////////////////////////
+//LLSocialPhotoPanel///////
+///////////////////////////
+
+LLSocialPhotoPanel::LLSocialPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mCaptionTextBox(NULL),
+mLocationCheckbox(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLSocialPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLSocialPhotoPanel::onClickNewSnapshot, this));
+}
+
+LLSocialPhotoPanel::~LLSocialPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLSocialPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLSocialPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLSocialPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mCaptionTextBox = getChild<LLUICtrl>("photo_caption");
+ mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialPhotoPanel::draw()
+{
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+
+ // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mCaptionTextBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection);
+ mRefreshBtn->setEnabled(no_ongoing_connection);
+ mLocationCheckbox->setEnabled(no_ongoing_connection);
+
+ // Display the preview if one is available
+ if (previewp && previewp->getThumbnailImage())
+ {
+ const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ // calc preview offset within the preview rect
+ const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
+ const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
+
+ // calc preview offset within the floater rect
+ // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
+ // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
+ // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
+ S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
+ S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
+
+ mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+
+ previewp->drawPreviewRect(offset_x, offset_y) ;
+ }
+
+ // Update the visibility of the working (computing preview) label
+ mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
+
+ // Enable Post if we have a preview to send and no on going connection being processed
+ mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* LLSocialPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+ if (visible)
+ {
+ if (mPreviewHandle.get())
+ {
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if(preview)
+ {
+ lldebugs << "opened, updating snapshot" << llendl;
+ preview->updateSnapshot(TRUE);
+ }
+ }
+ else
+ {
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
+ mPreviewHandle = previewp->getHandle();
+
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ //previewp->setSnapshotQuality(98);
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLSocialPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ //setStatus(Impl::STATUS_READY);
+ lldebugs << "updating snapshot" << llendl;
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLSocialPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialPhotoPanel", boost::bind(&LLSocialPhotoPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialPhotoPanel::sendPhoto()
+{
+ // Get the caption
+ std::string caption = mCaptionTextBox->getValue().asString();
+
+ // Add the location if required
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ if (add_location)
+ {
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ if (caption.empty())
+ caption = slurl.getSLURLString();
+ else
+ caption = caption + " " + slurl.getSLURLString();
+ }
+
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Facebook
+ LLFacebookConnect::instance().sharePhoto(previewp->getFormattedImage(), caption);
+
+ updateControls();
+}
+
+void LLSocialPhotoPanel::clearAndClose()
+{
+ mCaptionTextBox->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLSocialPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_bytes = previewp && previewp->getDataSize() > 0;
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+ LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD);
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ std::string bytes_string;
+ if (got_snap)
+ {
+ LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 );
+ }
+
+ //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string
+ getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown");
+ getChild<LLUICtrl>("file_size_label")->setColor(
+ shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
+ && got_bytes
+ && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" ));
+
+ updateResolution(FALSE);
+}
+
+void LLSocialPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+
+ std::string sdstring = combobox->getSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ S32 original_width = 0 , original_height = 0 ;
+ previewp->getSize(original_width, original_height) ;
+
+ if (width == 0 || height == 0)
+ {
+ // take resolution from current window size
+ lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
+ previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
+ }
+ else
+ {
+ // use the resolution from the selected pre-canned drop-down choice
+ lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
+ previewp->setSize(width, height);
+ }
+
+ checkAspectRatio(width);
+
+ previewp->getSize(width, height);
+
+ if(original_width != width || original_height != height)
+ {
+ previewp->setSize(width, height);
+
+ // hide old preview as the aspect ratio could be wrong
+ lldebugs << "updating thumbnail" << llendl;
+
+ previewp->updateSnapshot(FALSE, TRUE);
+ if(do_update)
+ {
+ lldebugs << "Will update controls" << llendl;
+ updateControls();
+ LLSocialPhotoPanel::onClickNewSnapshot();
+ }
+ }
+
+ }
+}
+
+void LLSocialPhotoPanel::checkAspectRatio(S32 index)
+{
+ LLSnapshotLivePreview *previewp = getPreviewView() ;
+
+ BOOL keep_aspect = FALSE;
+
+ if (0 == index) // current window size
+ {
+ keep_aspect = TRUE;
+ }
+ else // predefined resolution
+ {
+ keep_aspect = FALSE;
+ }
+
+ if (previewp)
+ {
+ previewp->mKeepAspectRatio = keep_aspect;
+ }
+}
+
+LLUICtrl* LLSocialPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+////////////////////////
+//LLSocialCheckinPanel//
+////////////////////////
+
+LLSocialCheckinPanel::LLSocialCheckinPanel() :
+ mMapUrl(""),
+ mReloadingMapTexture(false)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLSocialCheckinPanel::onSend, this));
+}
+
+BOOL LLSocialCheckinPanel::postBuild()
+{
+ // Keep pointers to widgets so we don't traverse the UI hierarchy too often
+ mPostButton = getChild<LLUICtrl>("post_place_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_place_btn");
+ mMessageTextEditor = getChild<LLUICtrl>("place_caption");
+ mMapLoadingIndicator = getChild<LLUICtrl>("map_loading_indicator");
+ mMapPlaceholder = getChild<LLIconCtrl>("map_placeholder");
+ mMapDefault = getChild<LLIconCtrl>("map_default");
+ mMapCheckBox = getChild<LLCheckBoxCtrl>("add_place_view_cb");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialCheckinPanel::draw()
+{
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ mPostButton->setEnabled(no_ongoing_connection);
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mMessageTextEditor->setEnabled(no_ongoing_connection);
+ mMapCheckBox->setEnabled(no_ongoing_connection);
+
+ std::string map_url = get_map_url();
+ // Did we change location?
+ if (map_url != mMapUrl)
+ {
+ mMapUrl = map_url;
+ // Load the map tile
+ mMapTexture = LLViewerTextureManager::getFetchedTextureFromUrl(mMapUrl, FTT_MAP_TILE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ mMapTexture->setBoostLevel(LLGLTexture::BOOST_MAP);
+ mReloadingMapTexture = true;
+ // In the meantime, put the "loading" indicator on, hide the tile map and disable the checkbox
+ mMapLoadingIndicator->setVisible(true);
+ mMapPlaceholder->setVisible(false);
+ }
+ // Are we done loading the map tile?
+ if (mReloadingMapTexture && mMapTexture->isFullyLoaded())
+ {
+ // Don't do it again next time around
+ mReloadingMapTexture = false;
+ // Convert the map texture to the appropriate image object
+ LLPointer<LLUIImage> ui_image = new LLUIImage(mMapUrl, mMapTexture);
+ // Load the map widget with the correct map tile image
+ mMapPlaceholder->setImage(ui_image);
+ // Now hide the loading indicator, bring the tile in view and reenable the checkbox with its previous value
+ mMapLoadingIndicator->setVisible(false);
+ mMapPlaceholder->setVisible(true);
+ }
+ // Show the default icon if that's the checkbox value (the real one...)
+ // This will hide/show the loading indicator and/or tile underneath
+ mMapDefault->setVisible(!(mMapCheckBox->get()));
+
+ LLPanel::draw();
+}
+
+void LLSocialCheckinPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialCheckinPanel", boost::bind(&LLSocialCheckinPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendCheckin();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendCheckin();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialCheckinPanel::sendCheckin()
+{
+ // Get the location SLURL
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ std::string slurl_string = slurl.getSLURLString();
+
+ // Use a valid http:// URL if the scheme is secondlife://
+ LLURI slurl_uri(slurl_string);
+ if (slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME)
+ {
+ slurl_string = DEFAULT_CHECKIN_LOCATION_URL;
+ }
+
+ // Get the region name
+ std::string region_name = gAgent.getRegion()->getName();
+
+ // Get the region description
+ std::string description;
+ LLAgentUI::buildLocationString(description, LLAgentUI::LOCATION_FORMAT_NORMAL_COORDS, gAgent.getPositionAgent());
+
+ // Optionally add the region map view
+ bool add_map_view = mMapCheckBox->getValue().asBoolean();
+ std::string map_url = (add_map_view ? get_map_url() : DEFAULT_CHECKIN_ICON_URL);
+
+ // Get the caption
+ std::string caption = mMessageTextEditor->getValue().asString();
+
+ // Post to Facebook
+ LLFacebookConnect::instance().postCheckin(slurl_string, region_name, description, map_url, caption);
+}
+
+void LLSocialCheckinPanel::clearAndClose()
+{
+ mMessageTextEditor->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+///////////////////////////
+//LLSocialAccountPanel//////
+///////////////////////////
+
+LLSocialAccountPanel::LLSocialAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLSocialAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLSocialAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLSocialAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLSocialAccountPanel::postBuild()
+{
+ mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
+ mAccountNameLabel = getChild<LLTextBox>("account_name_label");
+ mPanelButtons = getChild<LLUICtrl>("panel_buttons");
+ mConnectButton = getChild<LLUICtrl>("connect_btn");
+ mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialAccountPanel::draw()
+{
+ LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLFacebookConnect::FB_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLFacebookConnect::FB_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLSocialAccountPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectInfoChange, this));
+
+ //Connected
+ if(LLFacebookConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) ||
+ (LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED))
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
+ }
+}
+
+bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ if(LLFacebookConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLFacebookConnect::FB_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLSocialAccountPanel::onFacebookConnectInfoChange()
+{
+ LLSD info = LLFacebookConnect::instance().getInfo();
+ std::string clickable_name;
+
+ //Strings of format [http://www.somewebsite.com Click Me] become clickable text
+ if(info.has("link") && info.has("name"))
+ {
+ clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
+ }
+
+ mAccountNameLabel->setText(clickable_name);
+
+ return false;
+}
+
+void LLSocialAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLSocialAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLSocialAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("facebook_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLSocialAccountPanel::showConnectedLayout()
+{
+ LLFacebookConnect::instance().loadFacebookInfo();
+
+ mAccountCaptionLabel->setText(getString("facebook_connected"));
+ hideConnectButton();
+}
+
+void LLSocialAccountPanel::onConnect()
+{
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+
+ //Clear only the facebook browser cookies so that the facebook login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
+}
+
+void LLSocialAccountPanel::onDisconnect()
+{
+ LLFacebookConnect::instance().disconnectFromFacebook();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
+}
+
+////////////////////////
+//LLFloaterSocial///////
+////////////////////////
+
+LLFloaterSocial::LLFloaterSocial(const LLSD& key) : LLFloater(key),
+ mSocialPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterSocial::onCancel, this));
+}
+
+void LLFloaterSocial::onCancel()
+{
+ closeFloater();
+}
+
+BOOL LLFloaterSocial::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mSocialPhotoPanel = static_cast<LLSocialPhotoPanel*>(getChild<LLUICtrl>("panel_social_photo"));
+ // Connection status widgets
+ mStatusErrorText = getChild<LLTextBox>("connection_error_text");
+ mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
+ mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
+ return LLFloater::postBuild();
+}
+
+// static
+void LLFloaterSocial::preUpdate()
+{
+ LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ if (instance)
+ {
+ //Will set file size text to 'unknown'
+ instance->mSocialPhotoPanel->updateControls();
+ }
+}
+
+// static
+void LLFloaterSocial::postUpdate()
+{
+ LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ if (instance)
+ {
+ //Will set the file size text
+ instance->mSocialPhotoPanel->updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = instance->mSocialPhotoPanel->getRefreshBtn();
+
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+
+ }
+}
+
+void LLFloaterSocial::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLFacebookConnect::FB_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLFacebookConnect::FB_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLFacebookConnect::FB_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLFacebookConnect::FB_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFacebookConnect::FB_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFacebookConnect::FB_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloatersocial.h
new file mode 100644
index 0000000000..bbe07c9704
--- /dev/null
+++ b/indra/newview/llfloatersocial.h
@@ -0,0 +1,165 @@
+/**
+* @file llfloatersocial.h
+* @brief Header file for llfloatersocial
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLFLOATERSOCIAL_H
+#define LL_LLFLOATERSOCIAL_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+
+class LLSocialStatusPanel : public LLPanel
+{
+public:
+ LLSocialStatusPanel();
+ BOOL postBuild();
+ void draw();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendStatus();
+ void clearAndClose();
+
+private:
+ LLUICtrl* mMessageTextEditor;
+ LLUICtrl* mPostButton;
+ LLUICtrl* mCancelButton;
+};
+
+class LLSocialPhotoPanel : public LLPanel
+{
+public:
+ LLSocialPhotoPanel();
+ ~LLSocialPhotoPanel();
+
+ BOOL postBuild();
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(const LLSD& new_visibility);
+ void onClickNewSnapshot();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mCaptionTextBox;
+ LLUICtrl * mLocationCheckbox;
+ LLUICtrl * mPostButton;
+ LLUICtrl* mCancelButton;
+};
+
+class LLSocialCheckinPanel : public LLPanel
+{
+public:
+ LLSocialCheckinPanel();
+ BOOL postBuild();
+ void draw();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendCheckin();
+ void clearAndClose();
+
+private:
+ std::string mMapUrl;
+ LLPointer<LLViewerFetchedTexture> mMapTexture;
+ LLUICtrl* mPostButton;
+ LLUICtrl* mCancelButton;
+ LLUICtrl* mMessageTextEditor;
+ LLUICtrl* mMapLoadingIndicator;
+ LLIconCtrl* mMapPlaceholder;
+ LLIconCtrl* mMapDefault;
+ LLCheckBoxCtrl* mMapCheckBox;
+ bool mReloadingMapTexture;
+};
+
+class LLSocialAccountPanel : public LLPanel
+{
+public:
+ LLSocialAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(const LLSD& new_visibility);
+ bool onFacebookConnectStateChange(const LLSD& data);
+ bool onFacebookConnectInfoChange();
+ void onConnect();
+ void onUseAnotherAccount();
+ void onDisconnect();
+
+ void showConnectButton();
+ void hideConnectButton();
+ void showDisconnectedLayout();
+ void showConnectedLayout();
+
+ LLTextBox * mAccountCaptionLabel;
+ LLTextBox * mAccountNameLabel;
+ LLUICtrl * mPanelButtons;
+ LLUICtrl * mConnectButton;
+ LLUICtrl * mDisconnectButton;
+};
+
+
+class LLFloaterSocial : public LLFloater
+{
+public:
+ LLFloaterSocial(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onCancel();
+
+ static void preUpdate();
+ static void postUpdate();
+
+private:
+ LLSocialPhotoPanel* mSocialPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERSOCIAL_H
+
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index 3fe2518de6..9d703d2752 100755
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -29,6 +29,7 @@
#include "llcombobox.h"
#include "lliconctrl.h"
#include "llfloaterreg.h"
+#include "llfacebookconnect.h"
#include "lllayoutstack.h"
#include "llpluginclassmedia.h"
#include "llprogressbar.h"
@@ -46,7 +47,8 @@ LLFloaterWebContent::_Params::_Params()
id("id"),
window_class("window_class", "web_content"),
show_chrome("show_chrome", true),
- allow_address_entry("allow_address_entry", true),
+ allow_address_entry("allow_address_entry", true),
+ allow_back_forward_navigation("allow_back_forward_navigation", true),
preferred_media_size("preferred_media_size"),
trusted_content("trusted_content", false),
show_page_title("show_page_title", true)
@@ -65,7 +67,11 @@ LLFloaterWebContent::LLFloaterWebContent( const Params& params )
mBtnReload(NULL),
mBtnStop(NULL),
mUUID(params.id()),
- mShowPageTitle(params.show_page_title)
+ mShowPageTitle(params.show_page_title),
+ mAllowNavigation(true),
+ mCurrentURL(""),
+ mDisplayURL(""),
+ mSecureURL(false)
{
mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this ));
mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this ));
@@ -97,7 +103,7 @@ BOOL LLFloaterWebContent::postBuild()
// cache image for secure browsing
mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag");
-
+
// initialize the URL history using the system URL History manager
initializeURLHistory();
@@ -243,6 +249,7 @@ void LLFloaterWebContent::open_media(const Params& p)
getChild<LLLayoutPanel>("status_bar")->setVisible(p.show_chrome);
getChild<LLLayoutPanel>("nav_controls")->setVisible(p.show_chrome);
bool address_entry_enabled = p.allow_address_entry && !p.trusted_content;
+ mAllowNavigation = p.allow_back_forward_navigation;
getChildView("address")->setEnabled(address_entry_enabled);
getChildView("popexternal")->setEnabled(address_entry_enabled);
@@ -287,6 +294,16 @@ void LLFloaterWebContent::onOpen(const LLSD& key)
//virtual
void LLFloaterWebContent::onClose(bool app_quitting)
{
+ // If we close the web browsing window showing the facebook login, we need to signal to this object that the connection will not happen
+ LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
+ if (fbc_web == this)
+ {
+ if (!LLFacebookConnect::instance().isConnected())
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ }
+ }
+
LLViewerMedia::proxyWindowClosed(mUUID);
destroy();
}
@@ -295,8 +312,11 @@ void LLFloaterWebContent::onClose(bool app_quitting)
void LLFloaterWebContent::draw()
{
// this is asynchronous so we need to keep checking
- mBtnBack->setEnabled( mWebBrowser->canNavigateBack() );
- mBtnForward->setEnabled( mWebBrowser->canNavigateForward() );
+ mBtnBack->setEnabled( mWebBrowser->canNavigateBack() && mAllowNavigation);
+ mBtnForward->setEnabled( mWebBrowser->canNavigateForward() && mAllowNavigation);
+
+ // Show/hide the lock icon
+ mSecureLockIcon->setVisible(mSecureURL && !mAddressCombo->hasFocus());
LLFloater::draw();
}
@@ -342,19 +362,6 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
// we populate the status bar with URLs as they change so clear it now we're done
const std::string end_str = "";
mStatusBarText->setText( end_str );
-
- // decide if secure browsing icon should be displayed
- std::string prefix = std::string("https://");
- std::string test_prefix = mCurrentURL.substr(0, prefix.length());
- LLStringUtil::toLower(test_prefix);
- if(test_prefix == prefix)
- {
- mSecureLockIcon->setVisible(true);
- }
- else
- {
- mSecureLockIcon->setVisible(false);
- }
}
else if(event == MEDIA_EVENT_CLOSE_REQUEST)
{
@@ -397,15 +404,40 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
void LLFloaterWebContent::set_current_url(const std::string& url)
{
- mCurrentURL = url;
-
- // serialize url history into the system URL History manager
- LLURLHistory::removeURL("browser", mCurrentURL);
- LLURLHistory::addURL("browser", mCurrentURL);
-
- mAddressCombo->remove( mCurrentURL );
- mAddressCombo->add( mCurrentURL );
- mAddressCombo->selectByValue( mCurrentURL );
+ if (!url.empty())
+ {
+ if (!mCurrentURL.empty())
+ {
+ // Clean up the current browsing list to show true URL
+ mAddressCombo->remove(mDisplayURL);
+ mAddressCombo->add(mCurrentURL);
+ }
+
+ // Update current URL
+ mCurrentURL = url;
+ LLStringUtil::trim(mCurrentURL);
+
+ // Serialize url history into the system URL History manager
+ LLURLHistory::removeURL("browser", mCurrentURL);
+ LLURLHistory::addURL("browser", mCurrentURL);
+
+ // Check if this is a secure URL
+ static const std::string secure_prefix = std::string("https://");
+ std::string prefix = mCurrentURL.substr(0, secure_prefix.length());
+ LLStringUtil::toLower(prefix);
+ mSecureURL = (prefix == secure_prefix);
+
+ // Hack : we move the text a bit to make space for the lock icon in the secure URL case
+ mDisplayURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL);
+
+ // Clean up browsing list (prevent dupes) and add/select the new URL to it
+ mAddressCombo->remove(mCurrentURL);
+ mAddressCombo->add(mDisplayURL);
+ mAddressCombo->selectByValue(mDisplayURL);
+
+ // Set the focus back to the web page. When setting the url, there's no point to leave the focus anywhere else.
+ mWebBrowser->setFocus(TRUE);
+ }
}
void LLFloaterWebContent::onClickForward()
@@ -449,6 +481,7 @@ void LLFloaterWebContent::onEnterAddress()
// make sure there is at least something there.
// (perhaps this test should be for minimum length of a URL)
std::string url = mAddressCombo->getValue().asString();
+ LLStringUtil::trim(url);
if ( url.length() > 0 )
{
mWebBrowser->navigateTo( url, "text/html");
@@ -460,6 +493,7 @@ void LLFloaterWebContent::onPopExternal()
// make sure there is at least something there.
// (perhaps this test should be for minimum length of a URL)
std::string url = mAddressCombo->getValue().asString();
+ LLStringUtil::trim(url);
if ( url.length() > 0 )
{
LLWeb::loadURLExternal( url );
diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h
index cfc87e9015..f7e0db8d9e 100755
--- a/indra/newview/llfloaterwebcontent.h
+++ b/indra/newview/llfloaterwebcontent.h
@@ -54,6 +54,7 @@ public:
id;
Optional<bool> show_chrome,
allow_address_entry,
+ allow_back_forward_navigation,
trusted_content,
show_page_title;
Optional<LLRect> preferred_media_size;
@@ -105,9 +106,12 @@ protected:
LLView* mBtnReload;
LLView* mBtnStop;
- std::string mCurrentURL;
+ std::string mCurrentURL; // Current URL
+ std::string mDisplayURL; // URL being displayed in the address bar (can differ by trailing / leading space)
std::string mUUID;
bool mShowPageTitle;
+ bool mAllowNavigation;
+ bool mSecureURL; // true when the current url is prefixed "https://"
};
#endif // LL_LLFLOATERWEBCONTENT_H
diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp
index 586965e5a0..c28657dbcd 100755
--- a/indra/newview/llfolderviewmodelinventory.cpp
+++ b/indra/newview/llfolderviewmodelinventory.cpp
@@ -74,6 +74,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder )
it != end_it;
++it)
{
+ // Recursive call to sort() on child (CHUI-849)
LLFolderViewFolder* child_folderp = *it;
sort(child_folderp);
@@ -129,12 +130,12 @@ void LLFolderViewModelItemInventory::requestSort()
void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size)
{
LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size);
-
- bool passed_filter_before = mPrevPassedAllFilters;
+ bool before = mPrevPassedAllFilters;
mPrevPassedAllFilters = passedFilter(filter_generation);
- if (passed_filter_before != mPrevPassedAllFilters)
+ if (before != mPrevPassedAllFilters)
{
+ // Need to rearrange the folder if the filtered state of the item changed
LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder();
if (parent_folder)
{
@@ -150,11 +151,11 @@ bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* ite
bool continue_filtering = true;
if (item->getLastFilterGeneration() < filter_generation)
{
- // recursive application of the filter for child items
+ // Recursive application of the filter for child items (CHUI-849)
continue_filtering = item->filter( filter );
}
- // track latest generation to pass any child items, for each folder up to root
+ // Update latest generation to pass filter in parent and propagate up to root
if (item->passedFilter())
{
LLFolderViewModelItemInventory* view_model = this;
@@ -174,53 +175,61 @@ bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter)
const S32 filter_generation = filter.getCurrentGeneration();
const S32 must_pass_generation = filter.getFirstRequiredGeneration();
- if (getLastFilterGeneration() >= must_pass_generation
+ if (getLastFilterGeneration() >= must_pass_generation
&& getLastFolderFilterGeneration() >= must_pass_generation
&& !passedFilter(must_pass_generation))
{
// failed to pass an earlier filter that was a subset of the current one
- // go ahead and flag this item as done
+ // go ahead and flag this item as not pass
setPassedFilter(false, filter_generation);
setPassedFolderFilter(false, filter_generation);
return true;
}
- const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY)
- ? filter.checkFolder(this)
- : true;
+ // *TODO : Revise the logic for fast pass on less restrictive filter case
+ /*
+ const S32 sufficient_pass_generation = filter.getFirstSuccessGeneration();
+ if (getLastFilterGeneration() >= sufficient_pass_generation
+ && getLastFolderFilterGeneration() >= sufficient_pass_generation
+ && passedFilter(sufficient_pass_generation))
+ {
+ // passed an earlier filter that was a superset of the current one
+ // go ahead and flag this item as pass
+ setPassedFilter(true, filter_generation);
+ setPassedFolderFilter(true, filter_generation);
+ return true;
+ }
+ */
+
+ const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) ? filter.checkFolder(this) : true;
setPassedFolderFilter(passed_filter_folder, filter_generation);
- if(!mChildren.empty()
+ bool continue_filtering = true;
+
+ if (!mChildren.empty()
&& (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass
- || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement
+ || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement
{
// now query children
- for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end();
- iter != end_iter && filter.getFilterCount() > 0;
- ++iter)
+ for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); iter != end_iter; ++iter)
{
- if (!filterChildItem((*iter), filter))
+ continue_filtering = filterChildItem((*iter), filter);
+ if (!continue_filtering)
{
break;
}
}
}
- // if we didn't use all filter iterations
- // that means we filtered all of our descendants
- // so filter ourselves now
- if (filter.getFilterCount() > 0)
+ // If we didn't use all the filter time that means we filtered all of our descendants so we can filter ourselves now
+ if (continue_filtering)
{
- filter.decrementFilterCount();
-
+ // This is where filter check on the item done (CHUI-849)
const bool passed_filter = filter.check(this);
setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize());
- return true;
- }
- else
- {
- return false;
+ continue_filtering = !filter.isTimedOut();
}
+ return continue_filtering;
}
LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel()
@@ -307,8 +316,8 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a,
}
}
-LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model )
- : LLFolderViewModelItemCommon(root_view_model),
- mPrevPassedAllFilters(false)
+LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mPrevPassedAllFilters(false)
{
}
diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h
index 890d03d1c9..9dcfdfa185 100755
--- a/indra/newview/llfolderviewmodelinventory.h
+++ b/indra/newview/llfolderviewmodelinventory.h
@@ -59,9 +59,8 @@ public:
virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0;
virtual LLToolDragAndDrop::ESource getDragSource() const = 0;
-
protected:
- bool mPrevPassedAllFilters;
+ bool mPrevPassedAllFilters;
};
class LLInventorySort
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index a0f2918bd7..302d21c2e4 100755
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -116,6 +116,80 @@ public:
};
LLGroupHandler gGroupHandler;
+// This object represents a pending request for specified group member information
+// which is needed to check whether avatar can leave group
+class LLFetchGroupMemberData : public LLGroupMgrObserver
+{
+public:
+ LLFetchGroupMemberData(const LLUUID& group_id) :
+ mGroupId(group_id),
+ mRequestProcessed(false),
+ LLGroupMgrObserver(group_id)
+ {
+ llinfos << "Sending new group member request for group_id: "<< group_id << llendl;
+ LLGroupMgr* mgr = LLGroupMgr::getInstance();
+ // register ourselves as an observer
+ mgr->addObserver(this);
+ // send a request
+ mgr->sendGroupPropertiesRequest(group_id);
+ mgr->sendCapGroupMembersRequest(group_id);
+ }
+
+ ~LLFetchGroupMemberData()
+ {
+ if (!mRequestProcessed)
+ {
+ // Request is pending
+ llwarns << "Destroying pending group member request for group_id: "
+ << mGroupId << llendl;
+ }
+ // Remove ourselves as an observer
+ LLGroupMgr::getInstance()->removeObserver(this);
+ }
+
+ void changed(LLGroupChange gc)
+ {
+ if (gc == GC_MEMBER_DATA && !mRequestProcessed)
+ {
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupId);
+ if (!gdatap)
+ {
+ llwarns << "LLGroupMgr::getInstance()->getGroupData() was NULL" << llendl;
+ }
+ else if (!gdatap->isMemberDataComplete())
+ {
+ llwarns << "LLGroupMgr::getInstance()->getGroupData()->isMemberDataComplete() was FALSE" << llendl;
+ }
+ else
+ {
+ processGroupData();
+ mRequestProcessed = true;
+ }
+ }
+ }
+
+ LLUUID getGroupId() { return mGroupId; }
+ virtual void processGroupData() = 0;
+protected:
+ LLUUID mGroupId;
+private:
+ bool mRequestProcessed;
+};
+
+class LLFetchLeaveGroupData: public LLFetchGroupMemberData
+{
+public:
+ LLFetchLeaveGroupData(const LLUUID& group_id)
+ : LLFetchGroupMemberData(group_id)
+ {}
+ void processGroupData()
+ {
+ LLGroupActions::processLeaveGroupDataResponse(mGroupId);
+ }
+};
+
+LLFetchLeaveGroupData* gFetchLeaveGroupData = NULL;
+
// static
void LLGroupActions::search()
{
@@ -208,23 +282,52 @@ bool LLGroupActions::onJoinGroup(const LLSD& notification, const LLSD& response)
void LLGroupActions::leave(const LLUUID& group_id)
{
if (group_id.isNull())
+ {
return;
+ }
- S32 count = gAgent.mGroups.count();
- S32 i;
- for (i = 0; i < count; ++i)
+ LLGroupData group_data;
+ if (gAgent.getGroupData(group_id, group_data))
{
- if(gAgent.mGroups.get(i).mID == group_id)
- break;
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
+ if (!gdatap || !gdatap->isMemberDataComplete())
+ {
+ if (gFetchLeaveGroupData != NULL)
+ {
+ delete gFetchLeaveGroupData;
+ gFetchLeaveGroupData = NULL;
+ }
+ gFetchLeaveGroupData = new LLFetchLeaveGroupData(group_id);
+ }
+ else
+ {
+ processLeaveGroupDataResponse(group_id);
+ }
}
- if (i < count)
+}
+
+//static
+void LLGroupActions::processLeaveGroupDataResponse(const LLUUID group_id)
+{
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
+ LLUUID agent_id = gAgent.getID();
+ LLGroupMgrGroupData::member_list_t::iterator mit = gdatap->mMembers.find(agent_id);
+ //get the member data for the group
+ if ( mit != gdatap->mMembers.end() )
{
- LLSD args;
- args["GROUP"] = gAgent.mGroups.get(i).mName;
- LLSD payload;
- payload["group_id"] = group_id;
- LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup);
+ LLGroupMemberData* member_data = (*mit).second;
+
+ if ( member_data && member_data->isOwner() && gdatap->mMemberCount == 1)
+ {
+ LLNotificationsUtil::add("OwnerCannotLeaveGroup");
+ return;
+ }
}
+ LLSD args;
+ args["GROUP"] = gdatap->mName;
+ LLSD payload;
+ payload["group_id"] = group_id;
+ LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup);
}
// static
diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h
index 3f9852f194..afc4686dd7 100755
--- a/indra/newview/llgroupactions.h
+++ b/indra/newview/llgroupactions.h
@@ -114,6 +114,14 @@ public:
private:
static bool onJoinGroup(const LLSD& notification, const LLSD& response);
static bool onLeaveGroup(const LLSD& notification, const LLSD& response);
+
+ /**
+ * This function is called by LLFetchLeaveGroupData upon receiving a response to a group
+ * members data request.
+ */
+ static void processLeaveGroupDataResponse(const LLUUID group_id);
+
+ friend class LLFetchLeaveGroupData;
};
#endif // LL_LLGROUPACTIONS_H
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 3b72ad3cd9..515b669853 100755
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -103,6 +103,7 @@ BOOL LLSessionTimeoutTimer::tick()
}
+void notify_of_message(const LLSD& msg, bool is_dnd_msg);
void process_dnd_im(const LLSD& notification)
{
@@ -129,15 +130,9 @@ void process_dnd_im(const LLSD& notification)
fromID,
false,
false); //will need slight refactor to retrieve whether offline message or not (assume online for now)
+ }
- LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
-
- if (im_box)
- {
- im_box->flashConversationItemWidget(sessionID, true);
- }
-
- }
+ notify_of_message(data, true);
}
@@ -158,72 +153,72 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id,
LLNotificationsUtil::add("IMToast", args, args, boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID()));
}
-void on_new_message(const LLSD& msg)
+void notify_of_message(const LLSD& msg, bool is_dnd_msg)
{
- std::string user_preferences;
- LLUUID participant_id = msg["from_id"].asUUID();
- LLUUID session_id = msg["session_id"].asUUID();
- LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
+ std::string user_preferences;
+ LLUUID participant_id = msg[is_dnd_msg ? "FROM_ID" : "from_id"].asUUID();
+ LLUUID session_id = msg[is_dnd_msg ? "SESSION_ID" : "session_id"].asUUID();
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
- // do not show notification which goes from agent
- if (gAgent.getID() == participant_id)
- {
- return;
- }
-
- // determine state of conversations floater
- enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status;
+ // do not show notification which goes from agent
+ if (gAgent.getID() == participant_id)
+ {
+ return;
+ }
+ // determine state of conversations floater
+ enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status;
- LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id);
+ bool store_dnd_message = false; // flag storage of a dnd message
if (!LLFloater::isVisible(im_box) || im_box->isMinimized())
{
conversations_floater_status = CLOSED;
}
else if (!im_box->hasFocus() &&
- !(session_floater && LLFloater::isVisible(session_floater)
- && !session_floater->isMinimized() && session_floater->hasFocus()))
+ !(session_floater && LLFloater::isVisible(session_floater)
+ && !session_floater->isMinimized() && session_floater->hasFocus()))
{
conversations_floater_status = NOT_ON_TOP;
}
else if (im_box->getSelectedSession() != session_id)
{
conversations_floater_status = ON_TOP;
- }
+ }
else
{
conversations_floater_status = ON_TOP_AND_ITEM_IS_SELECTED;
}
- // determine user prefs for this session
- if (session_id.isNull())
- {
- user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions");
- }
- else if(session->isP2PSessionType())
- {
- if (LLAvatarTracker::instance().isBuddy(participant_id))
- {
- user_preferences = gSavedSettings.getString("NotificationFriendIMOptions");
- }
- else
- {
- user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions");
- }
- }
- else if(session->isAdHocSessionType())
- {
- user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions");
- }
- else if(session->isGroupSessionType())
- {
- user_preferences = gSavedSettings.getString("NotificationGroupChatOptions");
- }
+ // determine user prefs for this session
+ if (session_id.isNull())
+ {
+ user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions");
+ }
+ else if(session->isP2PSessionType())
+ {
+ if (LLAvatarTracker::instance().isBuddy(participant_id))
+ {
+ user_preferences = gSavedSettings.getString("NotificationFriendIMOptions");
+ }
+ else
+ {
+ user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions");
+ }
+ }
+ else if(session->isAdHocSessionType())
+ {
+ user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions");
+ }
+ else if(session->isGroupSessionType())
+ {
+ user_preferences = gSavedSettings.getString("NotificationGroupChatOptions");
+ }
- // actions:
+ // actions:
// 0. nothing - exit
if (("none" == user_preferences ||
@@ -261,57 +256,100 @@ void on_new_message(const LLSD& msg)
}
}
}
- else
- {
- //If in DND mode, allow notification to be stored so upon DND exit
- //useMostItrusiveIMNotification will be called to notify user a message exists
- if(session_id.notNull()
- && participant_id.notNull()
- && !session_floater->isShown())
- {
- LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
- }
- }
- }
+ else
+ {
+ store_dnd_message = true;
+ }
- // 2. Flash line item
- if ("openconversations" == user_preferences
- || ON_TOP == conversations_floater_status
- || ("toast" == user_preferences && ON_TOP != conversations_floater_status)
- || ("flash" == user_preferences && CLOSED == conversations_floater_status))
- {
- if(!LLMuteList::getInstance()->isMuted(participant_id))
- {
- im_box->flashConversationItemWidget(session_id, true);
- }
- }
+ }
- // 3. Flash FUI button
- if (("toast" == user_preferences || "flash" == user_preferences) &&
- (CLOSED == conversations_floater_status
- || NOT_ON_TOP == conversations_floater_status))
- {
- if(!LLMuteList::getInstance()->isMuted(participant_id)
- && !gAgent.isDoNotDisturb())
- {
- gToolBarView->flashCommand(LLCommandId("chat"), true);
- }
- }
+ // 2. Flash line item
+ if ("openconversations" == user_preferences
+ || ON_TOP == conversations_floater_status
+ || ("toast" == user_preferences && ON_TOP != conversations_floater_status)
+ || ("flash" == user_preferences && CLOSED == conversations_floater_status)
+ || is_dnd_msg)
+ {
+ if(!LLMuteList::getInstance()->isMuted(participant_id))
+ {
+ if(gAgent.isDoNotDisturb())
+ {
+ store_dnd_message = true;
+ }
+ else
+ {
+ if (is_dnd_msg && (ON_TOP == conversations_floater_status ||
+ NOT_ON_TOP == conversations_floater_status ||
+ CLOSED == conversations_floater_status))
+ {
+ im_box->highlightConversationItemWidget(session_id, true);
+ }
+ else
+ {
+ im_box->flashConversationItemWidget(session_id, true);
+ }
+ }
+ }
+ }
- // 4. Toast
- if ((("toast" == user_preferences) &&
- (CLOSED == conversations_floater_status
- || NOT_ON_TOP == conversations_floater_status))
- || !session_floater->isMessagePaneExpanded())
+ // 3. Flash FUI button
+ if (("toast" == user_preferences || "flash" == user_preferences) &&
+ (CLOSED == conversations_floater_status
+ || NOT_ON_TOP == conversations_floater_status)
+ && !is_dnd_msg) //prevent flashing FUI button because the conversation floater will have already opened
+ {
+ if(!LLMuteList::getInstance()->isMuted(participant_id))
+ {
+ if(!gAgent.isDoNotDisturb())
+ {
+ gToolBarView->flashCommand(LLCommandId("chat"), true);
+ }
+ else
+ {
+ store_dnd_message = true;
+ }
+ }
+ }
- {
- //Show IM toasts (upper right toasts)
- // Skip toasting for system messages and for nearby chat
- if(session_id.notNull() && participant_id.notNull())
- {
- LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
- }
- }
+ // 4. Toast
+ if ((("toast" == user_preferences) &&
+ (ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status))
+ || !session_floater->isMessagePaneExpanded())
+
+ {
+ //Show IM toasts (upper right toasts)
+ // Skip toasting for system messages and for nearby chat
+ if(session_id.notNull() && participant_id.notNull())
+ {
+ if(!is_dnd_msg)
+ {
+ if(gAgent.isDoNotDisturb())
+ {
+ store_dnd_message = true;
+ }
+ else
+ {
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
+ }
+ }
+ if (store_dnd_message)
+ {
+ // If in DND mode, allow notification to be stored so upon DND exit
+ // the user will be notified with some limitations (see 'is_dnd_msg' flag checks)
+ if(session_id.notNull()
+ && participant_id.notNull()
+ && !session_floater->isShown())
+ {
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
+}
+
+void on_new_message(const LLSD& msg)
+{
+ notify_of_message(msg, false);
}
LLIMModel::LLIMModel()
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 92f2d33073..3c6974cf6d 100755
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -70,11 +70,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p)
mFilterSubString(p.substring),
mCurrentGeneration(0),
mFirstRequiredGeneration(0),
- mFirstSuccessGeneration(0),
- mFilterCount(0)
+ mFirstSuccessGeneration(0)
{
- mNextFilterGeneration = mCurrentGeneration + 1;
-
// copy mFilterOps into mDefaultFilterOps
markDefault();
}
@@ -92,9 +89,7 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item)
return passed_clipboard;
}
- std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos;
-
- BOOL passed = (mFilterSubString.size() == 0 || string_offset != std::string::npos);
+ bool passed = (mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) != std::string::npos : true);
passed = passed && checkAgainstFilterType(listener);
passed = passed && checkAgainstPermissions(listener);
passed = passed && checkAgainstFilterLinks(listener);
@@ -105,17 +100,12 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item)
bool LLInventoryFilter::check(const LLInventoryItem* item)
{
- std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos;
-
+ const bool passed_string = (mFilterSubString.size() ? item->getName().find(mFilterSubString) != std::string::npos : true);
const bool passed_filtertype = checkAgainstFilterType(item);
const bool passed_permissions = checkAgainstPermissions(item);
- const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID());
- const bool passed = (passed_filtertype
- && passed_permissions
- && passed_clipboard
- && (mFilterSubString.size() == 0 || string_offset != std::string::npos));
+ const bool passed_clipboard = checkAgainstClipboard(item->getUUID());
- return passed;
+ return passed_filtertype && passed_permissions && passed_clipboard && passed_string;
}
bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const
@@ -439,7 +429,7 @@ void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types)
current_types = types;
if (more_bits_set && fewer_bits_set)
{
- // neither less or more restrive, both simultaneously
+ // neither less or more restrictive, both simultaneously
// so we need to filter from scratch
setModified(FILTER_RESTART);
}
@@ -714,7 +704,7 @@ void LLInventoryFilter::resetDefault()
void LLInventoryFilter::setModified(EFilterModified behavior)
{
mFilterText.clear();
- mCurrentGeneration = mNextFilterGeneration++;
+ mCurrentGeneration++;
if (mFilterModified == FILTER_NONE)
{
@@ -1021,21 +1011,19 @@ LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const
return mFilterOps.mShowFolderState;
}
-void LLInventoryFilter::setFilterCount(S32 count)
-{
- mFilterCount = count;
-}
-S32 LLInventoryFilter::getFilterCount() const
+bool LLInventoryFilter::isTimedOut()
{
- return mFilterCount;
+ return mFilterTime.hasExpired();
}
-void LLInventoryFilter::decrementFilterCount()
-{
- mFilterCount--;
+void LLInventoryFilter::resetTime(S32 timeout)
+{
+ mFilterTime.reset();
+ F32 time_in_sec = (F32)(timeout)/1000.0;
+ mFilterTime.setTimerExpirySec(time_in_sec);
}
-S32 LLInventoryFilter::getCurrentGeneration() const
+S32 LLInventoryFilter::getCurrentGeneration() const
{
return mCurrentGeneration;
}
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index 4912b5ca91..ce516af0b9 100755
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -215,12 +215,11 @@ public:
void setModified(EFilterModified behavior = FILTER_RESTART);
// +-------------------------------------------------------------------+
- // + Count
+ // + Time
// +-------------------------------------------------------------------+
- void setFilterCount(S32 count);
- S32 getFilterCount() const;
- void decrementFilterCount();
-
+ void resetTime(S32 timeout);
+ bool isTimedOut();
+
// +-------------------------------------------------------------------+
// + Default
// +-------------------------------------------------------------------+
@@ -262,13 +261,15 @@ private:
const std::string mName;
S32 mCurrentGeneration;
+ // The following makes checking for pass/no pass possible even if the item is not checked against the current generation
+ // Any item that *did not pass* the "required generation" will *not pass* the current one
+ // Any item that *passes* the "success generation" will *pass* the current one
S32 mFirstRequiredGeneration;
S32 mFirstSuccessGeneration;
- S32 mNextFilterGeneration;
- S32 mFilterCount;
EFilterModified mFilterModified;
-
+ LLTimer mFilterTime;
+
std::string mFilterText;
std::string mEmptyLookupMessage;
};
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index cf1fd4c0d0..e5b9e11d48 100755
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -192,7 +192,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
p.show_item_link_overlays = mShowItemLinkOverlays;
p.root = NULL;
p.options_menu = "menu_inventory.xml";
-
+
return LLUICtrlFactory::create<LLFolderView>(p);
}
@@ -396,6 +396,7 @@ LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState()
return getFilter().getShowFolderState();
}
+// Called when something changed in the global model (new item, item coming through the wire, rename, move, etc...) (CHUI-849)
void LLInventoryPanel::modelChanged(U32 mask)
{
static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh");
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
index 2d7454b636..379bbc5f8d 100755
--- a/indra/newview/lllogchat.cpp
+++ b/indra/newview/lllogchat.cpp
@@ -631,7 +631,7 @@ void LLLogChat::deleteTranscripts()
}
// static
-bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id)
+bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id, bool is_group)
{
std::vector<std::string> list_of_transcriptions;
LLLogChat::getListOfTranscriptFiles(list_of_transcriptions);
@@ -641,20 +641,53 @@ bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id)
LLAvatarName avatar_name;
LLAvatarNameCache::get(avatar_id, &avatar_name);
std::string avatar_user_name = avatar_name.getAccountName();
- std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_');
-
- BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions)
+ if(!is_group)
{
- if (std::string::npos != transcript_file_name.find(avatar_user_name))
+ std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_');
+ BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions)
{
- return true;
+ if (std::string::npos != transcript_file_name.find(avatar_user_name))
+ {
+ return true;
+ }
}
}
+ else
+ {
+ std::string file_name;
+ gCacheName->getGroupName(avatar_id, file_name);
+ file_name = makeLogFileName(file_name);
+ BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions)
+ {
+ if (transcript_file_name == file_name)
+ {
+ return true;
+ }
+ }
+ }
+
}
return false;
}
+bool LLLogChat::isNearbyTranscriptExist()
+{
+ std::vector<std::string> list_of_transcriptions;
+ LLLogChat::getListOfTranscriptFiles(list_of_transcriptions);
+
+ std::string file_name;
+ file_name = makeLogFileName("chat");
+ BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions)
+ {
+ if (transcript_file_name == file_name)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
//which are more strict by its nature (only firstname and secondname)
//Example, an object's name can be written like "Object <actual_object's_name>"
diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h
index e819f00dd9..bd70dbaac9 100755
--- a/indra/newview/lllogchat.h
+++ b/indra/newview/lllogchat.h
@@ -67,7 +67,8 @@ public:
std::vector<std::string>& listOfFilesToMove);
static void deleteTranscripts();
- static bool isTranscriptExist(const LLUUID& avatar_id);
+ static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false);
+ static bool isNearbyTranscriptExist();
private:
static std::string cleanFileName(std::string filename);
diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp
index 08c98e4f28..2854962922 100755
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -35,6 +35,9 @@
#include "llnotificationmanager.h"
#include "llnotifications.h"
#include "llscriptfloater.h"
+#include "llfacebookconnect.h"
+#include "llavatarname.h"
+#include "llavatarnamecache.h"
using namespace LLNotificationsUI;
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index d6535c88e9..53deded2f2 100755
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -130,6 +130,8 @@ BOOL LLPanelMainInventory::postBuild()
mFilterTabs = getChild<LLTabContainer>("inventory filter tabs");
mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this));
+ mCounterCtrl = getChild<LLUICtrl>("ItemcountText");
+
//panel->getFilter().markDefault();
// Set up the default inv. panel/filter settings.
@@ -566,7 +568,7 @@ void LLPanelMainInventory::draw()
void LLPanelMainInventory::updateItemcountText()
{
// *TODO: Calling setlocale() on each frame may be inefficient.
- LLLocale locale(LLStringUtil::getLocale());
+ //LLLocale locale(LLStringUtil::getLocale());
std::string item_count_string;
LLResMgr::getInstance()->getIntegerString(item_count_string, gInventory.getItemCount());
@@ -589,8 +591,7 @@ void LLPanelMainInventory::updateItemcountText()
text = getString("ItemcountUnknown");
}
- // *TODO: Cache the LLUICtrl* for the ItemcountText control
- getChild<LLUICtrl>("ItemcountText")->setValue(text);
+ mCounterCtrl->setValue(text);
}
void LLPanelMainInventory::onFocusReceived()
diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h
index 899931aa89..394b004e20 100755
--- a/indra/newview/llpanelmaininventory.h
+++ b/indra/newview/llpanelmaininventory.h
@@ -121,6 +121,7 @@ private:
LLFilterEditor* mFilterEditor;
LLTabContainer* mFilterTabs;
+ LLUICtrl* mCounterCtrl;
LLHandle<LLFloater> mFinderHandle;
LLInventoryPanel* mActivePanel;
bool mResortActivePanel;
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 4138558bad..72953ec6d3 100755
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -28,6 +28,8 @@
// libs
#include "llavatarname.h"
+#include "llconversationview.h"
+#include "llfloaterimcontainer.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llmenubutton.h"
@@ -48,15 +50,19 @@
#include "llavataractions.h"
#include "llavatarlist.h"
#include "llavatarlistitem.h"
+#include "llavatarnamecache.h"
#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcallbacklist.h"
+#include "llerror.h"
+#include "llfacebookconnect.h"
#include "llfloateravatarpicker.h"
-//#include "llfloaterminiinspector.h"
#include "llfriendcard.h"
#include "llgroupactions.h"
#include "llgrouplist.h"
#include "llinventoryobserver.h"
#include "llnetmap.h"
#include "llpanelpeoplemenus.h"
+#include "llparticipantlist.h"
#include "llsidetraypanelcontainer.h"
#include "llrecentpeople.h"
#include "llviewercontrol.h" // for gSavedSettings
@@ -64,6 +70,10 @@
#include "llvoiceclient.h"
#include "llworld.h"
#include "llspeakers.h"
+#include "llfloaterwebcontent.h"
+
+#include "llagentui.h"
+#include "llslurl.h"
#define FRIEND_LIST_UPDATE_TIMEOUT 0.5
#define NEARBY_LIST_UPDATE_INTERVAL 1
@@ -73,9 +83,9 @@ static const std::string FRIENDS_TAB_NAME = "friends_panel";
static const std::string GROUP_TAB_NAME = "groups_panel";
static const std::string RECENT_TAB_NAME = "recent_panel";
static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars
-
static const std::string COLLAPSED_BY_USER = "collapsed_by_user";
+
/** Comparator for comparing avatar items by last interaction date */
class LLAvatarItemRecentComparator : public LLAvatarItemComparator
{
@@ -493,6 +503,7 @@ public:
LLPanelPeople::LLPanelPeople()
: LLPanel(),
+ mTryToConnectToFbc(true),
mTabContainer(NULL),
mOnlineFriendList(NULL),
mAllFriendList(NULL),
@@ -571,6 +582,7 @@ BOOL LLPanelPeople::postBuild()
getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+ getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
mTabContainer = getChild<LLTabContainer>("tabs");
mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2));
@@ -581,8 +593,11 @@ BOOL LLPanelPeople::postBuild()
// updater is active only if panel is visible to user.
friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2));
friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this));
+ friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::updateFacebookList, this, _2));
+
mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online");
mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all");
+ mSuggestedFriends = friends_tab->getChild<LLAvatarList>("suggested_friends");
mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online"));
mOnlineFriendList->setShowIcons("FriendsListShowIcons");
mOnlineFriendList->showPermissions("FriendsListShowPermissions");
@@ -615,6 +630,7 @@ BOOL LLPanelPeople::postBuild()
mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu);
setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false);
setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false);
@@ -693,7 +709,7 @@ void LLPanelPeople::updateFriendListHelpText()
// Seems sometimes all_friends can be empty because of issue with Inventory loading (clear cache, slow connection...)
// So, lets check all lists to avoid overlapping the text with online list. See EXT-6448.
- bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches();
+ bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches() || mSuggestedFriends->filterHasMatches();
no_friends_text->setVisible(!any_friend_exists);
if (no_friends_text->getVisible())
{
@@ -760,9 +776,40 @@ void LLPanelPeople::updateFriendList()
mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches());
//update trash and other buttons according to a selected item
updateButtons();
+ updateSuggestedFriendList();
showFriendsAccordionsIfNeeded();
}
+bool LLPanelPeople::updateSuggestedFriendList()
+{
+ const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ uuid_vec_t& suggested_friends = mSuggestedFriends->getIDs();
+ suggested_friends.clear();
+
+ //Add suggested friends
+ LLSD friends = LLFacebookConnect::instance().getContent();
+ for (LLSD::array_const_iterator i = friends.beginArray(); i != friends.endArray(); ++i)
+ {
+ LLUUID agent_id = (*i).asUUID();
+ bool second_life_buddy = agent_id.notNull() ? av_tracker.isBuddy(agent_id) : false;
+
+ if(!second_life_buddy)
+ {
+ //FB+SL but not SL friend
+ if (agent_id.notNull())
+ {
+ suggested_friends.push_back(agent_id);
+ }
+ }
+ }
+
+ //Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display)
+ mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches());
+ showFriendsAccordionsIfNeeded();
+
+ return false;
+}
+
void LLPanelPeople::updateNearbyList()
{
if (!mNearbyList)
@@ -786,6 +833,51 @@ void LLPanelPeople::updateRecentList()
mRecentList->setDirty();
}
+bool LLPanelPeople::onConnectedToFacebook(const LLSD& data)
+{
+ LLSD::Integer connection_state = data.get("enum").asInteger();
+
+ if (connection_state == LLFacebookConnect::FB_CONNECTED)
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ }
+ else if(connection_state == LLFacebookConnect::FB_NOT_CONNECTED)
+ {
+ updateSuggestedFriendList();
+ };
+
+ return false;
+}
+
+void LLPanelPeople::updateFacebookList(bool visible)
+{
+ if (visible)
+ {
+ LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLPanelPeople", boost::bind(&LLPanelPeople::updateSuggestedFriendList, this));
+
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLPanelPeople", boost::bind(&LLPanelPeople::onConnectedToFacebook, this, _1));
+
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ }
+ else if(mTryToConnectToFbc)
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook();
+ mTryToConnectToFbc = false;
+ }
+
+ updateSuggestedFriendList();
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople");
+ LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople");
+ }
+}
+
void LLPanelPeople::updateButtons()
{
std::string cur_tab = getActiveTabName();
@@ -989,23 +1081,25 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)
{
// store accordion tabs opened/closed state before any manipulation with accordion tabs
if (!saved_filter.empty())
- {
- notifyChildren(LLSD().with("action","store_state"));
- }
+ {
+ notifyChildren(LLSD().with("action","store_state"));
+ }
mOnlineFriendList->setNameFilter(filter);
mAllFriendList->setNameFilter(filter);
+ mSuggestedFriends->setNameFilter(filter);
- setAccordionCollapsedByUser("tab_online", false);
- setAccordionCollapsedByUser("tab_all", false);
- showFriendsAccordionsIfNeeded();
+ setAccordionCollapsedByUser("tab_online", false);
+ setAccordionCollapsedByUser("tab_all", false);
+ setAccordionCollapsedByUser("tab_suggested_friends", false);
+ showFriendsAccordionsIfNeeded();
// restore accordion tabs state _after_ all manipulations
if(saved_filter.empty())
- {
- notifyChildren(LLSD().with("action","restore_state"));
- }
-}
+ {
+ notifyChildren(LLSD().with("action","restore_state"));
+ }
+ }
else if (cur_tab == GROUP_TAB_NAME)
{
mGroupList->setNameFilter(filter);
@@ -1225,7 +1319,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)
mAllFriendList->showPermissions(show_permissions);
mOnlineFriendList->showPermissions(show_permissions);
}
-}
+ }
void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)
{
@@ -1383,6 +1477,7 @@ void LLPanelPeople::showFriendsAccordionsIfNeeded()
// Expand and show accordions if needed, else - hide them
showAccordion("tab_online", mOnlineFriendList->filterHasMatches());
showAccordion("tab_all", mAllFriendList->filterHasMatches());
+ showAccordion("tab_suggested_friends", mSuggestedFriends->filterHasMatches());
// Rearrange accordions
LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
@@ -1446,4 +1541,5 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)
return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));
}
+
// EOF
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index 4740964dee..c7141f36ee 100755
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -22,7 +22,7 @@
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
- */
+ */
#ifndef LL_LLPANELPEOPLE_H
#define LL_LLPANELPEOPLE_H
@@ -30,6 +30,7 @@
#include <llpanel.h>
#include "llcallingcard.h" // for avatar tracker
+#include "llfloaterwebcontent.h"
#include "llvoiceclient.h"
class LLAvatarList;
@@ -55,6 +56,8 @@ public:
// when voice is available
/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ bool mTryToConnectToFbc;
+
// internals
class Updater;
@@ -73,8 +76,10 @@ private:
// methods indirectly called by the updaters
void updateFriendListHelpText();
void updateFriendList();
+ bool updateSuggestedFriendList();
void updateNearbyList();
void updateRecentList();
+ void updateFacebookList(bool visible);
bool isItemsFreeOfFriends(const uuid_vec_t& uuids);
@@ -121,6 +126,8 @@ private:
void onFriendListRefreshComplete(LLUICtrl*ctrl, const LLSD& param);
+ bool onConnectedToFacebook(const LLSD& data);
+
void setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed);
void setAccordionCollapsedByUser(const std::string& name, bool collapsed);
bool isAccordionCollapsedByUser(LLUICtrl* acc_tab);
@@ -129,6 +136,7 @@ private:
LLTabContainer* mTabContainer;
LLAvatarList* mOnlineFriendList;
LLAvatarList* mAllFriendList;
+ LLAvatarList* mSuggestedFriends;
LLAvatarList* mNearbyList;
LLAvatarList* mRecentList;
LLGroupList* mGroupList;
@@ -140,6 +148,7 @@ private:
Updater* mFriendListUpdater;
Updater* mNearbyListUpdater;
Updater* mRecentListUpdater;
+ Updater* mFacebookListUpdater;
Updater* mButtonsUpdater;
LLHandle< LLFloater > mPicker;
};
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 49f7361c4a..313056f06a 100755
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -47,6 +47,7 @@ namespace LLPanelPeopleMenus
PeopleContextMenu gPeopleContextMenu;
NearbyPeopleContextMenu gNearbyPeopleContextMenu;
+SuggestedFriendsContextMenu gSuggestedFriendsContextMenu;
//== PeopleContextMenu ===============================================================
@@ -301,4 +302,36 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
hide_context_entries(menu, items, disabled_items);
}
+//== SuggestedFriendsContextMenu ===============================================================
+
+LLContextMenu* SuggestedFriendsContextMenu::createMenu()
+{
+ // set up the callbacks for all of the avatar menu items
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLContextMenu* menu;
+
+ // Set up for one person selected menu
+ const LLUUID& id = mUUIDs.front();
+ registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, id));
+ registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, id));
+
+ // create the context menu from the XUI
+ menu = createFromFile("menu_people_nearby.xml");
+ buildContextMenu(*menu, 0x0);
+
+ return menu;
+}
+
+void SuggestedFriendsContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ items.push_back(std::string("view_profile"));
+ items.push_back(std::string("add_friend"));
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index 0a1dcef303..5367eca0d3 100755
--- a/indra/newview/llpanelpeoplemenus.h
+++ b/indra/newview/llpanelpeoplemenus.h
@@ -58,8 +58,21 @@ protected:
/*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags);
};
+/**
+ * Menu used in the suggested friends list.
+ */
+class SuggestedFriendsContextMenu : public PeopleContextMenu
+{
+public:
+ /*virtual*/ LLContextMenu * createMenu();
+
+protected:
+ /*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags);
+};
+
extern PeopleContextMenu gPeopleContextMenu;
extern NearbyPeopleContextMenu gNearbyPeopleContextMenu;
+extern SuggestedFriendsContextMenu gSuggestedFriendsContextMenu;
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 76d38f067d..9504f22a1d 100755
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -94,7 +94,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() :
mZoomObjectFace(0),
mVolumeSliderVisible(0),
mWindowShade(NULL),
- mHideImmediately(false)
+ mHideImmediately(false),
+ mSecureURL(false)
{
mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this));
mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this));
@@ -345,7 +346,7 @@ void LLPanelPrimMediaControls::updateShape()
// Disable zoom if HUD
mZoomCtrl->setEnabled(!is_hud);
mUnzoomCtrl->setEnabled(!is_hud);
- mSecureLockIcon->setVisible(false);
+ mSecureURL = false;
mCurrentURL = media_impl->getCurrentMediaURL();
mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate);
@@ -382,7 +383,7 @@ void LLPanelPrimMediaControls::updateShape()
mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible());
mWhitelistIcon->setVisible(false);
- mSecureLockIcon->setVisible(false);
+ mSecureURL = false;
if (mMediaPanelScroll)
{
mMediaPanelScroll->setVisible(false);
@@ -416,7 +417,7 @@ void LLPanelPrimMediaControls::updateShape()
mMediaPlaySliderCtrl->setEnabled(true);
}
- // video vloume
+ // video volume
if(volume <= 0.0)
{
mMuteBtn->setToggleState(true);
@@ -492,10 +493,8 @@ void LLPanelPrimMediaControls::updateShape()
std::string prefix = std::string("https://");
std::string test_prefix = mCurrentURL.substr(0, prefix.length());
LLStringUtil::toLower(test_prefix);
- if(test_prefix == prefix)
- {
- mSecureLockIcon->setVisible(has_focus);
- }
+ mSecureURL = has_focus && (test_prefix == prefix);
+ mCurrentURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL);
if(mCurrentURL!=mPreviousURL)
{
@@ -746,6 +745,9 @@ void LLPanelPrimMediaControls::draw()
clearFaceOnFade();
}
}
+
+ // Show/hide the lock icon for secure browsing
+ mSecureLockIcon->setVisible(mSecureURL && !mMediaAddress->hasFocus());
// Build rect for icon area in coord system of this panel
// Assumes layout_stack is a direct child of this panel
diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h
index eeb433e306..6d2eb3430e 100755
--- a/indra/newview/llpanelprimmediacontrols.h
+++ b/indra/newview/llpanelprimmediacontrols.h
@@ -191,6 +191,7 @@ private:
bool mUpdateSlider;
bool mClearFaceOnFade;
bool mHideImmediately;
+ bool mSecureURL;
LLMatrix4 mLastCameraMat;
EZoomLevel mCurrentZoom;
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index c53760bca1..b5c9f4a310 100755
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llavatarnamecache.h"
+#include "llerror.h"
#include "llimview.h"
#include "llfloaterimcontainer.h"
#include "llparticipantlist.h"
@@ -401,6 +402,23 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
adjustParticipant(avatar_id);
}
+static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents");
+
+
+void LLParticipantList::addTestAvatarAgents()
+{
+ LLFastTimer _(FTM_FOLDERVIEW_TEST);
+
+ LL_INFOS("LLParticipantList") << "start adding 300 users" << LL_ENDL;
+
+ for(int i = 0; i < 300; ++i)
+ {
+ addAvatarIDExceptAgent(LLUUID().generateNewID());
+ }
+
+ LL_INFOS("LLParticipantList") << "finished adding 300 users" << LL_ENDL;
+}
+
void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)
{
LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h
index 3a3ae76604..936e289c08 100755
--- a/indra/newview/llparticipantlist.h
+++ b/indra/newview/llparticipantlist.h
@@ -50,6 +50,7 @@ public:
* @param[in] avatar_id - Avatar UUID to be added into the list
*/
void addAvatarIDExceptAgent(const LLUUID& avatar_id);
+ void addTestAvatarAgents();
/**
* Refreshes the participant list.
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
new file mode 100644
index 0000000000..7532ebfc57
--- /dev/null
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -0,0 +1,874 @@
+/**
+* @file llsnapshotlivepreview.cpp
+* @brief Implementation of llsnapshotlivepreview
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llagentui.h"
+#include "llcombobox.h"
+#include "lleconomy.h"
+#include "llfloaterperms.h"
+#include "llfloaterreg.h"
+#include "llfloatersocial.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+#include "lllandmarkactions.h"
+#include "lllocalcliprect.h"
+#include "llnotificationsutil.h"
+#include "llslurl.h"
+#include "llsnapshotlivepreview.h"
+#include "lltoolfocus.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h" // upload_new_resource()
+#include "llviewerstats.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llwebsharing.h"
+#include "llwindow.h"
+#include "llworld.h"
+
+const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f;
+
+F32 SHINE_TIME = 0.5f;
+F32 SHINE_WIDTH = 0.6f;
+F32 SHINE_OPACITY = 0.3f;
+F32 FALL_TIME = 0.6f;
+S32 BORDER_WIDTH = 6;
+
+const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512
+
+std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
+
+LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p)
+ : LLView(p),
+ mColor(1.f, 0.f, 0.f, 0.5f),
+ mCurImageIndex(0),
+ mPreviewImage(NULL),
+ mThumbnailImage(NULL) ,
+ mThumbnailWidth(0),
+ mThumbnailHeight(0),
+ mPreviewImageEncoded(NULL),
+ mFormattedImage(NULL),
+ mShineCountdown(0),
+ mFlashAlpha(0.f),
+ mNeedsFlash(TRUE),
+ mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
+ mDataSize(0),
+ mSnapshotType(SNAPSHOT_POSTCARD),
+ mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))),
+ mSnapshotUpToDate(FALSE),
+ mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
+ mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
+ mSnapshotActive(FALSE),
+ mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
+{
+ setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
+ mSnapshotDelayTimer.setTimerExpirySec(0.0f);
+ mSnapshotDelayTimer.start();
+ // gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.insert(this);
+ setFollowsAll();
+ mWidth[0] = gViewerWindow->getWindowWidthRaw();
+ mWidth[1] = gViewerWindow->getWindowWidthRaw();
+ mHeight[0] = gViewerWindow->getWindowHeightRaw();
+ mHeight[1] = gViewerWindow->getWindowHeightRaw();
+ mImageScaled[0] = FALSE;
+ mImageScaled[1] = FALSE;
+
+ mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
+ mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
+ mThumbnailUpdateLock = FALSE ;
+ mThumbnailUpToDate = FALSE ;
+}
+
+LLSnapshotLivePreview::~LLSnapshotLivePreview()
+{
+ // delete images
+ mPreviewImage = NULL;
+ mPreviewImageEncoded = NULL;
+ mFormattedImage = NULL;
+
+ // gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.erase(this);
+}
+
+void LLSnapshotLivePreview::setMaxImageSize(S32 size)
+{
+ if(size < MAX_SNAPSHOT_IMAGE_SIZE)
+ {
+ mMaxImageSize = size;
+ }
+ else
+ {
+ mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
+ }
+}
+
+LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
+{
+ return mViewerImage[mCurImageIndex];
+}
+
+F32 LLSnapshotLivePreview::getAspect()
+{
+ F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
+ F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
+
+ if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ return image_aspect_ratio;
+ }
+ else
+ {
+ return window_aspect_ratio;
+ }
+}
+
+F32 LLSnapshotLivePreview::getImageAspect()
+{
+ if (!getCurrentImage())
+ {
+ return 0.f;
+ }
+
+ return getAspect() ;
+}
+
+void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
+{
+ // Invalidate current image.
+ lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
+ if (getSnapshotUpToDate())
+ {
+ S32 old_image_index = mCurImageIndex;
+ mCurImageIndex = (mCurImageIndex + 1) % 2;
+ setSize(mWidth[old_image_index], mHeight[old_image_index]);
+ mFallAnimTimer.start();
+ }
+ mSnapshotUpToDate = FALSE;
+
+ // Update snapshot source rect depending on whether we keep the aspect ratio.
+ LLRect& rect = mImageRect[mCurImageIndex];
+ rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
+
+ F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
+ F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
+
+ if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ // trim off top and bottom
+ S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
+ rect.mBottom += (getRect().getHeight() - new_height) / 2;
+ rect.mTop -= (getRect().getHeight() - new_height) / 2;
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ // trim off left and right
+ S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
+ rect.mLeft += (getRect().getWidth() - new_width) / 2;
+ rect.mRight -= (getRect().getWidth() - new_width) / 2;
+ }
+ }
+
+ // Stop shining animation.
+ mShineAnimTimer.stop();
+
+ // Update snapshot if requested.
+ if (new_snapshot)
+ {
+ mSnapshotDelayTimer.start();
+ mSnapshotDelayTimer.setTimerExpirySec(delay);
+ LLFloaterSnapshot::preUpdate();
+ LLFloaterSocial::preUpdate();
+ }
+
+ // Update thumbnail if requested.
+ if(new_thumbnail)
+ {
+ mThumbnailUpToDate = FALSE ;
+ }
+}
+
+void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
+{
+ llclamp(quality, 0, 100);
+ if (quality != mSnapshotQuality)
+ {
+ mSnapshotQuality = quality;
+ gSavedSettings.setS32("SnapshotQuality", quality);
+ mSnapshotUpToDate = FALSE;
+ }
+}
+
+void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
+{
+ F32 line_width ;
+ glGetFloatv(GL_LINE_WIDTH, &line_width) ;
+ glLineWidth(2.0f * line_width) ;
+ LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ;
+ gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ;
+ glLineWidth(line_width) ;
+
+ //draw four alpha rectangles to cover areas outside of the snapshot image
+ if(!mKeepAspectRatio)
+ {
+ LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ;
+ S32 dwl = 0, dwr = 0 ;
+ if(mThumbnailWidth > mPreviewRect.getWidth())
+ {
+ dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ;
+ dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ;
+
+ gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
+ gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
+ }
+
+ if(mThumbnailHeight > mPreviewRect.getHeight())
+ {
+ S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ;
+ gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y ,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ;
+
+ dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ;
+ gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ;
+ }
+ }
+}
+
+//called when the frame is frozen.
+void LLSnapshotLivePreview::draw()
+{
+ if (getCurrentImage() &&
+ mPreviewImageEncoded.notNull() &&
+ getSnapshotUpToDate())
+ {
+ LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
+ gl_rect_2d(getRect(), bg_color);
+ const LLRect& rect = getImageRect();
+ LLRect shadow_rect = rect;
+ shadow_rect.stretch(BORDER_WIDTH);
+ gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
+
+ LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
+ gGL.color4fv(image_color.mV);
+ gGL.getTexUnit(0)->bind(getCurrentImage());
+ // calculate UV scale
+ F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f);
+ F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f);
+ gGL.pushMatrix();
+ {
+ gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.texCoord2f(uv_width, uv_height);
+ gGL.vertex2i(rect.getWidth(), rect.getHeight() );
+
+ gGL.texCoord2f(0.f, uv_height);
+ gGL.vertex2i(0, rect.getHeight() );
+
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(uv_width, 0.f);
+ gGL.vertex2i(rect.getWidth(), 0);
+ }
+ gGL.end();
+ }
+ gGL.popMatrix();
+
+ gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha);
+ gl_rect_2d(getRect());
+ if (mNeedsFlash)
+ {
+ if (mFlashAlpha < 1.f)
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
+ }
+ else
+ {
+ mNeedsFlash = FALSE;
+ }
+ }
+ else
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ // Draw shining animation if appropriate.
+ if (mShineCountdown > 0)
+ {
+ mShineCountdown--;
+ if (mShineCountdown == 0)
+ {
+ mShineAnimTimer.start();
+ }
+ }
+ else if (mShineAnimTimer.getStarted())
+ {
+ lldebugs << "Drawing shining animation" << llendl;
+ F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
+
+ // draw "shine" effect
+ LLLocalClipRect clip(getLocalRect());
+ {
+ // draw diagonal stripe with gradient that passes over screen
+ S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
+ S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
+ S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
+ S32 y1 = 0;
+ S32 y2 = gViewerWindow->getWindowHeightScaled();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.color4f(1.f, 1.f, 1.f, 0.f);
+ gGL.vertex2i(x1, y1);
+ gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.vertex2i(x2, y1);
+
+ gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ gGL.vertex2i(x2, y1);
+ gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.color4f(1.f, 1.f, 1.f, 0.f);
+ gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.vertex2i(x3, y1);
+ }
+ gGL.end();
+ }
+
+ // if we're at the end of the animation, stop
+ if (shine_interp >= 1.f)
+ {
+ mShineAnimTimer.stop();
+ }
+ }
+ }
+
+ // draw framing rectangle
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f(1.f, 1.f, 1.f, 1.f);
+ const LLRect& outline_rect = getImageRect();
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
+
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ }
+ gGL.end();
+ }
+
+ // draw old image dropping away
+ if (mFallAnimTimer.getStarted())
+ {
+ S32 old_image_index = (mCurImageIndex + 1) % 2;
+ if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
+ {
+ lldebugs << "Drawing fall animation" << llendl;
+ F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
+ F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
+ LLColor4 image_color(1.f, 1.f, 1.f, alpha);
+ gGL.color4fv(image_color.mV);
+ gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]);
+ // calculate UV scale
+ // *FIX get this to work with old image
+ BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
+ F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
+ F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
+ gGL.pushMatrix();
+ {
+ LLRect& rect = mImageRect[old_image_index];
+ gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
+ gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.texCoord2f(uv_width, uv_height);
+ gGL.vertex2i(rect.getWidth(), rect.getHeight() );
+
+ gGL.texCoord2f(0.f, uv_height);
+ gGL.vertex2i(0, rect.getHeight() );
+
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(uv_width, 0.f);
+ gGL.vertex2i(rect.getWidth(), 0);
+ }
+ gGL.end();
+ }
+ gGL.popMatrix();
+ }
+ }
+}
+
+/*virtual*/
+void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLRect old_rect = getRect();
+ LLView::reshape(width, height, called_from_parent);
+ if (old_rect.getWidth() != width || old_rect.getHeight() != height)
+ {
+ lldebugs << "window reshaped, updating thumbnail" << llendl;
+ updateSnapshot(FALSE, TRUE);
+ }
+}
+
+BOOL LLSnapshotLivePreview::setThumbnailImageSize()
+{
+ if(getWidth() < 10 || getHeight() < 10)
+ {
+ return FALSE ;
+ }
+ S32 window_width = gViewerWindow->getWindowWidthRaw() ;
+ S32 window_height = gViewerWindow->getWindowHeightRaw() ;
+
+ F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
+
+ // UI size for thumbnail
+ // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h.
+ const LLRect& thumbnail_rect = mThumbnailPlaceholderRect;
+ S32 max_width = thumbnail_rect.getWidth();
+ S32 max_height = thumbnail_rect.getHeight();
+
+ if (window_aspect_ratio > (F32)max_width / max_height)
+ {
+ // image too wide, shrink to width
+ mThumbnailWidth = max_width;
+ mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
+ }
+ else
+ {
+ // image too tall, shrink to height
+ mThumbnailHeight = max_height;
+ mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
+ }
+
+ if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
+ {
+ return FALSE ;//if the window is too small, ignore thumbnail updating.
+ }
+
+ S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
+ if(!mKeepAspectRatio)
+ {
+ F32 ratio_x = (F32)getWidth() / window_width ;
+ F32 ratio_y = (F32)getHeight() / window_height ;
+
+ //if(getWidth() > window_width ||
+ // getHeight() > window_height )
+ {
+ if(ratio_x > ratio_y)
+ {
+ top = (S32)(top * ratio_y / ratio_x) ;
+ }
+ else
+ {
+ right = (S32)(right * ratio_x / ratio_y) ;
+ }
+ }
+ //else
+ //{
+ // right = (S32)(right * ratio_x) ;
+ // top = (S32)(top * ratio_y) ;
+ //}
+ left = (S32)((mThumbnailWidth - right) * 0.5f) ;
+ bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
+ top += bottom ;
+ right += left ;
+ }
+ mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ;
+
+ return TRUE ;
+}
+
+void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
+{
+ if(mThumbnailUpdateLock) //in the process of updating
+ {
+ return ;
+ }
+ if(getThumbnailUpToDate() && !force_update)//already updated
+ {
+ return ;
+ }
+ if(getWidth() < 10 || getHeight() < 10)
+ {
+ return ;
+ }
+
+ ////lock updating
+ mThumbnailUpdateLock = TRUE ;
+
+ if(!setThumbnailImageSize())
+ {
+ mThumbnailUpdateLock = FALSE ;
+ mThumbnailUpToDate = TRUE ;
+ return ;
+ }
+
+ if(mThumbnailImage)
+ {
+ resetThumbnailImage() ;
+ }
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if(!gViewerWindow->thumbnailSnapshot(raw,
+ mThumbnailWidth, mThumbnailHeight,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ mSnapshotBufferType) )
+ {
+ raw = NULL ;
+ }
+
+ if(raw)
+ {
+ raw->expandToPowerOfTwo();
+ mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+ mThumbnailUpToDate = TRUE ;
+ }
+
+ //unlock updating
+ mThumbnailUpdateLock = FALSE ;
+}
+
+
+// Called often. Checks whether it's time to grab a new snapshot and if so, does it.
+// Returns TRUE if new snapshot generated, FALSE otherwise.
+//static
+BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+ if (previewp->getWidth() == 0 || previewp->getHeight() == 0)
+ {
+ llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl;
+ return FALSE;
+ }
+
+ // If we're in freeze-frame mode and camera has moved, update snapshot.
+ LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
+ LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
+ if (gSavedSettings.getBOOL("FreezeTime") &&
+ (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
+ {
+ previewp->mCameraPos = new_camera_pos;
+ previewp->mCameraRot = new_camera_rot;
+ // request a new snapshot whenever the camera moves, with a time delay
+ BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
+ lldebugs << "camera moved, updating thumbnail" << llendl;
+ previewp->updateSnapshot(
+ autosnap, // whether a new snapshot is needed or merely invalidate the existing one
+ FALSE, // or if 1st arg is false, whether to produce a new thumbnail image.
+ autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true.
+ }
+
+ // see if it's time yet to snap the shot and bomb out otherwise.
+ previewp->mSnapshotActive =
+ (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
+ && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
+ if ( ! previewp->mSnapshotActive)
+ {
+ return FALSE;
+ }
+
+ // time to produce a snapshot
+ previewp->setThumbnailImageSize();
+
+ lldebugs << "producing snapshot" << llendl;
+ if (!previewp->mPreviewImage)
+ {
+ previewp->mPreviewImage = new LLImageRaw;
+ }
+
+ if (!previewp->mPreviewImageEncoded)
+ {
+ previewp->mPreviewImageEncoded = new LLImageRaw;
+ }
+
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+
+ previewp->getWindow()->incBusyCount();
+ previewp->setImageScaled(FALSE);
+
+ // grab the raw image and encode it into desired format
+ if(gViewerWindow->rawSnapshot(
+ previewp->mPreviewImage,
+ previewp->getWidth(),
+ previewp->getHeight(),
+ previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
+ previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ previewp->mSnapshotBufferType,
+ previewp->getMaxImageSize()))
+ {
+ previewp->mPreviewImageEncoded->resize(
+ previewp->mPreviewImage->getWidth(),
+ previewp->mPreviewImage->getHeight(),
+ previewp->mPreviewImage->getComponents());
+
+ if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ lldebugs << "Encoding new image of format J2C" << llendl;
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(
+ previewp->mPreviewImage->getData(),
+ previewp->mPreviewImage->getWidth(),
+ previewp->mPreviewImage->getHeight(),
+ previewp->mPreviewImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
+ previewp->setImageScaled(TRUE);
+ if (formatted->encode(scaled, 0.f))
+ {
+ previewp->mDataSize = formatted->getDataSize();
+ formatted->decode(previewp->mPreviewImageEncoded, 0);
+ }
+ }
+ else
+ {
+ // delete any existing image
+ previewp->mFormattedImage = NULL;
+ // now create the new one of the appropriate format.
+ LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
+ lldebugs << "Encoding new image of format " << format << llendl;
+
+ switch(format)
+ {
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
+ previewp->mFormattedImage = new LLImagePNG();
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
+ previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
+ previewp->mFormattedImage = new LLImageBMP();
+ break;
+ }
+ if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0))
+ {
+ previewp->mDataSize = previewp->mFormattedImage->getDataSize();
+ // special case BMP to copy instead of decode otherwise decode will crash.
+ if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
+ {
+ previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage);
+ }
+ else
+ {
+ previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0);
+ }
+ }
+ }
+
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(
+ previewp->mPreviewImageEncoded->getData(),
+ previewp->mPreviewImageEncoded->getWidth(),
+ previewp->mPreviewImageEncoded->getHeight(),
+ previewp->mPreviewImageEncoded->getComponents());
+
+ if(!scaled->isBufferInvalid())
+ {
+ // leave original image dimensions, just scale up texture buffer
+ if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
+ {
+ // go ahead and shrink image to appropriate power of 2 for display
+ scaled->biasedScaleToPowerOfTwo(1024);
+ previewp->setImageScaled(TRUE);
+ }
+ else
+ {
+ // expand image but keep original image data intact
+ scaled->expandToPowerOfTwo(1024, FALSE);
+ }
+
+ previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
+ LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
+ gGL.getTexUnit(0)->bind(curr_preview_image);
+ if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE)
+ {
+ curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT);
+ }
+ else
+ {
+ curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+ }
+ curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ previewp->mSnapshotUpToDate = TRUE;
+ previewp->generateThumbnailImage(TRUE) ;
+
+ previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
+ previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
+ }
+ }
+ previewp->getWindow()->decBusyCount();
+ // only show fullscreen preview when in freeze frame mode
+ previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
+ previewp->mSnapshotDelayTimer.stop();
+ previewp->mSnapshotActive = FALSE;
+
+ if(!previewp->getThumbnailUpToDate())
+ {
+ previewp->generateThumbnailImage() ;
+ }
+ lldebugs << "done creating snapshot" << llendl;
+ LLFloaterSnapshot::postUpdate();
+ LLFloaterSocial::postUpdate();
+
+ return TRUE;
+}
+
+void LLSnapshotLivePreview::setSize(S32 w, S32 h)
+{
+ lldebugs << "setSize(" << w << ", " << h << ")" << llendl;
+ setWidth(w);
+ setHeight(h);
+}
+
+void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
+{
+ w = getWidth();
+ h = getHeight();
+}
+
+void LLSnapshotLivePreview::saveTexture()
+{
+ lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl;
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(),
+ mPreviewImage->getWidth(),
+ mPreviewImage->getHeight(),
+ mPreviewImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
+ lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl;
+
+ if (formatted->encode(scaled, 0.0f))
+ {
+ LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
+ std::string pos_string;
+ LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL);
+ std::string who_took_it;
+ LLAgentUI::buildFullname(who_took_it);
+ LLAssetStorage::LLStoreAssetCallback callback = NULL;
+ S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+ void *userdata = NULL;
+ upload_new_resource(tid, // tid
+ LLAssetType::AT_TEXTURE,
+ "Snapshot : " + pos_string,
+ "Taken by " + who_took_it + " at " + pos_string,
+ 0,
+ LLFolderType::FT_SNAPSHOT_CATEGORY,
+ LLInventoryType::IT_SNAPSHOT,
+ PERM_ALL, // Note: Snapshots to inventory is a special case of content upload
+ LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads
+ LLFloaterPerms::getEveryonePerms(),
+ "Snapshot : " + pos_string,
+ callback, expected_upload_cost, userdata);
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ else
+ {
+ LLNotificationsUtil::add("ErrorEncodingSnapshot");
+ llwarns << "Error encoding snapshot" << llendl;
+ }
+
+ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
+
+ mDataSize = 0;
+}
+
+BOOL LLSnapshotLivePreview::saveLocal()
+{
+ BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
+
+ if(success)
+ {
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ return success;
+}
+
+void LLSnapshotLivePreview::saveWeb()
+{
+ // *FIX: Will break if the window closes because of CloseSnapshotOnKeep!
+ // Needs to pass on ownership of the image.
+ LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get());
+ if(!jpg)
+ {
+ llwarns << "Formatted image not a JPEG" << llendl;
+ return;
+ }
+
+ LLSD metadata;
+ metadata["description"] = getChild<LLLineEditor>("description")->getText();
+
+ LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(),
+ boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4));
+
+ gViewerWindow->playSnapshotAnimAndSound();
+}
+
+void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z)
+{
+ metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString();
+
+ LLWebSharing::instance().shareSnapshot(snapshot, metadata);
+}
diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h
new file mode 100644
index 0000000000..fe3d257b02
--- /dev/null
+++ b/indra/newview/llsnapshotlivepreview.h
@@ -0,0 +1,164 @@
+/**
+* @file llsnapshotlivepreview.h
+* @brief Header file for llsnapshotlivepreview
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLSNAPSHOTLIVEPREVIEW_H
+#define LL_LLSNAPSHOTLIVEPREVIEW_H
+
+#include "llpanelsnapshot.h"
+#include "llviewerwindow.h"
+
+class LLImageJPEG;
+
+///----------------------------------------------------------------------------
+/// Class LLSnapshotLivePreview
+///----------------------------------------------------------------------------
+class LLSnapshotLivePreview : public LLView
+{
+ LOG_CLASS(LLSnapshotLivePreview);
+public:
+ enum ESnapshotType
+ {
+ SNAPSHOT_POSTCARD,
+ SNAPSHOT_TEXTURE,
+ SNAPSHOT_LOCAL,
+ SNAPSHOT_WEB
+ };
+
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Params()
+ {
+ name = "snapshot_live_preview";
+ mouse_opaque = false;
+ }
+ };
+
+
+ LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
+ ~LLSnapshotLivePreview();
+
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void setSize(S32 w, S32 h);
+ void setWidth(S32 w) { mWidth[mCurImageIndex] = w; }
+ void setHeight(S32 h) { mHeight[mCurImageIndex] = h; }
+ void getSize(S32& w, S32& h) const;
+ S32 getWidth() const { return mWidth[mCurImageIndex]; }
+ S32 getHeight() const { return mHeight[mCurImageIndex]; }
+ S32 getDataSize() const { return mDataSize; }
+ void setMaxImageSize(S32 size) ;
+ S32 getMaxImageSize() {return mMaxImageSize ;}
+
+ ESnapshotType getSnapshotType() const { return mSnapshotType; }
+ LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; }
+ BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
+ BOOL isSnapshotActive() { return mSnapshotActive; }
+ LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; }
+ S32 getThumbnailWidth() const { return mThumbnailWidth ; }
+ S32 getThumbnailHeight() const { return mThumbnailHeight ; }
+ BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
+ BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
+ LLViewerTexture* getCurrentImage();
+ F32 getImageAspect();
+ F32 getAspect() ;
+ const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; }
+ BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; }
+ void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; }
+ const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
+
+ void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
+ void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
+ void setSnapshotQuality(S32 quality);
+ void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
+ void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
+ void saveWeb();
+ void saveTexture();
+ BOOL saveLocal();
+
+ LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
+ LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
+
+ /// Sets size of preview thumbnail image and thhe surrounding rect.
+ void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; }
+ BOOL setThumbnailImageSize() ;
+ void generateThumbnailImage(BOOL force_update = FALSE) ;
+ void resetThumbnailImage() { mThumbnailImage = NULL ; }
+ void drawPreviewRect(S32 offset_x, S32 offset_y) ;
+
+ // Returns TRUE when snapshot generated, FALSE otherwise.
+ static BOOL onIdle( void* snapshot_preview );
+
+ // callback for region name resolve
+ void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z);
+
+private:
+ LLColor4 mColor;
+ LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
+ LLRect mImageRect[2];
+ S32 mWidth[2];
+ S32 mHeight[2];
+ BOOL mImageScaled[2];
+ S32 mMaxImageSize ;
+
+ //thumbnail image
+ LLPointer<LLViewerTexture> mThumbnailImage ;
+ S32 mThumbnailWidth ;
+ S32 mThumbnailHeight ;
+ LLRect mPreviewRect ;
+ BOOL mThumbnailUpdateLock ;
+ BOOL mThumbnailUpToDate ;
+ LLRect mThumbnailPlaceholderRect;
+
+ S32 mCurImageIndex;
+ LLPointer<LLImageRaw> mPreviewImage;
+ LLPointer<LLImageRaw> mPreviewImageEncoded;
+ LLPointer<LLImageFormatted> mFormattedImage;
+ LLFrameTimer mSnapshotDelayTimer;
+ S32 mShineCountdown;
+ LLFrameTimer mShineAnimTimer;
+ F32 mFlashAlpha;
+ BOOL mNeedsFlash;
+ LLVector3d mPosTakenGlobal;
+ S32 mSnapshotQuality;
+ S32 mDataSize;
+ ESnapshotType mSnapshotType;
+ LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat;
+ BOOL mSnapshotUpToDate;
+ LLFrameTimer mFallAnimTimer;
+ LLVector3 mCameraPos;
+ LLQuaternion mCameraRot;
+ BOOL mSnapshotActive;
+ LLViewerWindow::ESnapshotType mSnapshotBufferType;
+
+public:
+ static std::set<LLSnapshotLivePreview*> sList;
+ BOOL mKeepAspectRatio ;
+};
+
+#endif // LL_LLSNAPSHOTLIVEPREVIEW_H
+
diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp
index 75e6e3d13a..025ef3945d 100755
--- a/indra/newview/lltoastimpanel.cpp
+++ b/indra/newview/lltoastimpanel.cpp
@@ -28,6 +28,7 @@
#include "lltoastimpanel.h"
#include "llagent.h"
+#include "llavatarnamecache.h"
#include "llfloaterreg.h"
#include "llgroupactions.h"
#include "llgroupiconctrl.h"
@@ -61,6 +62,15 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif
style_params.font.name(font_name);
style_params.font.size(font_size);
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(p.session_id);
+ mIsGroupMsg = (im_session->mSessionType == LLIMModel::LLIMSession::GROUP_SESSION);
+ if(mIsGroupMsg)
+ {
+ mAvatarName->setValue(im_session->mName);
+ LLAvatarName avatar_name;
+ LLAvatarNameCache::get(p.avatar_id, &avatar_name);
+ p.message = "[From " + avatar_name.getDisplayName() + "]\n" + p.message;
+ }
//Handle IRC styled /me messages.
std::string prefix = p.message.substr(0, 4);
@@ -81,12 +91,17 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif
mMessage->setText(p.message, style_params);
}
- mAvatarName->setValue(p.from);
+ if(!mIsGroupMsg)
+ {
+ mAvatarName->setValue(p.from);
+ }
mTime->setValue(p.time);
mSessionID = p.session_id;
mAvatarID = p.avatar_id;
mNotification = p.notification;
+
+
initIcon();
S32 maxLinesCount;
@@ -147,7 +162,14 @@ void LLToastIMPanel::spawnNameToolTip()
LLToolTip::Params params;
params.background_visible(false);
- params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE));
+ if(!mIsGroupMsg)
+ {
+ params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE));
+ }
+ else
+ {
+ params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_group", LLSD().with("group_id", mSessionID), FALSE));
+ }
params.delay_time(0.0f); // spawn instantly on hover
params.image(LLUI::getUIImage("Info_Small"));
params.message("");
diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h
index 3eb11fb3bc..767617dabc 100755
--- a/indra/newview/lltoastimpanel.h
+++ b/indra/newview/lltoastimpanel.h
@@ -73,6 +73,8 @@ private:
LLTextBox* mAvatarName;
LLTextBox* mTime;
LLTextBox* mMessage;
+
+ bool mIsGroupMsg;
};
#endif // LLTOASTIMPANEL_H_
diff --git a/indra/newview/llurlhistory.cpp b/indra/newview/llurlhistory.cpp
index dd17068be5..a80b9da13c 100755
--- a/indra/newview/llurlhistory.cpp
+++ b/indra/newview/llurlhistory.cpp
@@ -103,22 +103,29 @@ LLSD LLURLHistory::getURLHistory(const std::string& collection)
// static
void LLURLHistory::addURL(const std::string& collection, const std::string& url)
{
- if(! url.empty())
+ if(!url.empty())
{
- sHistorySD[collection].insert(0, url);
+ LLURI u(url);
+ std::string simplified_url = u.scheme() + "://" + u.authority() + u.path();
+ sHistorySD[collection].insert(0, simplified_url);
LLURLHistory::limitSize(collection);
}
}
// static
void LLURLHistory::removeURL(const std::string& collection, const std::string& url)
{
- for(int index = 0; index < sHistorySD[collection].size(); index++)
+ if(!url.empty())
{
- if(sHistorySD[collection].get(index).asString() == url)
- {
- sHistorySD[collection].erase(index);
- }
- }
+ LLURI u(url);
+ std::string simplified_url = u.scheme() + "://" + u.authority() + u.path();
+ for(int index = 0; index < sHistorySD[collection].size(); index++)
+ {
+ if(sHistorySD[collection].get(index).asString() == simplified_url)
+ {
+ sHistorySD[collection].erase(index);
+ }
+ }
+ }
}
// static
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c6b28b9e5e..4ce049df03 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -103,6 +103,7 @@
#include "llfloatersettingsdebug.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloatersnapshot.h"
+#include "llfloatersocial.h"
#include "llfloatersounddevices.h"
#include "llfloaterspellchecksettings.h"
#include "llfloatertelehub.h"
@@ -303,6 +304,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater);
LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>);
LLFloaterReg::add("sound_devices", "floater_sound_devices.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundDevices>);
+ LLFloaterReg::add("social", "floater_social.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSocial>);
LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>);
LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);
@@ -311,7 +313,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
-
+ LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterUIPreviewUtil::registerFloater();
LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload");
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 2df028de69..13483790ed 100755
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -2000,7 +2000,12 @@ void LLViewerMediaImpl::loadURI()
"<>#%"
";/?:@&=",
false);
- llinfos << "Asking media source to load URI: " << uri << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(uri);
+ std::string sanitized_uri = (u.query().empty() ? uri : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "Asking media source to load URI: " << sanitized_uri << llendl;
+ }
mMediaSource->loadURI( uri );
@@ -2567,7 +2572,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED)
{
// Helpful to have media urls in log file. Shouldn't be spammy.
- llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(url);
+ std::string sanitized_url = (u.query().empty() ? url : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "NOT LOADING media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mime_type << llendl;
+ }
// This impl should not be loaded at this time.
LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL;
@@ -2582,7 +2592,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
void LLViewerMediaImpl::navigateInternal()
{
// Helpful to have media urls in log file. Shouldn't be spammy.
- llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(mMediaURL);
+ std::string sanitized_url = (u.query().empty() ? mMediaURL : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mMimeType << llendl;
+ }
if(mNavigateSuspended)
{
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 49eb7dc94a..1c2cb978c7 100755
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -45,6 +45,7 @@
#include "llagent.h"
#include "llagentaccess.h"
#include "llagentcamera.h"
+#include "llagentui.h"
#include "llagentwearables.h"
#include "llagentpilot.h"
#include "llcompilequeue.h"
@@ -52,6 +53,7 @@
#include "lldaycyclemanager.h"
#include "lldebugview.h"
#include "llenvmanager.h"
+#include "llfacebookconnect.h"
#include "llfilepicker.h"
#include "llfirstuse.h"
#include "llfloaterbuy.h"
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 0db118835c..4d57fbc891 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2649,7 +2649,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
{
send_do_not_disturb_message(msg, from_id);
}
- else
+
+ if (!is_muted)
{
LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL;
// Read the binary bucket for more information.
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 8422708add..455495df55 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1585,6 +1585,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("EnvironmentSettings");
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
+ capabilityNames.append("FacebookConnect");
+ //capabilityNames.append("FacebookRedirect");
if (gSavedSettings.getBOOL("UseHTTPInventory"))
{
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 87ae224655..40b8560071 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1813,7 +1813,6 @@ void LLViewerWindow::initBase()
gFloaterView = main_view->getChild<LLFloaterView>("Floater View");
gFloaterView->setFloaterSnapView(main_view->getChild<LLView>("floater_snap_region")->getHandle());
gSnapshotFloaterView = main_view->getChild<LLSnapshotFloaterView>("Snapshot Floater View");
-
// Console
llassert( !gConsole );
diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp
index 641f338f2c..69255af179 100755
--- a/indra/newview/llwebprofile.cpp
+++ b/indra/newview/llwebprofile.cpp
@@ -148,9 +148,6 @@ public:
LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl;
LLWebProfile::reportImageUploadStatus(true);
}
-
-private:
- LLPointer<LLImageFormatted> mImagep;
};
@@ -172,7 +169,7 @@ public:
headers["Cookie"] = LLWebProfile::getAuthCookie();
const std::string& redir_url = content["location"];
LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl;
- LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers);
+ LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder(), headers);
}
else
{
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index a9176595c7..f53995732f 100755
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -884,4 +884,19 @@
<color
name="blue"
value="0 0 1 1"/>
+
+ <!--Resize bar colors -->
+
+ <color
+ name="ResizebarBorderLight"
+ value="0.231 0.231 0.231 1"/>
+
+ <color
+ name="ResizebarBorderDark"
+ value="0.133 0.133 0.133 1"/>
+
+ <color
+ name="ResizebarBody"
+ value="0.208 0.208 0.208 1"/>
+
</colors>
diff --git a/indra/newview/skins/default/textures/icons/Facebook.png b/indra/newview/skins/default/textures/icons/Facebook.png
new file mode 100644
index 0000000000..8287d56f88
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Facebook.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/map_placeholder.png b/indra/newview/skins/default/textures/icons/map_placeholder.png
new file mode 100644
index 0000000000..31e457aa75
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/map_placeholder.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index b0e4b71d21..94c187e21a 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -148,6 +148,7 @@ with the same filename but different name
<texture name="Command_Preferences_Icon" file_name="toolbar_icons/preferences.png" preload="true" />
<texture name="Command_Profile_Icon" file_name="toolbar_icons/profile.png" preload="true" />
<texture name="Command_Search_Icon" file_name="toolbar_icons/search.png" preload="true" />
+ <texture name="Command_Social_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
<texture name="Command_Snapshot_Icon" file_name="toolbar_icons/snapshot.png" preload="true" />
<texture name="Command_Speak_Icon" file_name="toolbar_icons/speak.png" preload="true" />
<texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" />
@@ -199,6 +200,8 @@ with the same filename but different name
<texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" />
<texture name="Edit_Wrench" file_name="icons/Edit_Wrench.png" preload="false" />
+ <texture name="Facebook_Icon" file_name="icons/Facebook.png" preload="false" />
+
<texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
<texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" />
<texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" />
@@ -323,6 +326,8 @@ with the same filename but different name
<texture name="Locked_Icon" file_name="icons/Locked_Icon.png" preload="false" />
+ <texture name="Map_Placeholder_Icon" file_name="icons/map_placeholder.png" preload="true" />
+
<texture name="MarketplaceBtn_Off" file_name="widgets/MarketplaceBtn_Off.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" />
<texture name="MarketplaceBtn_Selected" file_name="widgets/MarketplaceBtn_Selected.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" />
@@ -565,6 +570,7 @@ with the same filename but different name
<texture name="Snapshot_Email" file_name="snapshot_email.png" preload="false" />
<texture name="Snapshot_Inventory" file_name="toolbar_icons/inventory.png" preload="false" />
<texture name="Snapshot_Profile" file_name="toolbar_icons/profile.png" preload="false" />
+ <texture name="Snapshot_Facebook" file_name="toolbar_icons/facebook.png" preload="false" />
<texture name="startup_logo" file_name="windows/startup_logo.png" preload="true" />
@@ -774,4 +780,7 @@ with the same filename but different name
<texture name="Popup_Caution" file_name="icons/pop_up_caution.png"/>
<texture name="Camera_Drag_Dot" file_name="world/CameraDragDot.png"/>
<texture name="NavBar Separator" file_name="navbar/separator.png"/>
+
+ <texture name="Horizontal Drag Handle" file_name="widgets/horizontal_drag_handle.png"/>
+ <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png"/>
</textures>
diff --git a/indra/newview/skins/default/textures/toolbar_icons/facebook.png b/indra/newview/skins/default/textures/toolbar_icons/facebook.png
new file mode 100644
index 0000000000..b960b834dc
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/facebook.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png
new file mode 100644
index 0000000000..642eac4065
--- /dev/null
+++ b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png
new file mode 100644
index 0000000000..b06b70cf36
--- /dev/null
+++ b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png
Binary files differ
diff --git a/indra/newview/skins/default/xui/en/floater_fbc_web.xml b/indra/newview/skins/default/xui/en/floater_fbc_web.xml
new file mode 100644
index 0000000000..0d35e22a19
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_fbc_web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater name="floater_fbc_web"
+ help_topic="fbc_web"
+ width="780"
+ height="775"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="false"
+ filename="floater_web_content.xml"/>
diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml
index 65f623a47e..da016462db 100755
--- a/indra/newview/skins/default/xui/en/floater_im_container.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_container.xml
@@ -24,24 +24,28 @@
value="Conv_toolbar_expand"/>
<layout_stack
animate="true"
- bottom="-1"
+ bottom="-5"
+ drag_handle_gap="6"
+ drag_handle_first_indent="27"
+ drag_handle_second_indent="10"
follows="all"
layout="topleft"
left="0"
name="conversations_stack"
orientation="horizontal"
right="-1"
+ show_drag_handle="true"
top="0">
<layout_panel
auto_resize="false"
user_resize="true"
name="conversations_layout_panel"
min_dim="38"
- expanded_min_dim="156">
+ expanded_min_dim="136">
<layout_stack
animate="false"
follows="left|top|right"
- height="35"
+ height="27"
layout="topleft"
left="0"
name="conversations_pane_buttons_stack"
@@ -50,7 +54,6 @@
top="0">
<layout_panel
auto_resize="true"
- height="35"
name="conversations_pane_buttons_expanded">
<menu_button
follows="top|left"
@@ -64,7 +67,7 @@
left="5"
name="sort_btn"
tool_tip="View/sort options"
- top="5"
+ top="1"
width="31" />
<button
follows="top|left"
@@ -74,7 +77,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="add_btn"
tool_tip="Start a new conversation"
@@ -87,7 +90,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="speak_btn"
tool_tip="Speak with people using your microphone"
@@ -95,9 +98,8 @@
</layout_panel>
<layout_panel
auto_resize="false"
- height="35"
name="conversations_pane_buttons_collapsed"
- width="41">
+ width="31">
<button
follows="right|top"
height="25"
@@ -106,8 +108,8 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
- left="1"
+ top="1"
+ left="0"
name="expand_collapse_btn"
tool_tip="Collapse/Expand this list"
width="31" />
@@ -119,7 +121,7 @@
layout="topleft"
name="conversations_list_panel"
opaque="true"
- top="35"
+ top_pad="0"
left="5"
right="-1"/>
</layout_panel>
@@ -127,7 +129,7 @@
auto_resize="true"
user_resize="true"
name="messages_layout_panel"
- expanded_min_dim="222">
+ expanded_min_dim="212">
<panel_container
bottom="-1"
follows="all"
@@ -136,44 +138,44 @@
name="im_box_tab_container"
right="-1"
top="0">
- <panel
- bottom="-1"
- follows="all"
- layout="topleft"
- name="stub_panel"
- opaque="true"
- top_pad="0"
- left="0"
- right="-1">
- <button
- follows="right|top"
- height="25"
- image_hover_unselected="Toolbar_Middle_Over"
- image_overlay="Conv_toolbar_collapse"
- image_selected="Toolbar_Middle_Selected"
- image_unselected="Toolbar_Middle_Off"
+ <panel
+ bottom="-1"
+ follows="all"
layout="topleft"
- top="5"
- right="-10"
- name="stub_collapse_btn"
- tool_tip="Collapse this pane"
- width="31" />
- <text
- type="string"
- clip_partial="false"
- follows="left|top|right"
- layout="topleft"
- left="15"
- right="-15"
- name="stub_textbox"
- top="25"
- height="40"
- valign="center"
- parse_urls="true"
- wrap="true">
- This conversation is in a separate window. [secondlife:/// Bring it back.]
- </text>
- </panel>
+ name="stub_panel"
+ opaque="true"
+ top_pad="0"
+ left="0"
+ right="-1">
+ <button
+ follows="right|top"
+ height="25"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Conv_toolbar_collapse"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ top="1"
+ right="-10"
+ name="stub_collapse_btn"
+ tool_tip="Collapse this pane"
+ width="31" />
+ <text
+ type="string"
+ clip_partial="false"
+ follows="left|top|right"
+ layout="topleft"
+ left="15"
+ right="-15"
+ name="stub_textbox"
+ top="25"
+ height="40"
+ valign="center"
+ parse_urls="true"
+ wrap="true">
+ This conversation is in a separate window. [secondlife:/// Bring it back.]
+ </text>
+ </panel>
</panel_container>
</layout_panel>
</layout_stack>
diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml
index 2152a9f6e9..8da4213c65 100755
--- a/indra/newview/skins/default/xui/en/floater_im_session.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_session.xml
@@ -70,26 +70,23 @@
top="0"
left="0"
right="-1"
- bottom="-3">
+ bottom="-1">
<layout_stack
animate="false"
+ bottom="-1"
default_tab_group="2"
follows="all"
- right="-5"
- bottom="-1"
- top="0"
- left="5"
- border_size="0"
+ left="3"
layout="topleft"
- orientation="vertical"
name="main_stack"
- tab_group="1">
+ right="-3"
+ orientation="vertical"
+ tab_group="1"
+ top="0">
<layout_panel
auto_resize="false"
name="toolbar_panel"
- height="35"
- right="-1"
- left="1">
+ height="25">
<menu_button
menu_filename="menu_im_session_showmodes.xml"
follows="top|left"
@@ -102,7 +99,7 @@
left="5"
name="view_options_btn"
tool_tip="View/sort options"
- top="5"
+ top="1"
width="31" />
<menu_button
menu_filename="menu_im_conversation.xml"
@@ -113,7 +110,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="gear_btn"
visible="false"
@@ -128,7 +125,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="add_btn"
tool_tip="Add someone to this conversation"
@@ -141,7 +138,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="voice_call_btn"
tool_tip="Open voice connection"
@@ -166,8 +163,8 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
- right="-67"
+ top="1"
+ right="-70"
name="close_btn"
tool_tip="End this conversation"
width="31" />
@@ -179,7 +176,7 @@
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
- top="5"
+ top="1"
left_pad="2"
name="expand_collapse_btn"
tool_tip="Collapse/Expand this pane"
@@ -194,18 +191,21 @@
layout="topleft"
left_pad="2"
name="tear_off_btn"
- top="5"
+ top="1"
width="31" />
</layout_panel>
<layout_panel
name="body_panel"
- top="1"
- bottom="-1">
+ height="235">
<layout_stack
default_tab_group="2"
+ drag_handle_gap="6"
+ drag_handle_first_indent="0"
+ drag_handle_second_indent="1"
follows="all"
orientation="horizontal"
name="im_panels"
+ show_drag_handle="true"
tab_group="1"
top="0"
right="-1"
@@ -217,14 +217,12 @@
min_dim="0"
width="150"
user_resize="true"
- auto_resize="false"
- bottom="-1" />
+ auto_resize="false" />
<layout_panel
default_tab_group="3"
tab_group="2"
name="right_part_holder"
- min_width="221"
- bottom="-1">
+ min_width="172">
<layout_stack
animate="true"
default_tab_group="2"
@@ -233,7 +231,7 @@
name="translate_and_chat_stack"
tab_group="1"
top="0"
- left="0"
+ left="1"
right="-1"
bottom="-1">
<layout_panel
@@ -259,7 +257,7 @@
parse_highlights="true"
parse_urls="true"
right="-1"
- left="5"
+ left="0"
top="0"
bottom="-1" />
</layout_panel>
@@ -268,10 +266,7 @@
</layout_stack>
</layout_panel>
<layout_panel
- top_delta="0"
- top="0"
- height="26"
- bottom="-1"
+ height="35"
auto_resize="false"
name="chat_layout_panel">
<layout_stack
@@ -281,15 +276,11 @@
orientation="horizontal"
name="input_panels"
top="0"
- bottom="-2"
+ bottom="-1"
left="0"
right="-1">
<layout_panel
- name="input_editor_layout_panel"
- auto_resize="true"
- user_resize="false"
- top="0"
- bottom="-1">
+ name="input_editor_layout_panel">
<chat_editor
layout="topleft"
expand_lines_count="5"
@@ -302,32 +293,27 @@
max_length="1023"
spellcheck="true"
tab_group="3"
- top="1"
- bottom="-2"
- left="4"
- right="-4"
+ bottom="-8"
+ left="5"
+ right="-5"
wrap="true" />
</layout_panel>
<layout_panel
auto_resize="false"
- user_resize="false"
name="input_button_layout_panel"
- width="30"
- top="0"
- bottom="-1">
+ width="32">
<button
- layout="topleft"
left="1"
- right="-1"
- top="1"
- height="22"
+ top="4"
+ height="25"
follows="left|right|top"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="Conv_expand_one_line"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
name="minz_btn"
- tool_tip="Shows/hides message panel" />
+ tool_tip="Shows/hides message panel"
+ width="28" />
</layout_panel>
</layout_stack>
</layout_panel>
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 49d64767cc..853c209bca 100755
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -21,7 +21,11 @@
Sending Email
</string>
<string
- name="profile_progress_str">
+ name="facebook_progress_str">
+ Posting to Facebook
+ </string>
+ <string
+ name="profile_progress_str">
Posting
</string>
<string
@@ -33,7 +37,11 @@
Saving to Computer
</string>
<string
- name="profile_succeeded_str">
+ name="facebook_succeeded_str">
+ Image uploaded
+ </string>
+ <string
+ name="profile_succeeded_str">
Image uploaded
</string>
<string
@@ -49,7 +57,11 @@
Saved to Computer!
</string>
<string
- name="profile_failed_str">
+ name="facebook_failed_str">
+ Failed to upload image to your Facebook timeline.
+ </string>
+ <string
+ name="profile_failed_str">
Failed to upload image to your Profile Feed.
</string>
<string
diff --git a/indra/newview/skins/default/xui/en/floater_social.xml b/indra/newview/skins/default/xui/en/floater_social.xml
new file mode 100644
index 0000000000..39df777d1b
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_social.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="false"
+ help_topic="social floater"
+ layout="topleft"
+ name="floater_social"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="POST TO FACEBOOK"
+ height="482"
+ width="304">
+ <panel
+ height="482"
+ width="304"
+ visible="true"
+ name="background"
+ background_visible="true"
+ bg_opaque_color="DkGray"
+ bg_alpha_color="DkGray_66"
+ follows="all"
+ top="0"
+ left="0">
+ <tab_container
+ name="tabs"
+ tab_group="1"
+ tab_min_width="70"
+ tab_height="30"
+ tab_position="top"
+ background_visible="true"
+ bg_opaque_color="DkGray"
+ bg_alpha_color="DkGray_66"
+ top="7"
+ height="437"
+ halign="center">
+ <panel
+ filename="panel_social_status.xml"
+ class="llsocialstatuspanel"
+ follows="all"
+ label="STATUS"
+ help_topic="panel_social_status"
+ name="panel_social_status"/>
+ <panel
+ filename="panel_social_photo.xml"
+ class="llsocialphotopanel"
+ follows="all"
+ label="PHOTO"
+ help_topic="panel_social_photo"
+ name="panel_social_photo"/>
+ <panel
+ filename="panel_social_place.xml"
+ class="llsocialcheckinpanel"
+ follows="all"
+ label="CHECK IN"
+ help_topic="panel_social_place"
+ name="panel_social_place"/>
+ <panel
+ filename="panel_social_account.xml"
+ class="llsocialaccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ help_topic="panel_social_account"
+ name="panel_social_account"/>
+ </tab_container>
+ <panel
+ name="connection_status_panel"
+ follows="left|bottom|right"
+ background_visible="true"
+ bg_opaque_color="DkGray"
+ bg_alpha_color="DkGray_66"
+ height="24">
+ <text
+ name="connection_error_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left="9"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="DrYellow"
+ font="SansSerif">
+ Error
+ </text>
+ <loading_indicator
+ follows="left|bottom|right"
+ height="24"
+ width="24"
+ name="connection_loading_indicator"
+ top="2"
+ left="9"
+ visible="true"/>
+ <text
+ name="connection_loading_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left_pad="5"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="EmphasisColor"
+ font="SansSerif">
+ Loading...
+ </text>
+ </panel>
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml
index cea10adca8..a80440e844 100755
--- a/indra/newview/skins/default/xui/en/floater_web_content.xml
+++ b/indra/newview/skins/default/xui/en/floater_web_content.xml
@@ -125,10 +125,10 @@
<icon
name="media_secure_lock_flag"
height="16"
- follows="top|right"
+ follows="top|left"
image_name="Lock2"
layout="topleft"
- left_delta="620"
+ left_delta="2"
top_delta="2"
visible="false"
tool_tip="Secured Browsing"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index b01c3067ff..6da4b3d234 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -16,6 +16,14 @@
parameter="agent" />
</menu_item_call>
<menu_item_call
+ label="Post to Facebook..."
+ name="PostToFacebook">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="social"/>
+ </menu_item_call>
+ <menu_item_separator/>
+ <menu_item_call
label="Appearance..."
name="ChangeOutfit">
<menu_item_call.on_click
@@ -3021,6 +3029,13 @@
parameter="http://google.com"/>
</menu_item_call>
<menu_item_call
+ label="FB Connect Test"
+ name="FB Connect Test">
+ <menu_item_call.on_click
+ function="Advanced.WebContentTest"
+ parameter="https://cryptic-ridge-1632.herokuapp.com/"/>
+ </menu_item_call>
+ <menu_item_call
label="Dump SelectMgr"
name="Dump SelectMgr">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 2e0e8bf885..6da25ef210 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3709,6 +3709,17 @@ Leave Group?
</notification>
<notification
+ icon="alertmodal.tga"
+ name="OwnerCannotLeaveGroup"
+ type="alertmodal">
+ Unable to leave group. You cannot leave the group because you are the last owner of the group. Please assign another member to the owner role first.
+ <tag>group</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
icon="alert.tga"
name="ConfirmKick"
type="alert">
@@ -5981,6 +5992,13 @@ Please select at least one type of content to search (General, Moderate, or Adul
[MESSAGE]
</notification>
+ <notification
+ icon="notify.tga"
+ name="FacebookConnect"
+ type="notifytip">
+[MESSAGE]
+ </notification>
+
<notification
icon="notify.tga"
name="PaymentReceived"
@@ -6573,7 +6591,7 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; has given you th
icon="notify.tga"
name="JoinGroup"
persist="true"
- type="notify">
+ type="offer">
<tag>group</tag>
[MESSAGE]
<form name="form">
diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml
index 7ce2627be9..1b394a3dca 100755
--- a/indra/newview/skins/default/xui/en/panel_people.xml
+++ b/indra/newview/skins/default/xui/en/panel_people.xml
@@ -365,6 +365,23 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M
top="0"
width="307" />
</accordion_tab>
+ <accordion_tab
+ layout="topleft"
+ height="173"
+ name="tab_suggested_friends"
+ title="People you may want to friend">
+ <avatar_list
+ ignore_online_status="true"
+ allow_select="true"
+ follows="all"
+ height="173"
+ layout="topleft"
+ left="0"
+ name="suggested_friends"
+ show_permissions_granted="true"
+ top="0"
+ width="307" />
+ </accordion_tab>
</accordion>
<text
follows="all"
diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
index 198ccd6e2f..8f90521bb2 100755
--- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
+++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
@@ -315,7 +315,18 @@
<line_editor.commit_callback
function="MediaCtrl.CommitURL"/>
</line_editor>
- <layout_stack
+ <icon
+ name="media_secure_lock_flag"
+ height="16"
+ follows="top|left"
+ image_name="Lock2"
+ layout="topleft"
+ left_delta="2"
+ top_delta="2"
+ visible="false"
+ tool_tip="Secured Browsing"
+ width="16" />
+ <layout_stack
name="media_address_url_icons"
animate="false"
follows="top|right"
@@ -340,19 +351,6 @@
tool_tip="White List enabled"
width="16" />
</layout_panel>
- <layout_panel
- layout="topleft"
- width="16"
- mouse_opaque="false"
- auto_resize="false">
- <icon
- name="media_secure_lock_flag"
- height="16"
- image_name="Lock2"
- layout="topleft"
- tool_tip="Secured Browsing"
- width="16" />
- </layout_panel>
</layout_stack>
</layout_panel>
<layout_panel
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
index d2f29ade44..61c8c971c2 100755
--- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -16,11 +16,11 @@
imgoverlay_label_space="10"
label="Post to My Profile Feed"
layout="topleft"
- left="10"
+ left_delta="0"
name="save_to_profile_btn"
pad_left="10"
right="-10"
- top="5">
+ top_pad="10">
<button.commit_callback
function="Snapshot.SaveToProfile" />
</button>
diff --git a/indra/newview/skins/default/xui/en/panel_social_account.xml b/indra/newview/skins/default/xui/en/panel_social_account.xml
new file mode 100644
index 0000000000..882e410bdb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_account">
+ <string
+ name="facebook_connected"
+ value="You are connected to Facebook as:" />
+ <string
+ name="facebook_disconnected"
+ value="Not connected to Facebook" />
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_caption_label"
+ top="21"
+ type="string">
+ Not connected to Facebook.
+ </text>
+ <text
+ layout="topleft"
+ top_pad="2"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_name_label"
+ parse_urls="true"
+ type="string"/>
+ <panel
+ layout="topleft"
+ name="panel_buttons"
+ height="345"
+ left="9">
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_pad="9"
+ visible="true"
+ height="23"
+ label="Connect..."
+ name="connect_btn"
+ width="210">
+ <commit_callback function="SocialSharing.Connect"/>
+ </button>
+
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_delta="0"
+ height="23"
+ label="Disconnect"
+ name="disconnect_btn"
+ width="210"
+ visible="false">
+ <commit_callback function="SocialSharing.Disconnect"/>
+ </button>
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="0"
+ name="account_learn_more_label"
+ top_pad="20"
+ type="string">
+ [https://support.secondlife.com/ Learn about posting to Facebook]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_photo.xml b/indra/newview/skins/default/xui/en/panel_social_photo.xml
new file mode 100644
index 0000000000..a55613b52a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_photo.xml
@@ -0,0 +1,152 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="snapshot_panel"
+ height="367">
+ <combo_box
+ control_name="SocialPhotoResolution"
+ follows="left|top"
+ top="6"
+ left="9"
+ name="resolution_combobox"
+ tool_tip="Image resolution"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="640x480"
+ name="640x480"
+ value="[i640,i480]" />
+ <combo_box.item
+ label="800x600"
+ name="800x600"
+ value="[i800,i600]" />
+ <combo_box.item
+ label="1024x768"
+ name="1024x768"
+ value="[i1024,i768]" />
+ </combo_box>
+ <text
+ follows="left|top"
+ font="SansSerifSmall"
+ height="14"
+ left="208"
+ length="1"
+ halign="right"
+ name="file_size_label"
+ top="9"
+ type="string"
+ width="50">
+ [SIZE] KB
+ </text>
+ <panel
+ height="150"
+ width="250"
+ visible="true"
+ name="thumbnail_placeholder"
+ top="33"
+ follows="left|top"
+ left="9">
+ </panel>
+ <button
+ follows="left|top"
+ height="23"
+ label="Refresh"
+ left="9"
+ top_pad="5"
+ name="new_snapshot_btn"
+ tool_tip="Click to refresh"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.RefreshPhoto" />
+ </button>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ text_color="EmphasisColor"
+ height="14"
+ top_pad="-19"
+ left_pad="-20"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="caption_label"
+ top_pad="20"
+ type="string">
+ Comment (optional):
+ </text>
+ <text_editor
+ follows="left|top"
+ height="87"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_caption"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include location in posting"
+ name="add_location_cb"
+ left="9"
+ height="16"
+ top_pad="8"/>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_photo_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendPhoto" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_photo_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_place.xml b/indra/newview/skins/default/xui/en/panel_social_place.xml
new file mode 100644
index 0000000000..13e94f6998
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_place.xml
@@ -0,0 +1,132 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_place">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_place"
+ top="8">
+ <layout_panel
+ name="place_detail_panel"
+ height="181">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="place_caption_label"
+ top="13"
+ type="string">
+ Say something about where you are:
+ </text>
+ <text_editor
+ follows="top|left"
+ height="150"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="place_caption"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ </layout_panel>
+ <layout_panel
+ name="place_map_panel"
+ height="186">
+ <panel
+ follows="left|top"
+ height="128"
+ width="128"
+ background_visible="true"
+ bg_opaque_color="Black"
+ bg_alpha_color="Black"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_border">
+ </panel>
+ <loading_indicator
+ follows="left|top"
+ height="24"
+ width="24"
+ name="map_loading_indicator"
+ top="77"
+ left="61"
+ visible="true"/>
+ <icon
+ follows="left|top"
+ height="128"
+ width="128"
+ image_name="Map_Placeholder_Icon"
+ layout="topleft"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_placeholder">
+ </icon>
+ <icon
+ follows="left|top"
+ height="128"
+ width="128"
+ image_name="Map_Placeholder_Icon"
+ layout="topleft"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_default">
+ </icon>
+ <check_box
+ follows="left|top"
+ initial_value="false"
+ top_delta="8"
+ width="8"
+ label=""
+ name="add_place_view_cb"
+ left_pad="5"/>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ height="32"
+ width="130"
+ word_wrap="true"
+ left_pad="12"
+ top_delta="-8"
+ type="string">
+ Include overhead view of location
+ </text>
+ </layout_panel>
+ <layout_panel
+ name="place_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_place_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendCheckin" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_place_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_status.xml b/indra/newview/skins/default/xui/en/panel_social_status.xml
new file mode 100644
index 0000000000..54cfa3f524
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_status.xml
@@ -0,0 +1,67 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_status">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_status"
+ top="8">
+ <layout_panel
+ name="status_detail_panel"
+ height="367">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="status_caption_label"
+ top="13"
+ type="string">
+ What's on your mind?
+ </text>
+ <text_editor
+ follows="left|top"
+ height="150"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="status_message"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ </layout_panel>
+ <layout_panel
+ name="status_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_status_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendStatus" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_status_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index d964ccaf79..7f6701368a 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -148,6 +148,14 @@ Please try logging in again in a minute.</string>
<string name="SentToInvalidRegion">You were sent to an invalid region.</string>
<string name="TestingDisconnect">Testing viewer disconnect</string>
+ <!-- Facebook Connect and, eventually, other Social Network -->
+ <string name="SocialFacebookConnecting">Connecting to Facebook...</string>
+ <string name="SocialFacebookPosting">Posting...</string>
+ <string name="SocialFacebookDisconnecting">Disconnecting from Facebook...</string>
+ <string name="SocialFacebookErrorConnecting">Problem connecting to Facebook</string>
+ <string name="SocialFacebookErrorPosting">Problem posting to Facebook</string>
+ <string name="SocialFacebookErrorDisconnecting">Problem disconnecting from Facebook</string>
+
<!-- Tooltip -->
<string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar -->
<string name="TooltipNoName">(no name)</string> <!-- No name on an object -->
@@ -3423,6 +3431,9 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
Drag items from inventory here
</string>
+ <string name="facebook_post_success">
+ You posted to Facebook.
+ </string>
<string name="no_session_message">
(IM Session Doesn't Exist)
@@ -3855,6 +3866,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Profile_Label">Profile</string>
<string name="Command_Search_Label">Search</string>
<string name="Command_Snapshot_Label">Snapshot</string>
+ <string name="Command_Social_Label">Facebook</string>
<string name="Command_Speak_Label">Speak</string>
<string name="Command_View_Label">Camera controls</string>
<string name="Command_Voice_Label">Voice settings</string>
@@ -3882,6 +3894,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Profile_Tooltip">Edit or view your profile</string>
<string name="Command_Search_Tooltip">Find places, events, people</string>
<string name="Command_Snapshot_Tooltip">Take a picture</string>
+ <string name="Command_Social_Tooltip">Post to Facebook</string>
<string name="Command_Speak_Tooltip">Speak with people nearby using your microphone</string>
<string name="Command_View_Tooltip">Changing camera angle</string>
<string name="Command_Voice_Tooltip">Volume controls for calls and people near you in world</string>
diff --git a/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml
new file mode 100644
index 0000000000..af5aec2c34
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<person_tab_view
+ folder_arrow_image="Folder_Arrow"
+ folder_indentation="5"
+ item_height="24"
+ item_top_pad="3"
+ mouse_opaque="true"
+ follows="left|top|right"
+ text_pad="6"
+ text_pad_left="4"
+ text_pad_right="4"
+ arrow_size="10"
+ max_folder_item_overlap="2"/>
diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml
new file mode 100644
index 0000000000..46c1b7ff75
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<person_view
+ folder_arrow_image="Folder_Arrow"
+ folder_indentation="5"
+ item_height="24"
+ item_top_pad="3"
+ mouse_opaque="true"
+ follows="left|top|right"
+ icon_pad="4"
+ icon_width="20"
+ text_pad="6"
+ text_pad_left="4"
+ text_pad_right="4"
+ arrow_size="10"
+ max_folder_item_overlap="2">
+ <facebook_icon
+ follows="left"
+ height="14"
+ image_name="Facebook_Icon"
+ left="5"
+ bottom="6"
+ name="facebook_icon"
+ tool_tip="Facebook User"
+ visible="false"
+ width="14" />
+ <avatar_icon
+ follows="left"
+ layout="topleft"
+ height="20"
+ default_icon_name="Generic_Person"
+ left="5"
+ top="2"
+ visible="false"
+ width="20" />
+ <last_interaction_time_textbox
+ layout="topleft"
+ follows="right"
+ font="SansSerifSmall"
+ height="15"
+ left_pad="5"
+ right="-164"
+ name="last_interaction_time_textbox"
+ text_color="LtGray_50"
+ value="0s"
+ visible="false"
+ width="35" />
+ <permission_edit_theirs_icon
+ layout="topleft"
+ height="16"
+ follows="right"
+ image_name="Permission_Edit_Objects_Theirs"
+ left_pad="3"
+ right="-129"
+ name="permission_edit_theirs_icon"
+ tool_tip="You can edit this friend&apos;s objects"
+ top="4"
+ visible="false"
+ width="16" />
+ <permission_edit_mine_icon
+ layout="topleft"
+ height="16"
+ follows="right"
+ image_name="Permission_Edit_Objects_Mine"
+ left_pad="3"
+ right="-110"
+ name="permission_edit_mine_icon"
+ tool_tip="This friend can edit, delete or take your objects"
+ top="4"
+ visible="false"
+ width="16" />
+ <permission_map_icon
+ height="16"
+ follows="right"
+ image_name="Permission_Visible_Map"
+ left_pad="3"
+ tool_tip="This friend can locate you on the map"
+ right="-91"
+ name="permission_map_icon"
+ visible="false"
+ width="16" />
+ <permission_online_icon
+ height="16"
+ follows="right"
+ image_name="Permission_Visible_Online"
+ left_pad="3"
+ right="-72"
+ name="permission_online_icon"
+ tool_tip="This friend can see when you&apos;re online"
+ visible="false"
+ width="16" />
+ <info_btn
+ follows="right"
+ height="16"
+ image_pressed="Info_Press"
+ image_unselected="Info_Over"
+ left_pad="3"
+ right="-53"
+ name="info_btn"
+ tool_tip="More info"
+ tab_stop="false"
+ visible="false"
+ width="16" />
+ <profile_btn
+ layout="topleft"
+ follows="right"
+ height="20"
+ image_overlay="Web_Profile_Off"
+ left_pad="5"
+ right="-28"
+ name="profile_btn"
+ tab_stop="false"
+ tool_tip="View profile"
+ top="2"
+ visible="false"
+ width="20" />
+ <output_monitor
+ auto_update="true"
+ follows="right"
+ draw_border="false"
+ height="16"
+ right="-3"
+ mouse_opaque="true"
+ name="speaking_indicator"
+ visible="false"
+ width="20" />
+ </person_view>
+
diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp
index fd9527d631..8ce56326d8 100755
--- a/indra/newview/tests/lltranslate_test.cpp
+++ b/indra/newview/tests/lltranslate_test.cpp
@@ -308,8 +308,8 @@ void LLCurl::Responder::errorWithContent(U32, std::string const&, LLSD const&) {
void LLCurl::Responder::result(LLSD const&) {}
LLCurl::Responder::~Responder() {}
-void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32) {}
-void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32) {}
+void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32, bool) {}
+void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32, bool) {}
LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer)
: std::iostream(&mStreamBuf), mStreamBuf(channels, buffer) {}