diff options
Diffstat (limited to 'indra/newview')
93 files changed, 4698 insertions, 1359 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index acb9c57af8..5c9171f209 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -191,6 +191,7 @@ set(viewer_SOURCE_FILES llexpandabletextbox.cpp llexternaleditor.cpp llface.cpp + llfacebookconnect.cpp llfasttimerview.cpp llfavoritesbar.cpp llfeaturemanager.cpp @@ -273,6 +274,7 @@ set(viewer_SOURCE_FILES llfloatersettingsdebug.cpp llfloatersidepanelcontainer.cpp llfloatersnapshot.cpp + llfloatersocial.cpp llfloatersounddevices.cpp llfloaterspellchecksettings.cpp llfloatertelehub.cpp @@ -509,6 +511,7 @@ set(viewer_SOURCE_FILES llsidetraypanelcontainer.cpp llsky.cpp llslurl.cpp + llsnapshotlivepreview.cpp llspatialpartition.cpp llspeakers.cpp llspeakingindicatormanager.cpp @@ -777,6 +780,7 @@ set(viewer_HEADER_FILES llexpandabletextbox.h llexternaleditor.h llface.h + llfacebookconnect.h llfasttimerview.h llfavoritesbar.h llfeaturemanager.h @@ -859,6 +863,7 @@ set(viewer_HEADER_FILES llfloatersettingsdebug.h llfloatersidepanelcontainer.h llfloatersnapshot.h + llfloatersocial.h llfloatersounddevices.h llfloaterspellchecksettings.h llfloatertelehub.h @@ -1084,6 +1089,7 @@ set(viewer_HEADER_FILES llsidetraypanelcontainer.h llsky.h llslurl.h + llsnapshotlivepreview.h llspatialpartition.h llspeakers.h llspeakingindicatormanager.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index cff2619cfb..c47e8b5872 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.6.9 +3.6.10 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 44bf682908..8973cda667 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4941,6 +4941,16 @@ <key>Value</key> <array /> </map> + <key>LeapPlaybackEventsCommand</key> + <map> + <key>Comment</key> + <string>Command line to use leap to launch playback of event recordings</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>LLSD</string> + <key>Value</key> + </map> <key>LSLFindCaseInsensitivity</key> <map> <key>Comment</key> @@ -10444,6 +10454,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>ShowEventRecorderMenuItems</key> + <map> + <key>Comment</key> + <string>Whether or not Event Recorder menu choices - Start / Stop event recording should appear in the (currently) Develop menu</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>ShowGestureButton</key> <map> <key>Comment</key> @@ -13095,6 +13116,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/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 f67142d1ed..539d186441 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -223,6 +223,10 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" + +#include "llviewereventrecorder.h" + + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -696,6 +700,7 @@ LLAppViewer::LLAppViewer() : LLAppViewer::~LLAppViewer() { delete mSettingsLocationList; + LLViewerEventRecorder::instance().~LLViewerEventRecorder(); LLLoginInstance::instance().setUpdaterService(0); @@ -2250,13 +2255,13 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, BOOST_FOREACH(const SettingsFile& file, group.files) { - llinfos << "Attempting to load settings for the group " << file.name() - << " - from location " << location_key << llendl; + LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name() + << " - from location " << location_key << LL_ENDL; LLControlGroup* settings_group = LLControlGroup::getInstance(file.name); if(!settings_group) { - llwarns << "No matching settings group for name " << file.name() << llendl; + LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL; continue; } @@ -2285,7 +2290,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, if(settings_group->loadFromFile(full_settings_path, set_defaults, file.persistent)) { // success! - llinfos << "Loaded settings file " << full_settings_path << llendl; + LL_INFOS("Settings") << "Loaded settings file " << full_settings_path << LL_ENDL; } else { // failed to load @@ -2299,7 +2304,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, // only complain if we actually have a filename at this point if (!full_settings_path.empty()) { - llinfos << "Cannot load " << full_settings_path << " - No settings found." << llendl; + LL_INFOS("Settings") << "Cannot load " << full_settings_path << " - No settings found." << LL_ENDL; } } } @@ -2395,8 +2400,6 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setString("ClientSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); - gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel()); - #ifndef LL_RELEASE_FOR_DOWNLOAD // provide developer build only overrides for these control variables that are not // persisted to settings.xml @@ -2460,8 +2463,8 @@ bool LLAppViewer::initConfiguration() gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, clp.getOption("settings")[0]); gSavedSettings.setString("ClientSettingsFile", user_settings_filename); - llinfos << "Using command line specified settings filename: " - << user_settings_filename << llendl; + LL_INFOS("Settings") << "Using command line specified settings filename: " + << user_settings_filename << LL_ENDL; } // - load overrides from user_settings @@ -2477,8 +2480,8 @@ bool LLAppViewer::initConfiguration() { std::string session_settings_filename = clp.getOption("sessionsettings")[0]; gSavedSettings.setString("SessionSettingsFile", session_settings_filename); - llinfos << "Using session settings filename: " - << session_settings_filename << llendl; + LL_INFOS("Settings") << "Using session settings filename: " + << session_settings_filename << LL_ENDL; } loadSettingsFromDirectory("Session"); @@ -2486,8 +2489,8 @@ bool LLAppViewer::initConfiguration() { std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0]; gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename); - llinfos << "Using user session settings filename: " - << user_session_settings_filename << llendl; + LL_INFOS("Settings") << "Using user session settings filename: " + << user_session_settings_filename << LL_ENDL; } loadSettingsFromDirectory("UserSession"); @@ -2575,9 +2578,13 @@ bool LLAppViewer::initConfiguration() } } + if (clp.hasOption("logevents")) { + LLViewerEventRecorder::instance().setEventLoggingOn(); + } + std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); if(! CmdLineChannel.empty()) - { + { LLVersionInfo::resetChannel(CmdLineChannel); } @@ -2589,16 +2596,16 @@ bool LLAppViewer::initConfiguration() LLFastTimer::sLog = TRUE; LLFastTimer::sLogName = std::string("performance"); } - + std::string test_name(gSavedSettings.getString("LogMetrics")); if (! test_name.empty()) - { - LLFastTimer::sMetricLog = TRUE ; + { + LLFastTimer::sMetricLog = TRUE ; // '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test // In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...) llinfos << "'--logmetrics' argument : " << test_name << llendl; - LLFastTimer::sLogName = test_name; - } + LLFastTimer::sLogName = test_name; + } if (clp.hasOption("graphicslevel")) { @@ -2607,14 +2614,14 @@ bool LLAppViewer::initConfiguration() // that value for validity. U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance"); if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel)) - { + { // graphicslevel is valid: save it and engage it later. Capture // the requested value separately from the settings variable // because, if this is the first run, LLViewerWindow's constructor // will call LLFeatureManager::applyRecommendedSettings(), which // overwrites this settings variable! mForceGraphicsLevel = graphicslevel; - } + } } LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance"); @@ -2645,16 +2652,32 @@ 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 start_slurl; std::string CmdLineLoginLocation(gSavedSettings.getString("CmdLineLoginLocation")); if(! CmdLineLoginLocation.empty()) - { - LLSLURL start_slurl(CmdLineLoginLocation); + { + start_slurl = CmdLineLoginLocation; LLStartUp::setStartSLURL(start_slurl); if(start_slurl.getType() == LLSLURL::LOCATION) { LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid()); + } + } + + //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 (start_slurl.isValid() && + (gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) + { + if (sendURLToOtherInstance(start_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()) @@ -2795,7 +2818,7 @@ bool LLAppViewer::initConfiguration() LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL; LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); } - else if ((clp.hasOption("login") || clp.hasOption("autologin")) + else if ( ( clp.hasOption("login") || clp.hasOption("autologin")) && gSavedSettings.getString("CmdLineLoginLocation").empty()) { // If automatic login from command line with --login switch @@ -3179,7 +3202,7 @@ bool LLAppViewer::initWindow() LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false); gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel); } - + // Set this flag in case we crash while initializing GL gSavedSettings.setBOOL("RenderInitError", TRUE); gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 3cf3c739d9..80a80f4298 100755 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -172,21 +172,20 @@ void ll_nvapi_init(NvDRSSessionHandle hSession) nvapi_error(status); return; } + + // (5) Now we apply (or save) our changes to the system + status = NvAPI_DRS_SaveSettings(hSession); + if (status != NVAPI_OK) + { + nvapi_error(status); + return; + } } else if (status != NVAPI_OK) { nvapi_error(status); return; } - - - - // (5) Now we apply (or save) our changes to the system - status = NvAPI_DRS_SaveSettings(hSession); - if (status != NVAPI_OK) - { - nvapi_error(status); - } } //#define DEBUGGING_SEH_FILTER 1 diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index b513a52ff7..70cc48f12b 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -73,6 +73,8 @@ #include "llcallingcard.h" #include "llslurl.h" // IDEVO #include "llsidepanelinventory.h" +#include "llavatarname.h" +#include "llagentui.h" // static void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name) @@ -391,6 +393,72 @@ void LLAvatarActions::pay(const LLUUID& id) } } +void LLAvatarActions::teleport_request_callback(const LLSD& notification, const LLSD& response) +{ + S32 option; + if (response.isInteger()) + { + option = response.asInteger(); + } + else + { + option = LLNotificationsUtil::getSelectedOption(notification, response); + } + + if (0 == option) + { + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_ImprovedInstantMessage); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + + msg->nextBlockFast(_PREHASH_MessageBlock); + msg->addBOOLFast(_PREHASH_FromGroup, FALSE); + msg->addUUIDFast(_PREHASH_ToAgentID, notification["substitutions"]["uuid"] ); + msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); + msg->addU8Fast(_PREHASH_Dialog, IM_TELEPORT_REQUEST); + msg->addUUIDFast(_PREHASH_ID, LLUUID::null); + msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary + + std::string name; + LLAgentUI::buildFullname(name); + + msg->addStringFast(_PREHASH_FromAgentName, name); + msg->addStringFast(_PREHASH_Message, response["message"]); + msg->addU32Fast(_PREHASH_ParentEstateID, 0); + msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); + msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); + + gMessageSystem->addBinaryDataFast( + _PREHASH_BinaryBucket, + EMPTY_BINARY_BUCKET, + EMPTY_BINARY_BUCKET_SIZE); + + gAgent.sendReliableMessage(); + } +} + +// static +void LLAvatarActions::teleportRequest(const LLUUID& id) +{ + LLSD notification; + notification["uuid"] = id; + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) + { + // unlikely ... they just picked this name from somewhere... + LLAvatarNameCache::get(id, boost::bind(&LLAvatarActions::teleportRequest, id)); + return; // reinvoke this when the name resolves + } + notification["NAME"] = av_name.getCompleteName(); + + LLSD payload; + + LLNotificationsUtil::add("TeleportRequestPrompt", notification, payload, teleport_request_callback); +} + // static void LLAvatarActions::kick(const LLUUID& id) { diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 6e1198cd09..403414558e 100755 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -111,6 +111,12 @@ public: static void pay(const LLUUID& id); /** + * Request teleport from other avatar + */ + static void teleportRequest(const LLUUID& id); + static void teleport_request_callback(const LLSD& notification, const LLSD& response); + + /** * Share items with the avatar. */ static void share(const LLUUID& id); diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 88884042d4..131aea9da3 100755 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -220,18 +220,25 @@ void LLNotificationChiclet::setCounter(S32 counter) bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification ) { - if (notification->getName() == "ScriptDialog") + bool displayNotification; + if ( (notification->getName() == "ScriptDialog") // special case for scripts + // if there is no toast window for the notification, filter it + || (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID())) + ) { - return false; + displayNotification = false; } - - if( !(notification->canLogToIM() && notification->hasFormElements()) - && (!notification->getPayload().has("give_inventory_notification") - || notification->getPayload()["give_inventory_notification"])) + else if( !(notification->canLogToIM() && notification->hasFormElements()) + && (!notification->getPayload().has("give_inventory_notification") + || notification->getPayload()["give_inventory_notification"])) { - return true; + displayNotification = true; } - return false; + else + { + displayNotification = false; + } + return displayNotification; } ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp index 5ab108b39f..44212298cf 100755 --- a/indra/newview/llconversationloglist.cpp +++ b/indra/newview/llconversationloglist.cpp @@ -313,6 +313,10 @@ void LLConversationLogList::onCustomAction(const LLSD& userdata) { LLAvatarActions::offerTeleport(selected_conversation_participant_id); } + else if ("request_teleport" == command_name) + { + LLAvatarActions::teleportRequest(selected_conversation_participant_id); + } else if("add_friend" == command_name) { if (!LLAvatarActions::isFriend(selected_conversation_participant_id)) diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 192a594c9d..affa24f78c 100755 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -132,6 +132,7 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 items.push_back(std::string("view_profile")); items.push_back(std::string("im")); items.push_back(std::string("offer_teleport")); + items.push_back(std::string("request_teleport")); items.push_back(std::string("voice_call")); items.push_back(std::string("chat_history")); items.push_back(std::string("separator_chat_history")); diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index c3b53d5e4a..2f7f98ec7b 100755 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -261,7 +261,7 @@ namespace } // was llinfos but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG - lldebugs << "LLEventPollResponder::completed <" << mCount << "> " << events.size() << "events (id " + lldebugs << "LLEventPollResponder::httpSuccess <" << mCount << "> " << events.size() << "events (id " << LLSDXMLStreamer(mAcknowledge) << ")" << llendl; LLSD::array_const_iterator i = events.beginArray(); diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp new file mode 100644 index 0000000000..ffcf3c076a --- /dev/null +++ b/indra/newview/llfacebookconnect.cpp @@ -0,0 +1,615 @@ +/** + * @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 "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")); + +// 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 httpSuccess() + { + LL_DEBUGS("FacebookConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); + } + + /* virtual */ void httpFailure() + { + if ( HTTP_FOUND == getStatus() ) + { + const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() + << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; + } + else + { + LLFacebookConnect::instance().openFacebookWeb(location); + } + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED); + const LLSD& content = getContent(); + log_facebook_connect_error("Connect", getStatus(), getReason(), + content.get("error_code"), content.get("error_description")); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookShareResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookShareResponder); +public: + + LLFacebookShareResponder() + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTING); + } + + /* virtual */ void httpSuccess() + { + toast_user_for_success(); + LL_DEBUGS("FacebookConnect") << "Post successful. " << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED); + } + + /* virtual */ void httpFailure() + { + if ( HTTP_FOUND == getStatus() ) + { + const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() + << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; + } + else + { + LLFacebookConnect::instance().openFacebookWeb(location); + } + } + else if ( HTTP_NOT_FOUND == getStatus() ) + { + LLFacebookConnect::instance().connectToFacebook(); + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POST_FAILED); + const LLSD& content = getContent(); + log_facebook_connect_error("Share", getStatus(), getReason(), + content.get("error_code"), content.get("error_description")); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookDisconnectResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookDisconnectResponder); +public: + + LLFacebookDisconnectResponder() + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECTING); + } + + void setUserDisconnected() + { + // Clear data + LLFacebookConnect::instance().clearInfo(); + LLFacebookConnect::instance().clearContent(); + //Notify state change + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); + } + + /* virtual */ void httpSuccess() + { + LL_DEBUGS("FacebookConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL; + setUserDisconnected(); + } + + /* virtual */ void httpFailure() + { + //User not found so already disconnected + if ( HTTP_NOT_FOUND == getStatus() ) + { + LL_DEBUGS("FacebookConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL; + setUserDisconnected(); + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); + const LLSD& content = getContent(); + log_facebook_connect_error("Disconnect", getStatus(), getReason(), + 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 httpSuccess() + { + LL_DEBUGS("FacebookConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); + } + + /* virtual */ void httpFailure() + { + // show the facebook login page if not connected yet + if ( HTTP_NOT_FOUND == getStatus() ) + { + LL_DEBUGS("FacebookConnect") << "Not connected. " << dumpResponse() << LL_ENDL; + if (mAutoConnect) + { + LLFacebookConnect::instance().connectToFacebook(); + } + else + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); + } + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); + const LLSD& content = getContent(); + log_facebook_connect_error("Connected", getStatus(), getReason(), + content.get("error_code"), content.get("error_description")); + } + } + +private: + bool mAutoConnect; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookInfoResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookInfoResponder); +public: + + /* virtual */ void httpSuccess() + { + LL_INFOS("FacebookConnect") << "Facebook: Info received" << LL_ENDL; + LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. " << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().storeInfo(getContent()); + } + + /* virtual */ void httpFailure() + { + if ( HTTP_FOUND == getStatus() ) + { + const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() + << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; + } + else + { + LLFacebookConnect::instance().openFacebookWeb(location); + } + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + const LLSD& content = getContent(); + log_facebook_connect_error("Info", getStatus(), getReason(), + content.get("error_code"), content.get("error_description")); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookFriendsResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookFriendsResponder); +public: + + /* virtual */ void httpSuccess() + { + LL_DEBUGS("FacebookConnect") << "Getting Facebook friends successful. " << dumpResponse() << LL_ENDL; + LLFacebookConnect::instance().storeContent(getContent()); + } + + /* virtual */ void httpFailure() + { + if ( HTTP_FOUND == getStatus() ) + { + const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() + << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; + } + else + { + LLFacebookConnect::instance().openFacebookWeb(location); + } + } + else + { + LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; + const LLSD& content = getContent(); + log_facebook_connect_error("Friends", getStatus(), getReason(), + content.get("error_code"), content.get("error_description")); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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) +{ + std::string url = gAgent.getRegion()->getCapability("FacebookConnect"); + url += 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 2c3b34e128..90b4490e1d 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -662,10 +662,10 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLFloater* session_floater = widget->getSessionFloater(); if (session_floater != nearby_chat) { - widget->setVisibleIfDetached(visible); - } + widget->setVisibleIfDetached(visible); } } + } // Now, do the normal multifloater show/hide LLMultiFloater::setVisible(visible); @@ -700,13 +700,13 @@ void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& k // Only select other sessions if (!getSelectedSession().isNull()) { - selectConversationPair(getSelectedSession(), false, take_focus); + selectConversationPair(getSelectedSession(), false, take_focus); } if (mInitialized && mIsFirstLaunch) { collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); mIsFirstLaunch = false; - } +} } void LLFloaterIMContainer::updateResizeLimits() @@ -834,7 +834,7 @@ void LLFloaterIMContainer::assignResizeLimits() S32 conv_pane_target_width = is_conv_pane_expanded ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() ) - : mConversationsPane->getMinDim(); + : 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; @@ -995,7 +995,7 @@ void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order) conversation_floater->setSortOrder(order); } } - + gSavedSettings.setU32("ConversationSortOrder", (U32)order); } @@ -1080,6 +1080,10 @@ void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec { LLAvatarActions::offerTeleport(selectedIDS); } + else if ("request_teleport" == command) + { + LLAvatarActions::teleportRequest(selectedIDS.front()); + } else if ("voice_call" == command) { LLAvatarActions::startCall(userID); @@ -1182,7 +1186,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()); } @@ -1204,7 +1208,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, { LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); } - } +} } } @@ -1235,7 +1239,7 @@ void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata) if (action == "group_profile") { - LLGroupActions::show(mSelectedSession); + LLGroupActions::show(mSelectedSession); } else if (action == "activate_group") { diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 6ef4d8717d..8f20d4e082 100755 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2097,7 +2097,6 @@ void LLPanelLandOptions::refresh() // virtual void LLPanelLandOptions::draw() { - refreshSearch(); // Is this necessary? JC LLPanel::draw(); } @@ -2111,9 +2110,8 @@ void LLPanelLandOptions::refreshSearch() mCheckShowDirectory->set(FALSE); mCheckShowDirectory->setEnabled(FALSE); - // *TODO:Translate - const std::string& none_string = LLParcel::getCategoryUIString(LLParcel::C_NONE); - mCategoryCombo->setSimple(none_string); + const std::string& none_string = LLParcel::getCategoryString(LLParcel::C_NONE); + mCategoryCombo->setValue(none_string); mCategoryCombo->setEnabled(FALSE); return; } @@ -2140,10 +2138,9 @@ void LLPanelLandOptions::refreshSearch() mCheckShowDirectory ->set(show_directory); // Set by string in case the order in UI doesn't match the order by index. - // *TODO:Translate LLParcel::ECategory cat = parcel->getCategory(); - const std::string& category_string = LLParcel::getCategoryUIString(cat); - mCategoryCombo->setSimple(category_string); + const std::string& category_string = LLParcel::getCategoryString(cat); + mCategoryCombo->setValue(category_string); std::string tooltip; bool enable_show_directory = false; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 1969435ba1..87ae36716d 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -618,9 +618,12 @@ void LLFloaterPreference::cancel() // hide translation settings floater LLFloaterReg::hideInstance("prefs_translation"); - // hide translation settings floater + // hide autoreplace settings floater LLFloaterReg::hideInstance("prefs_autoreplace"); + // hide spellchecker settings folder + LLFloaterReg::hideInstance("prefs_spellchecker"); + // cancel hardware menu LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); if (hardware_settings) diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index ddfae005bb..eb0a201e69 100755 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -1734,7 +1734,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data) LLSD args; args["NUM_ADDED"] = llformat("%d",ids.size()); args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS); - args["LIST_TYPE"] = "Allowed Residents"; + args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeAllowedAgents"); args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS); LLNotificationsUtil::add("MaxAgentOnRegionBatch", args); delete change_info; @@ -1750,7 +1750,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data) LLSD args; args["NUM_ADDED"] = llformat("%d",ids.size()); args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS); - args["LIST_TYPE"] = "Banned Residents"; + args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeBannedAgents"); args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS); LLNotificationsUtil::add("MaxAgentOnRegionBatch", args); delete change_info; @@ -2816,9 +2816,10 @@ bool LLDispatchSetEstateAccess::operator()( } - std::string msg = llformat("Banned residents: (%d, max %d)", - totalBannedAgents, - ESTATE_MAX_ACCESS_IDS); + LLStringUtil::format_map_t args; + args["[BANNEDAGENTS]"] = llformat("%d", totalBannedAgents); + args["[MAXBANNED]"] = llformat("%d", ESTATE_MAX_ACCESS_IDS); + std::string msg = LLTrans::getString("RegionInfoBannedResidents", args); panel->getChild<LLUICtrl>("ban_resident_label")->setValue(LLSD(msg)); if (banned_agent_name_list) @@ -2838,9 +2839,10 @@ bool LLDispatchSetEstateAccess::operator()( if (access_flags & ESTATE_ACCESS_MANAGERS) { - std::string msg = llformat("Estate Managers: (%d, max %d)", - num_estate_managers, - ESTATE_MAX_MANAGERS); + LLStringUtil::format_map_t args; + args["[ESTATEMANAGERS]"] = llformat("%d", num_estate_managers); + args["[MAXMANAGERS]"] = llformat("%d", ESTATE_MAX_MANAGERS); + std::string msg = LLTrans::getString("RegionInfoEstateManagers", args); panel->getChild<LLUICtrl>("estate_manager_label")->setValue(LLSD(msg)); LLNameListCtrl* estate_manager_name_list = 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..2a74c8e3ea --- /dev/null +++ b/indra/newview/llfloatersocial.cpp @@ -0,0 +1,920 @@ +/** +* @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"; +const std::string DEFAULT_CHECKIN_QUERY_PARAMETERS = "?sourceid=slshare_checkin&utm_source=facebook&utm_medium=checkin&utm_campaign=slshare"; +const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=facebook&utm_medium=photo&utm_campaign=slshare"; + +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) + { + // Get the SLURL for the location + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + std::string slurl_string = slurl.getSLURLString(); + + // Add query parameters so Google Analytics can track incoming clicks! + slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS; + + // Add it to the caption (pretty crude, but we don't have a better option with photos) + if (caption.empty()) + caption = slurl_string; + else + caption = caption + " " + slurl_string; + } + + // 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; + } + + // Add query parameters so Google Analytics can track incoming clicks! + slurl_string += DEFAULT_CHECKIN_QUERY_PARAMETERS; + + // 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 21b171446f..3e3ac7eb90 100755 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -30,6 +30,7 @@ #include "lliconctrl.h" #include "llfloaterreg.h" #include "llhttpconstants.h" +#include "llfacebookconnect.h" #include "lllayoutstack.h" #include "llpluginclassmedia.h" #include "llprogressbar.h" @@ -47,7 +48,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) @@ -66,7 +68,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 )); @@ -98,7 +104,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(); @@ -244,6 +250,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); @@ -288,6 +295,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(); } @@ -296,8 +313,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(); } @@ -343,19 +363,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) { @@ -398,15 +405,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() @@ -450,6 +482,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, HTTP_CONTENT_TEXT_HTML); @@ -461,6 +494,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 86b5a5e00b..f22940cd07 100755 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -55,6 +55,7 @@ public: id; Optional<bool> show_chrome, allow_address_entry, + allow_back_forward_navigation, trusted_content, show_page_title; Optional<LLRect> preferred_media_size; @@ -106,9 +107,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/llimview.cpp b/indra/newview/llimview.cpp index 4bf4f6edfc..9ba0a184a5 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -130,10 +130,10 @@ 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) - } + } notify_of_message(data, true); -} + } @@ -155,22 +155,22 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, void notify_of_message(const LLSD& msg, bool is_dnd_msg) { - std::string user_preferences; + 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); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); - // do not show notification which goes from agent - if (gAgent.getID() == participant_id) - { - return; - } + // 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; + // determine state of conversations floater + enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; - LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id); bool store_dnd_message = false; // flag storage of a dnd message bool is_session_focused = session_floater->isTornOff() && session_floater->hasFocus(); @@ -179,23 +179,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) 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()) - { + // determine user prefs for this session + if (session_id.isNull()) + { if (msg["source_type"].asInteger() == CHAT_SOURCE_OBJECT) { user_preferences = gSavedSettings.getString("NotificationObjectIMOptions"); @@ -206,50 +206,50 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); + user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNearbyChatIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } + } } } - else if(session->isP2PSessionType()) - { - if (LLAvatarTracker::instance().isBuddy(participant_id)) - { - user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); + else if(session->isP2PSessionType()) + { + if (LLAvatarTracker::instance().isBuddy(participant_id)) + { + user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundFriendIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); } - } - else - { - user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); + } + else + { + user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNonFriendIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } - } + } + } } - else if(session->isAdHocSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); + else if(session->isAdHocSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundConferenceIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } + } } - else if(session->isGroupSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); + else if(session->isGroupSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundGroupChatIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); } - } + } - // actions: + // actions: // 0. nothing - exit if (("noaction" == user_preferences || @@ -287,23 +287,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } } } - else - { + 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) + // 2. Flash line item + if ("openconversations" == user_preferences + || ON_TOP == conversations_floater_status + || ("toast" == user_preferences && ON_TOP != conversations_floater_status) || ("flash" == user_preferences && (CLOSED == conversations_floater_status || NOT_ON_TOP == conversations_floater_status)) || is_dnd_msg) - { - if(!LLMuteList::getInstance()->isMuted(participant_id)) - { + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { if(gAgent.isDoNotDisturb()) { store_dnd_message = true; @@ -318,43 +318,43 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - im_box->flashConversationItemWidget(session_id, true); - } - } + im_box->flashConversationItemWidget(session_id, true); + } + } } } - // 3. Flash FUI button - if (("toast" == user_preferences || "flash" == user_preferences) && - (CLOSED == conversations_floater_status + // 3. Flash FUI button + if (("toast" == user_preferences || "flash" == user_preferences) && + (CLOSED == conversations_floater_status || NOT_ON_TOP == conversations_floater_status) && !is_session_focused && !is_dnd_msg) //prevent flashing FUI button because the conversation floater will have already opened { if(!LLMuteList::getInstance()->isMuted(participant_id)) - { + { if(!gAgent.isDoNotDisturb()) - { + { gToolBarView->flashCommand(LLCommandId("chat"), true, im_box->isMinimized()); - } + } else { store_dnd_message = true; } - } + } } - // 4. Toast - if ((("toast" == user_preferences) && + // 4. Toast + if ((("toast" == user_preferences) && (ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status) && (!session_floater->isTornOff() || !LLFloater::isVisible(session_floater))) - || !session_floater->isMessagePaneExpanded()) + || !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()) - { + { + //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()) @@ -363,10 +363,10 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } - } + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } +} } if (store_dnd_message) { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index c9fe2127fe..3877f9f040 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4627,6 +4627,16 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act LLAvatarActions::offerTeleport(item->getCreatorUUID()); } } + else if ("request_lure" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + LLAvatarActions::teleportRequest(item->getCreatorUUID()); + } + } + else LLItemBridge::performAction(model, action); } @@ -4713,6 +4723,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Send Instant Message Separator")); items.push_back(std::string("Send Instant Message")); items.push_back(std::string("Offer Teleport...")); + items.push_back(std::string("Request Teleport...")); items.push_back(std::string("Conference Chat")); if (!good_card) @@ -4722,6 +4733,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if (!good_card || !user_online) { disabled_items.push_back(std::string("Offer Teleport...")); + disabled_items.push_back(std::string("Request Teleport...")); disabled_items.push_back(std::string("Conference Chat")); } } diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index dea90b9042..08b5eaedbb 100755 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -343,8 +343,11 @@ void LLNetMap::draw() // Draw avatars for (U32 i = 0; i < avatar_ids.size(); i++) { - pos_map = globalPosToView(positions[i]); LLUUID uuid = avatar_ids[i]; + // Skip self, we'll draw it later + if (uuid == gAgent.getID()) continue; + + pos_map = globalPosToView(positions[i]); bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL); diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 08c98e4f28..a3b15931c6 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; @@ -87,7 +90,7 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) { LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); } - else + else if (notification->canShowToast()) { LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index d7c634d619..f551fc96ee 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"; + extern S32 gMaxAgentGroups; /** Comparator for comparing avatar items by last interaction date */ @@ -495,6 +505,7 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), + mTryToConnectToFbc(true), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), @@ -573,6 +584,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)); @@ -583,8 +595,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"); @@ -617,6 +632,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); @@ -695,7 +711,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()) { @@ -762,9 +778,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) @@ -788,6 +835,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(); @@ -993,23 +1085,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); @@ -1229,7 +1323,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } -} + } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) { @@ -1387,6 +1481,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"); @@ -1450,4 +1545,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..6979ae06e0 100755 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -47,6 +47,7 @@ namespace LLPanelPeopleMenus PeopleContextMenu gPeopleContextMenu; NearbyPeopleContextMenu gNearbyPeopleContextMenu; +SuggestedFriendsContextMenu gSuggestedFriendsContextMenu; //== PeopleContextMenu =============================================================== @@ -74,6 +75,7 @@ LLContextMenu* PeopleContextMenu::createMenu() registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id)); + registrar.add("Avatar.TeleportRequest", boost::bind(&PeopleContextMenu::requestTeleport, this)); registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id)); enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2)); @@ -125,6 +127,7 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags) items.push_back(std::string("view_profile")); items.push_back(std::string("im")); items.push_back(std::string("offer_teleport")); + items.push_back(std::string("request_teleport")); items.push_back(std::string("voice_call")); items.push_back(std::string("chat_history")); items.push_back(std::string("separator_chat_history")); @@ -255,6 +258,13 @@ bool PeopleContextMenu::checkContextMenuItem(const LLSD& userdata) return false; } +void PeopleContextMenu::requestTeleport() +{ + // boost::bind cannot recognize overloaded method LLAvatarActions::teleportRequest(), + // so we have to use a wrapper. + LLAvatarActions::teleportRequest(mUUIDs.front()); +} + void PeopleContextMenu::offerTeleport() { // boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(), @@ -284,6 +294,7 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags) items.push_back(std::string("view_profile")); items.push_back(std::string("im")); items.push_back(std::string("offer_teleport")); + items.push_back(std::string("request_teleport")); items.push_back(std::string("voice_call")); items.push_back(std::string("chat_history")); items.push_back(std::string("separator_chat_history")); @@ -301,4 +312,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..945382ebc5 100755 --- a/indra/newview/llpanelpeoplemenus.h +++ b/indra/newview/llpanelpeoplemenus.h @@ -47,6 +47,7 @@ private: bool enableContextMenuItem(const LLSD& userdata); bool checkContextMenuItem(const LLSD& userdata); void offerTeleport(); + void requestTeleport(); }; /** @@ -58,8 +59,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..ee6893907e 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,8 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) adjustParticipant(avatar_id); } +static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents"); + void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) { LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 928d26646b..c4d5450e2b 100755 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -207,6 +207,7 @@ LLSD _basic_constraints_ext(X509* cert) } } + BASIC_CONSTRAINTS_free( bs ); } return result; } @@ -268,6 +269,8 @@ LLSD _ext_key_usage_ext(X509* cert) ASN1_OBJECT_free(usage); } } + + EXTENDED_KEY_USAGE_free( eku ); } return result; } @@ -280,6 +283,8 @@ LLSD _subject_key_identifier_ext(X509 *cert) if(skeyid) { result = cert_string_from_octet_string(skeyid); + + ASN1_OCTET_STRING_free( skeyid ); } return result; } @@ -300,6 +305,9 @@ LLSD _authority_key_identifier_ext(X509* cert) { result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial); } + + + AUTHORITY_KEYID_free( akeyid ); } // we ignore the issuer name in the authority key identifier, we check the issue name via @@ -1049,6 +1057,8 @@ void LLBasicCertificateStore::validate(int validation_policy, throw LLInvalidCertificate((*current_cert)); } std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); + X509_free( cert_x509 ); + cert_x509 = NULL; t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash); if(cache_entry != mTrustedCertCache.end()) { 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/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 1a3add2bfb..626d69aca4 100755 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -118,6 +118,11 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id) } } + LLPanel * LLSysWellWindow::findItemByID(const LLUUID& id) +{ + return mMessageList->getItemByValue(id); +} + //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- void LLSysWellWindow::initChannel() diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index cc5c057d8b..71b41476f5 100755 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -55,6 +55,7 @@ public: // Operating with items void removeItemByID(const LLUUID& id); + LLPanel * findItemByID(const LLUUID& id); // Operating with outfit virtual void setVisible(BOOL visible); diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 09ab31df36..bdbd8f1f83 100755 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -93,7 +93,7 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif if(!mIsGroupMsg) { - mAvatarName->setValue(p.from); + mAvatarName->setValue(p.from); } mTime->setValue(p.time); mSessionID = p.session_id; @@ -164,7 +164,7 @@ void LLToastIMPanel::spawnNameToolTip() params.background_visible(false); if(!mIsGroupMsg) { - params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); } else { 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/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 2fbb3bf868..aa9de076b8 100755 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -687,7 +687,10 @@ BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL { // it is sufficient to set this value once per call to handlekey // without clearing it, as it is only used in the subsequent call to scanKey - mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); + mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); + // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) + // NOT whether some UI shortcut wishes to handle the keypress + } return mKeyHandledByUI[translated_key]; } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 2cec808f19..139e3c3a2d 100755 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1994,7 +1994,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 ); @@ -2561,7 +2566,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; @@ -2576,7 +2586,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 dc8a27c5c8..2b1c106932 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -40,11 +40,13 @@ #include "llinventorypanel.h" #include "llnotifications.h" #include "llnotificationsutil.h" +#include "llviewereventrecorder.h" // newview includes #include "llagent.h" #include "llagentaccess.h" #include "llagentcamera.h" +#include "llagentui.h" #include "llagentwearables.h" #include "llagentpilot.h" #include "llcompilequeue.h" @@ -52,6 +54,7 @@ #include "lldaycyclemanager.h" #include "lldebugview.h" #include "llenvmanager.h" +#include "llfacebookconnect.h" #include "llfilepicker.h" #include "llfirstuse.h" #include "llfloaterbuy.h" @@ -1952,6 +1955,43 @@ class LLAdvancedDropPacket : public view_listener_t }; +//////////////////// +// EVENT Recorder // +/////////////////// + + +class LLAdvancedViewerEventRecorder : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string command = userdata.asString(); + if ("start playback" == command) + { + llinfos << "Event Playback starting" << llendl; + LLViewerEventRecorder::instance().playbackRecording(); + llinfos << "Event Playback completed" << llendl; + } + else if ("stop playback" == command) + { + // Future + } + else if ("start recording" == command) + { + LLViewerEventRecorder::instance().setEventLoggingOn(); + llinfos << "Event recording started" << llendl; + } + else if ("stop recording" == command) + { + LLViewerEventRecorder::instance().setEventLoggingOff(); + llinfos << "Event recording stopped" << llendl; + } + + return true; + } +}; + + + ///////////////// // AGENT PILOT // @@ -8421,6 +8461,8 @@ void initialize_menus() // Don't prepend MenuName.Foo because these can be used in any menu. enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service)); + enable.add("displayViewerEventRecorderMenuItems",boost::bind(&LLViewerEventRecorder::displayViewerEventRecorderMenuItems,&LLViewerEventRecorder::instance())); + view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts"); enable.add("Conversation.IsConversationLoggingAllowed", boost::bind(&LLFloaterIMContainer::isConversationLoggingAllowed)); @@ -8679,6 +8721,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot"); view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop"); view_listener_t::addMenu(new LLAdvancedCheckAgentPilotLoop(), "Advanced.CheckAgentPilotLoop"); + view_listener_t::addMenu(new LLAdvancedViewerEventRecorder(), "Advanced.EventRecorder"); // Advanced > Debugging view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 50862a74a0..167c33f3ff 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2220,7 +2220,7 @@ static std::string clean_name_from_im(const std::string& name, EInstantMessage t case IM_LURE_ACCEPTED: case IM_LURE_DECLINED: case IM_GODLIKE_LURE_USER: - case IM_YET_TO_BE_USED: + case IM_TELEPORT_REQUEST: case IM_GROUP_ELECTION_DEPRECATED: //IM_GOTO_URL //IM_FROM_TASK_AS_ALERT @@ -2988,6 +2988,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) break; case IM_LURE_USER: + case IM_TELEPORT_REQUEST: { if (is_muted) { @@ -3010,7 +3011,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) bool canUserAccessDstRegion = true; bool doesUserRequireMaturityIncrease = false; - if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + // Do not parse the (empty) lure bucket for TELEPORT_REQUEST + if (IM_TELEPORT_REQUEST != dialog && parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) { region_access_str = LLViewerRegion::accessToString(region_access); region_access_icn = LLViewerRegion::getAccessIcon(region_access); @@ -3082,12 +3084,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } else { - LLNotification::Params params("TeleportOffered"); + LLNotification::Params params; + if (IM_LURE_USER == dialog) + { + params.name = "TeleportOffered"; + params.functor.name = "TeleportOffered"; + } + else if (IM_TELEPORT_REQUEST == dialog) + { + params.name = "TeleportRequest"; + params.functor.name = "TeleportRequest"; + } + params.substitutions = args; params.payload = payload; LLPostponedNotification::add<LLPostponedOfferNotification>( params, from_id, false); } - } } break; @@ -6874,6 +6886,51 @@ void send_group_notice(const LLUUID& group_id, bin_bucket_size); } +void send_lures(const LLSD& notification, const LLSD& response) +{ + std::string text = response["message"].asString(); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_StartLure); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Info); + msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. + msg->addStringFast(_PREHASH_Message, text); + for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); + it != notification["payload"]["ids"].endArray(); + ++it) + { + LLUUID target_id = it->asUUID(); + + msg->nextBlockFast(_PREHASH_TargetData); + msg->addUUIDFast(_PREHASH_TargetID, target_id); + + // Record the offer. + { + std::string target_name; + gCacheName->getFullName(target_id, target_name); // for im log filenames + LLSD args; + args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; + + LLSD payload; + + //*TODO please rewrite all keys to the same case, lower or upper + payload["from_id"] = target_id; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("TeleportOfferSent", args, payload); + + // Add the recepient to the recent people list. + LLRecentPeople::instance().add(target_id); + } + } + gAgent.sendReliableMessage(); +} + bool handle_lure_callback(const LLSD& notification, const LLSD& response) { static const unsigned OFFER_RECIPIENT_LIMIT = 250; @@ -6887,50 +6944,12 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) LLNotificationsUtil::add("TooManyTeleportOffers", args); return false; } - - std::string text = response["message"].asString(); - LLSLURL slurl; - LLAgentUI::buildSLURL(slurl); - text.append("\r\n").append(slurl.getSLURLString()); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_StartLure); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_Info); - msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. - msg->addStringFast(_PREHASH_Message, text); - for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); - it != notification["payload"]["ids"].endArray(); - ++it) - { - LLUUID target_id = it->asUUID(); - - msg->nextBlockFast(_PREHASH_TargetData); - msg->addUUIDFast(_PREHASH_TargetID, target_id); - - // Record the offer. - { - std::string target_name; - gCacheName->getFullName(target_id, target_name); // for im log filenames - LLSD args; - args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; - - LLSD payload; - - //*TODO please rewrite all keys to the same case, lower or upper - payload["from_id"] = target_id; - LLNotificationsUtil::add("TeleportOfferSent", args, payload); - - // Add the recepient to the recent people list. - LLRecentPeople::instance().add(target_id); - } - } - gAgent.sendReliableMessage(); + send_lures(notification, response); } return false; @@ -6970,6 +6989,58 @@ void handle_lure(const uuid_vec_t& ids) } } +bool teleport_request_callback(const LLSD& notification, const LLSD& response) +{ + LLUUID from_id = notification["payload"]["from_id"].asUUID(); + if(from_id.isNull()) + { + llwarns << "from_id is NULL" << llendl; + return false; + } + + std::string from_name; + gCacheName->getFullName(from_id, from_name); + + if(LLMuteList::getInstance()->isMuted(from_id) && !LLMuteList::getInstance()->isLinden(from_name)) + { + return false; + } + + S32 option = 0; + if (response.isInteger()) + { + option = response.asInteger(); + } + else + { + option = LLNotificationsUtil::getSelectedOption(notification, response); + } + + switch(option) + { + // Yes + case 0: + { + LLSD dummy_notification; + dummy_notification["payload"]["ids"][0] = from_id; + + LLSD dummy_response; + dummy_response["message"] = response["message"]; + + send_lures(dummy_notification, dummy_response); + } + break; + + // No + case 1: + default: + break; + } + + return false; +} + +static LLNotificationFunctorRegistration teleport_request_callback_reg("TeleportRequest", teleport_request_callback); void send_improved_im(const LLUUID& to_id, const std::string& name, diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index a90694da7a..7cac4ee891 100755 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -954,15 +954,17 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) objectp = *idle_iter; llassert(objectp->isActive()); objectp->idleUpdate(agent, world, frame_time); - - } + } //update flexible objects LLVolumeImplFlexible::updateClass(); //update animated textures - LLViewerTextureAnim::updateClass(); - } + if (gAnimateTextures) + { + LLViewerTextureAnim::updateClass(); + } + } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e7123b746d..b8300ac5eb 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1641,6 +1641,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 427ec361ec..78768fdbc8 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -198,6 +198,8 @@ #include "llagentui.h" #include "llwearablelist.h" +#include "llviewereventrecorder.h" + #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationmanager.h" @@ -954,27 +956,18 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK { llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl; } - return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down); - } - // Topmost view gets a chance before the hierarchy - //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - //if (top_ctrl) - //{ - // S32 local_x, local_y; - // top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); - // if (top_ctrl->pointInView(local_x, local_y)) - // { - // return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down) ; - // } - // else - // { - // if (down) - // { - // gFocusMgr.setTopCtrl(NULL); - // } - // } - //} + BOOL r = mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down); + if (r) { + + lldebugs << "LLViewerWindow::handleAnyMouseClick viewer with mousecaptor calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << llendl; + + LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y); + LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname)); + + } + return r; + } // Mark the click as handled and return if we aren't within the root view to avoid spurious bugs if( !mRootView->pointInView(x, y) ) @@ -982,27 +975,44 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK return TRUE; } // Give the UI views a chance to process the click - if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ) + + BOOL r= mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ; + if (r) { + + lldebugs << "LLViewerWindow::handleAnyMouseClick calling updatemouseeventinfo - global x "<< " " << x << "global y " << y << "buttonstate: " << buttonstatestr << " buttonname " << buttonname << llendl; + + LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y); + + // Clear local coords - this was a click on root window so these are not needed + // By not including them, this allows the test skeleton generation tool to be smarter when generating code + // the code generator can be smarter because when local coords are present it can try the xui path with local coords + // and fallback to global coordinates only if needed. + // The drawback to this approach is sometimes a valid xui path will appear to work fine, but NOT interact with the UI element + // (VITA support not implemented yet or not visible to VITA due to widget further up xui path not being visible to VITA) + // For this reason it's best to provide hints where possible here by leaving out local coordinates + LLViewerEventRecorder::instance().setMouseLocalCoords(-1,-1); + LLViewerEventRecorder::instance().logMouseEvent(buttonstatestr,buttonname); + if (LLView::sDebugMouseHandling) { - llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl; - } + llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLViewerEventRecorder::instance().get_xui() << llendl; + } return TRUE; - } - else if (LLView::sDebugMouseHandling) - { - llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl; - } + } else if (LLView::sDebugMouseHandling) + { + llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl; + } } // Do not allow tool manager to handle mouseclicks if we have disconnected if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) { + LLViewerEventRecorder::instance().clear_xui(); return TRUE; } - + // If we got this far on a down-click, it wasn't handled. // Up-clicks, though, are always handled as far as the OS is concerned. BOOL default_rtn = !down; @@ -1373,7 +1383,8 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) { LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); - return gViewerKeyboard.scanKey(key, key_down, key_up, key_level); + gViewerKeyboard.scanKey(key, key_down, key_up, key_level); + return; // Be clear this function returns nothing } @@ -2519,6 +2530,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) ||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE)) ||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE))) { + lldebugs << "LLviewerWindow::handleKey handle nav keys for nav" << llendl; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } @@ -2533,12 +2546,14 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) && keyboard_focus && keyboard_focus->handleKey(key,mask,FALSE)) { + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) ||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask))) { + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } } @@ -2548,6 +2563,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // if nothing has focus, go to first or last UI element as appropriate if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)) { + llwarns << "LLviewerWindow::handleKey give floaters first chance at tab key " << llendl; if (gMenuHolder) gMenuHolder->hideMenus(); // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode @@ -2562,11 +2578,13 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) { mRootView->focusNextRoot(); } + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } // hidden edit menu for cut/copy/paste if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask)) { + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } @@ -2606,18 +2624,27 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if (keyboard_focus->handleKey(key, mask, FALSE)) { + + lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned true" << llendl; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; + } else { + lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned FALSE" << llendl; } } if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) ) { + lldebugs << "LLviewerWindow::handleKey toolbar handling?" << llendl; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } // Try for a new-format gesture if (LLGestureMgr::instance().triggerGesture(key, mask)) { + lldebugs << "LLviewerWindow::handleKey new gesture feature" << llendl; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } @@ -2625,6 +2652,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // don't pass it down to the menus. if (gGestureList.trigger(key, mask)) { + lldebugs << "LLviewerWindow::handleKey check gesture trigger" << llendl; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); return TRUE; } @@ -2673,7 +2702,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) // HACK: Numeric keypad <enter> on Mac is Unicode 3 // HACK: Control-M on Windows is Unicode 13 if ((uni_char == 13 && mask != MASK_CONTROL) - || (uni_char == 3 && mask == MASK_NONE)) + || (uni_char == 3 && mask == MASK_NONE) ) { if (mask != MASK_ALT) { @@ -2696,14 +2725,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) return TRUE; } - //// Topmost view gets a chance before the hierarchy - //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - //if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) ) - //{ - // return TRUE; - //} - - return TRUE; + return TRUE; } return FALSE; @@ -2712,8 +2734,6 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) void LLViewerWindow::handleScrollWheel(S32 clicks) { - LLView::sMouseHandlerMessage.clear(); - LLUI::resetMouseIdleTimer(); LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 567138e160..3bdbb05beb 100755 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -145,9 +145,6 @@ public: LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl; LLWebProfile::reportImageUploadStatus(true); } - -private: - LLPointer<LLImageFormatted> mImagep; }; diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp index 28f959eb71..a8e06511d7 100755 --- a/indra/newview/llwindowlistener.cpp +++ b/indra/newview/llwindowlistener.cpp @@ -265,7 +265,9 @@ void LLWindowListener::getPaths(LLSD const & request) void LLWindowListener::keyDown(LLSD const & evt) { Response response(LLSD(), evt); - + KEY key = getKEY(evt); + MASK mask = getMask(evt); + if (evt.has("path")) { std::string path(evt["path"]); @@ -280,8 +282,6 @@ void LLWindowListener::keyDown(LLSD const & evt) response.setResponse(target_view->getInfo()); gFocusMgr.setKeyboardFocus(target_view); - KEY key = getKEY(evt); - MASK mask = getMask(evt); gViewerKeyboard.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } @@ -294,7 +294,8 @@ void LLWindowListener::keyDown(LLSD const & evt) } else { - mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt)); + gViewerKeyboard.handleKey(key, mask, false); + if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } } diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 5fa380e0e3..bfae142812 100755 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -522,6 +522,17 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& tooltip_fmt.setArg("[AREA]", llformat("%d", extra)); tooltip_fmt.setArg("[PRICE]", llformat("%d", extra2)); + + // Check for division by zero + if (extra != 0) + { + tooltip_fmt.setArg("[SQMPRICE]", llformat("%.1f", (F32)extra2 / (F32)extra)); + } + else + { + tooltip_fmt.setArg("[SQMPRICE]", LLTrans::getString("Unknown")); + } + new_item.setTooltip(tooltip_fmt.getString()); if (type == MAP_ITEM_LAND_FOR_SALE) diff --git a/indra/newview/skins/default/textures/icons/Facebook.png b/indra/newview/skins/default/textures/icons/Facebook.png Binary files differnew file mode 100644 index 0000000000..8287d56f88 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Facebook.png diff --git a/indra/newview/skins/default/textures/icons/map_placeholder.png b/indra/newview/skins/default/textures/icons/map_placeholder.png Binary files differnew file mode 100644 index 0000000000..31e457aa75 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/map_placeholder.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 54f60f4441..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" /> diff --git a/indra/newview/skins/default/textures/toolbar_icons/facebook.png b/indra/newview/skins/default/textures/toolbar_icons/facebook.png Binary files differnew file mode 100644 index 0000000000..b960b834dc --- /dev/null +++ b/indra/newview/skins/default/textures/toolbar_icons/facebook.png diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index c8c20424e1..4268b95370 100755 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -1274,7 +1274,7 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 793a6e6fa1..a660e812cc 100755 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -1365,7 +1365,7 @@ Only large parcels can be listed in search. <combo_box.item label="Any Category" name="item0" - value="any" /> + value="none" /> <combo_box.item label="Linden Location" name="item1" 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_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..b7ff374d5f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_social.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<floater + positioning="cascading" + can_close="true" + can_resize="false" + help_topic="floater_social" + 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" + follows="all" + top="0" + left="0"> + <tab_container + name="tabs" + tab_group="1" + tab_min_width="70" + tab_height="30" + tab_position="top" + top="7" + height="437" + halign="center"> + <panel + filename="panel_social_status.xml" + class="llsocialstatuspanel" + follows="all" + label="STATUS" + name="panel_social_status"/> + <panel + filename="panel_social_photo.xml" + class="llsocialphotopanel" + follows="all" + label="PHOTO" + name="panel_social_photo"/> + <panel + filename="panel_social_place.xml" + class="llsocialcheckinpanel" + follows="all" + label="CHECK IN" + name="panel_social_place"/> + <panel + filename="panel_social_account.xml" + class="llsocialaccountpanel" + follows="all" + label="ACCOUNT" + name="panel_social_account"/> + </tab_container> + <panel + name="connection_status_panel" + follows="left|bottom|right" + 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_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml index b3d28788da..31b1d091ee 100755 --- a/indra/newview/skins/default/xui/en/menu_conversation.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -53,6 +53,13 @@ <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/> </menu_item_call> <menu_item_call + label="Request teleport" + layout="topleft" + name="request_teleport"> + <on_click function="Avatar.DoToSelected" parameter="request_teleport"/> + <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call label="Voice call" layout="topleft" name="voice_call"> diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml index 8796b87955..a1a3afbf68 100755 --- a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml @@ -56,6 +56,16 @@ function="Calllog.Enable" parameter="can_offer_teleport"/> </menu_item_call> + <menu_item_call + label="Request Teleport" + name="request_teleport"> + <on_click + function="Calllog.Action" + parameter="request_teleport"/> + <on_enable + function="Calllog.Enable" + parameter="can_offer_teleport"/> + </menu_item_call> <menu_item_separator /> <menu_item_call label="Add Friend" diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 13dc0b941a..512205ba43 100755 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -591,6 +591,14 @@ parameter="lure" /> </menu_item_call> <menu_item_call + label="Request Teleport..." + layout="topleft" + name="Request Teleport..."> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="request_lure" /> + </menu_item_call> + <menu_item_call label="Start Conference Chat" layout="topleft" name="Conference Chat"> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml index 3abb5f7bc8..f12226ebeb 100755 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -29,6 +29,15 @@ parameter="can_offer_teleport"/> </menu_item_call> <menu_item_call + label="Request Teleport" + name="request_teleport"> + <menu_item_call.on_click + function="Avatar.TeleportRequest"/> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call label="Voice call" layout="topleft" name="voice_call"> @@ -134,5 +143,4 @@ function="Avatar.EnableItem" parameter="can_block" /> </menu_item_check> - <menu_item_separator /> </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 2d65052def..d635b8ee93 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 @@ -189,7 +197,7 @@ name="Preferences" shortcut="control|P"> <menu_item_call.on_click - function="Floater.Show" + function="Floater.Toggle" parameter="preferences" /> </menu_item_call> <menu_item_call @@ -2947,6 +2955,34 @@ label="Recorder" name="Recorder" tear_off="true"> + <menu_item_call visible="false" + label="Start event recording" + name="Start event recording"> + <menu_item_call.on_visible + function="displayViewerEventRecorderMenuItems" /> + <menu_item_call.on_click + function="Advanced.EventRecorder" + parameter="start recording" /> + </menu_item_call> + <menu_item_call visible="false" + label="Stop event recording" + name="Stop event recording"> + <menu_item_call.on_visible + function="displayViewerEventRecorderMenuItems" /> + <menu_item_call.on_click + function="Advanced.EventRecorder" + parameter="stop recording" /> + </menu_item_call> + <menu_item_call visible="false" + label="Playback event recording" + name="Playback event recording"> + <menu_item_call.on_visible + function="displayViewerEventRecorderMenuItems" /> + <menu_item_call.on_click + function="Advanced.EventRecorder" + parameter="start playback" /> + </menu_item_call> + <menu_item_call label="Start Playback" name="Start Playback"> @@ -3049,6 +3085,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 ed7c862433..d4dcb96519 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4071,6 +4071,27 @@ Join me in [REGION] <notification icon="alertmodal.tga" + name="TeleportRequestPrompt" + type="alertmodal"> +Request a teleport to [NAME] with the following message + <tag>confirm</tag> + <form name="form"> + <input name="message" type="text"> + + </input> + <button + default="true" + index="0" + name="OK" + text="OK"/> + <button + index="1" + name="Cancel" + text="Cancel"/> + </form> + </notification> + <notification + icon="alertmodal.tga" name="TooManyTeleportOffers" type="alertmodal"> You attempted to make [OFFERS] teleport offers @@ -6017,6 +6038,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" @@ -6637,7 +6665,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th sound="UISndNewIncomingIMSession"> [NAME_SLURL] has offered to teleport you to their location: -“[MESSAGE]” +"[MESSAGE]” <icon>[MATURITY_ICON]</icon> - [MATURITY_STR] <tag>confirm</tag> <form name="form"> @@ -6702,6 +6730,27 @@ However, this region contains content accessible to adults only. Teleport offer sent to [TO_NAME] </notification> + <notification + icon="notify.tga" + name="TeleportRequest" + log_to_im="true" + type="offer"> +[NAME_SLURL] is requesting to be teleported to your location. +[MESSAGE] + +Offer a teleport? + <tag>confirm</tag> + <form name="form"> + <button + index="0" + name="Yes" + text="Yes"/> + <button + index="1" + name="No" + text="No"/> + </form> + </notification> <notification icon="notify.tga" @@ -6750,7 +6799,6 @@ However, this region contains content accessible to adults only. icon="notify.tga" name="FriendshipOffered" log_to_im="true" - show_toast="false" type="notify"> <tag>friendship</tag> You have offered friendship to [TO_NAME] @@ -6800,7 +6848,6 @@ However, this region contains content accessible to adults only. icon="notify.tga" name="FriendshipAcceptedByMe" log_to_im="true" - show_toast="false" type="notify"> <tag>friendship</tag> Friendship offer accepted. @@ -6810,7 +6857,6 @@ Friendship offer accepted. icon="notify.tga" name="FriendshipDeclinedByMe" log_to_im="true" - show_toast="false" type="notify"> <tag>friendship</tag> Friendship offer declined. diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index ed274d0233..dc0e4a5947 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_preferences_move.xml b/indra/newview/skins/default/xui/en/panel_preferences_move.xml index d9067b41c7..8794e3bf95 100755 --- a/indra/newview/skins/default/xui/en/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_move.xml @@ -129,6 +129,16 @@ name="tap_tap_hold_to_run" width="237" top_pad="0"/> + <check_box + control_name="AutomaticFly" + follows="left|top" + height="20" + label="Hold jump or crouch key to start or stop flying" + layout="topleft" + left_delta="0" + name="automatic_fly" + width="237" + top_pad="0"/> <text follows="left|top" type="string" @@ -260,4 +270,4 @@ function="Floater.Show" parameter="pref_joystick" /> </button> -</panel>
\ No newline at end of file +</panel> 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..d7235396fe --- /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"> + [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share/ta-p/2149711 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/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index dd2a0c6627..064ece6e4b 100755 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -113,7 +113,7 @@ image_pressed="Pause_Press" image_pressed_selected="Play_Press" is_toggle="true" - left_pad="15" + left_pad="5" top="1" name="media_toggle_btn" tool_tip="Start/Stop All Media (Music, Video, Web pages)" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 1c46cec479..8ac95beddb 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 --> @@ -379,7 +387,7 @@ Please try logging in again in a minute.</string> <!-- world map --> <string name="texture_loading">Loading...</string> <string name="worldmap_offline">Offline</string> - <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE]</string> + <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE] ([SQMPRICE] L$/m²)</string> <string name="worldmap_results_none_found">None found.</string> <!-- animations uploading status codes --> @@ -2185,7 +2193,7 @@ For AI Character: Get the closest navigable point to the point provided. <string name="InventoryNoTexture">You do not have a copy of this texture in your inventory</string> <string name="InventoryInboxNoItems">Your Marketplace purchases will appear here. You may then drag them into your inventory to use them.</string> <string name="MarketplaceURL">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/</string> - <string name="MarketplaceURL_CreateStore">http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4</string> + <string name="MarketplaceURL_CreateStore">http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3</string> <string name="MarketplaceURL_Dashboard">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard</string> <string name="MarketplaceURL_Imports">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports</string> <string name="MarketplaceURL_LearnMore">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more</string> @@ -2464,7 +2472,11 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale all estates that you manage for [OWNER] </string> <string name="RegionInfoAllowedResidents">Allowed Residents: ([ALLOWEDAGENTS], max [MAXACCESS])</string> - <string name="RegionInfoAllowedGroups">Allowed groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string> + <string name="RegionInfoAllowedGroups">Allowed Groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string> + <string name="RegionInfoEstateManagers">Estate Managers: ([ESTATEMANAGERS], max [MAXMANAGERS])</string> + <string name="RegionInfoBannedResidents">Banned Residents: ([BANNEDAGENTS], max [MAXBANNED])</string> + <string name="RegionInfoListTypeAllowedAgents">Allowed Residents</string> + <string name="RegionInfoListTypeBannedAgents">Banned Residents</string> <!-- script limits floater --> <string name="ScriptLimitsParcelScriptMemory">Parcel Script Memory</string> @@ -3427,6 +3439,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) @@ -3859,6 +3874,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> @@ -3886,6 +3902,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'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'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/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 4686d1ed3a..484511a08b 100755 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1256,7 +1256,7 @@ Intenta iniciar sesión de nuevo en unos instantes. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 346fa62351..78d846ff4f 100755 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1274,7 +1274,7 @@ Veuillez réessayer de vous connecter dans une minute. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index 160df911d3..60ed2b0929 100755 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1265,7 +1265,7 @@ Prova ad accedere nuovamente tra un minuto. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index ab2fd461ab..a0f45e5a55 100755 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -1274,7 +1274,7 @@ support@secondlife.com にお問い合わせください。 https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index adc3ee14e0..2eb4c0a02e 100755 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1220,7 +1220,7 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 49ce33ec96..0f71edcee0 100755 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -1271,7 +1271,7 @@ support@secondlife.com. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 5a809ea783..c4b1530b2b 100755 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -1271,7 +1271,7 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ </string> <string name="MarketplaceURL_CreateStore"> - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3 </string> <string name="MarketplaceURL_Dashboard"> https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp index b28eb5db43..5e73dbb981 100755 --- a/indra/newview/tests/lltranslate_test.cpp +++ b/indra/newview/tests/lltranslate_test.cpp @@ -308,8 +308,8 @@ void LLCurl::Responder::httpCompleted() { } void LLCurl::Responder::completedRaw(LLChannelDescriptors const &,boost::shared_ptr<LLBufferArray> 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) {} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 19863dd845..894e368427 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -104,26 +104,37 @@ class ViewerManifest(LLManifest): self.path("dictionaries") self.end_prefix(pkgdir) - # CHOP-955: If we have "sourceid" in the build process - # environment, generate it into settings_install.xml. - try: - sourceid = os.environ["sourceid"] - except KeyError: - # no sourceid, no settings_install.xml file - pass - else: - if sourceid: - # Single-entry subset of the LLSD content of settings.xml - content = dict(sourceid=dict(Comment='Identify referring agency to Linden web servers', - Persist=1, - Type='String', - Value=sourceid)) - # put_in_file(src=) need not be an actual pathname; it - # only needs to be non-empty - settings_install = self.put_in_file(llsd.format_pretty_xml(content), - "settings_install.xml", - src="environment") - print "Put sourceid '%s' in %s" % (sourceid, settings_install) + # CHOP-955: If we have "sourceid" or "viewer_channel" in the + # build process environment, generate it into + # settings_install.xml. + settings_template = dict( + sourceid=dict(Comment='Identify referring agency to Linden web servers', + Persist=1, + Type='String', + Value=''), + CmdLineChannel=dict(Comment='Command line specified channel name', + Persist=0, + Type='String', + Value='')) + settings_install = {} + for key, setting in (("sourceid", "sourceid"), + ("channel", "CmdLineChannel")): + if key in self.args: + # only set if value is non-empty + if self.args[key]: + # copy corresponding setting from settings_template + settings_install[setting] = settings_template[setting].copy() + # then fill in Value + settings_install[setting]["Value"] = self.args[key] + print "Put %s '%s' in settings_install.xml" % (setting, self.args[key]) + + # did we actually copy anything into settings_install dict? + if settings_install: + # put_in_file(src=) need not be an actual pathname; it + # only needs to be non-empty + self.put_in_file(llsd.format_pretty_xml(settings_install), + "settings_install.xml", + src="environment") self.end_prefix("app_settings") @@ -611,6 +622,9 @@ class WindowsManifest(ViewerManifest): installer_file = self.args['installer_name'] else: installer_file = installer_file % substitution_strings + if len(self.args['package_id']) > 0: + installer_file = installer_file.replace(self.args['package_id'], "") + installer_file = installer_file.replace(".exe", self.args['package_id'] + ".exe") substitution_strings['installer_file'] = installer_file tempfile = "secondlife_setup_tmp.nsi" @@ -838,7 +852,9 @@ class DarwinManifest(ViewerManifest): volname="Second Life Installer" # DO NOT CHANGE without understanding comment above - if self.default_channel(): + if len(self.args['package_id']) > 0: + imagename = imagename + self.args['package_id'] + elif self.default_channel(): if not self.default_grid(): # beta case imagename = imagename + '_' + self.args['grid'].upper() @@ -851,7 +867,7 @@ class DarwinManifest(ViewerManifest): # make sure we don't have stale files laying about self.remove(sparsename, finalname) - self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 700 -layout SPUD' % { + self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 1000 -layout SPUD' % { 'sparse':sparsename, 'vol':volname}) @@ -926,6 +942,7 @@ class DarwinManifest(ViewerManifest): print "Converting temp disk image to final disk image" self.run_command('hdiutil convert %(sparse)r -format UDZO -imagekey zlib-level=9 -o %(final)r' % {'sparse':sparsename, 'final':finalname}) + self.run_command('hdiutil internet-enable -yes %(final)r' % {'final':finalname}) # get rid of the temp file self.package_file = finalname self.remove(sparsename) @@ -998,6 +1015,7 @@ class LinuxManifest(ViewerManifest): installer_name += '_' + self.args['grid'].upper() else: installer_name += '_' + self.channel_oneword().upper() + installer_name = installer_name + self.args['package_id'] self.strip_binaries() |