phoenix-firestorm/indra/newview/llfloaterfacebook.cpp

1243 lines
41 KiB
C++

/**
* @file llfloaterfacebook.cpp
* @brief Implementation of llfloaterfacebook
* @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 "llfloaterfacebook.h"
#include "llagent.h"
#include "llagentui.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llfacebookconnect.h"
#include "llfloaterbigpreview.h"
#include "llfloaterreg.h"
#include "lliconctrl.h"
#include "llimagefiltersmanager.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"
#include "lltabcontainer.h"
#include "llavatarlist.h"
#include "llpanelpeoplemenus.h"
#include "llaccordionctrl.h"
#include "llaccordionctrltab.h"
#include "llspinctrl.h"
#include "lfsimfeaturehandler.h"
static LLPanelInjector<LLFacebookStatusPanel> t_panel_status("llfacebookstatuspanel");
static LLPanelInjector<LLFacebookPhotoPanel> t_panel_photo("llfacebookphotopanel");
static LLPanelInjector<LLFacebookCheckinPanel> t_panel_checkin("llfacebookcheckinpanel");
static LLPanelInjector<LLFacebookFriendsPanel> t_panel_friends("llfacebookfriendspanel");
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";
const S32 MAX_QUALITY = 100; // Max quality value for jpeg images
const S32 MIN_QUALITY = 0; // Min quality value for jpeg images
const S32 TARGET_DATA_SIZE = 950000; // Size of the image (compressed) we're trying to send to Facebook
std::string get_map_url()
{
LLVector3d center_agent;
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
center_agent = regionp->getCenterGlobal();
}
int x_pos = center_agent[0] / 256.0;
int y_pos = center_agent[1] / 256.0;
// <FS:Ansariel> OpenSim support
//std::string map_url = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-1-%d-%d-objects.jpg", x_pos, y_pos);
std::string map_url = LFSimFeatureHandler::instance().mapServerURL() + llformat("map-1-%d-%d-objects.jpg", x_pos, y_pos);
// </FS:Ansariel>
return map_url;
}
// Compute target jpeg quality : see https://wiki.lindenlab.com/wiki/Facebook_Image_Quality for details
S32 compute_jpeg_quality(S32 width, S32 height)
{
F32 target_compression_ratio = (F32)(width * height * 3) / (F32)(TARGET_DATA_SIZE);
S32 quality = (S32)(110.0f - (2.0f * target_compression_ratio));
return llclamp(quality,MIN_QUALITY,MAX_QUALITY);
}
///////////////////////////
//LLFacebookStatusPanel//////
///////////////////////////
LLFacebookStatusPanel::LLFacebookStatusPanel() :
mMessageTextEditor(NULL),
mPostButton(NULL),
mCancelButton(NULL),
mAccountCaptionLabel(NULL),
mAccountNameLabel(NULL),
mPanelButtons(NULL),
mConnectButton(NULL),
mDisconnectButton(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLFacebookStatusPanel::onConnect, this));
mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLFacebookStatusPanel::onDisconnect, this));
setVisibleCallback(boost::bind(&LLFacebookStatusPanel::onVisibilityChange, this, _2));
mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLFacebookStatusPanel::onSend, this));
}
BOOL LLFacebookStatusPanel::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");
mMessageTextEditor = getChild<LLUICtrl>("status_message");
mPostButton = getChild<LLUICtrl>("post_status_btn");
mCancelButton = getChild<LLUICtrl>("cancel_status_btn");
return LLPanel::postBuild();
}
void LLFacebookStatusPanel::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);
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 LLFacebookStatusPanel::onSend()
{
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookStatusPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookStatusPanel", boost::bind(&LLFacebookStatusPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
{
sendStatus();
}
else
{
LLFacebookConnect::instance().checkConnectionToFacebook(true);
}
}
bool LLFacebookStatusPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
case LLFacebookConnect::FB_CONNECTED:
sendStatus();
break;
case LLFacebookConnect::FB_POSTED:
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookStatusPanel");
clearAndClose();
break;
}
return false;
}
bool LLFacebookStatusPanel::onFacebookConnectAccountStateChange(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;
}
void LLFacebookStatusPanel::sendStatus()
{
std::string message = mMessageTextEditor->getValue().asString();
if (!message.empty())
{
LLFacebookConnect::instance().updateStatus(message);
}
}
void LLFacebookStatusPanel::onVisibilityChange(BOOL visible)
{
if(visible)
{
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookAccountPanel");
LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookAccountPanel", boost::bind(&LLFacebookStatusPanel::onFacebookConnectAccountStateChange, this, _1));
LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLFacebookAccountPanel");
LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLFacebookAccountPanel", boost::bind(&LLFacebookStatusPanel::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("LLFacebookAccountPanel");
LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLFacebookAccountPanel");
}
}
bool LLFacebookStatusPanel::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 LLFacebookStatusPanel::showConnectButton()
{
if(!mConnectButton->getVisible())
{
mConnectButton->setVisible(TRUE);
mDisconnectButton->setVisible(FALSE);
}
}
void LLFacebookStatusPanel::hideConnectButton()
{
if(mConnectButton->getVisible())
{
mConnectButton->setVisible(FALSE);
mDisconnectButton->setVisible(TRUE);
}
}
void LLFacebookStatusPanel::showDisconnectedLayout()
{
mAccountCaptionLabel->setText(getString("facebook_disconnected"));
mAccountNameLabel->setText(std::string(""));
showConnectButton();
}
void LLFacebookStatusPanel::showConnectedLayout()
{
LLFacebookConnect::instance().loadFacebookInfo();
mAccountCaptionLabel->setText(getString("facebook_connected"));
hideConnectButton();
}
void LLFacebookStatusPanel::onConnect()
{
LLFacebookConnect::instance().checkConnectionToFacebook(true);
//Clear only the facebook browser cookies so that the facebook login screen appears
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
}
void LLFacebookStatusPanel::onDisconnect()
{
LLFacebookConnect::instance().disconnectFromFacebook();
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
}
void LLFacebookStatusPanel::clearAndClose()
{
mMessageTextEditor->setValue("");
LLFloater* floater = getParentByType<LLFloater>();
if (floater)
{
floater->closeFloater();
}
}
///////////////////////////
//LLFacebookPhotoPanel///////
///////////////////////////
LLFacebookPhotoPanel::LLFacebookPhotoPanel() :
mResolutionComboBox(NULL),
mRefreshBtn(NULL),
mBtnPreview(NULL),
mWorkingLabel(NULL),
mThumbnailPlaceholder(NULL),
mCaptionTextBox(NULL),
mPostButton(NULL),
mBigPreviewFloater(NULL),
mQuality(MAX_QUALITY)
{
mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFacebookPhotoPanel::onSend, this));
mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFacebookPhotoPanel::onClickNewSnapshot, this));
mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLFacebookPhotoPanel::onClickBigPreview, this));
}
LLFacebookPhotoPanel::~LLFacebookPhotoPanel()
{
if(mPreviewHandle.get())
{
mPreviewHandle.get()->die();
}
// <FS:Ansariel> Store settings at logout
gSavedSettings.setS32("FSLastSnapshotToFacebookResolution", getChild<LLComboBox>("resolution_combobox")->getCurrentIndex());
gSavedSettings.setS32("FSLastSnapshotToFacebookWidth", getChild<LLSpinCtrl>("custom_snapshot_width")->getValue().asInteger());
gSavedSettings.setS32("FSLastSnapshotToFacebookHeight", getChild<LLSpinCtrl>("custom_snapshot_height")->getValue().asInteger());
// </FS:Ansariel>
}
BOOL LLFacebookPhotoPanel::postBuild()
{
setVisibleCallback(boost::bind(&LLFacebookPhotoPanel::onVisibilityChange, this, _2));
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
// <FS:Ansariel> Store settings at logout; Nonsense!
//mResolutionComboBox->setValue("[i1200,i630]"); // hardcoded defaults ftw!
mResolutionComboBox->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
mFilterComboBox->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
mBtnPreview = getChild<LLButton>("big_preview_btn");
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
mCaptionTextBox = getChild<LLUICtrl>("photo_caption");
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare
getChild<LLSpinCtrl>("custom_snapshot_width")->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
getChild<LLSpinCtrl>("custom_snapshot_height")->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
getChild<LLComboBox>("resolution_combobox")->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToFacebookResolution"));
getChild<LLSpinCtrl>("custom_snapshot_width")->setValue(gSavedSettings.getS32("FSLastSnapshotToFacebookWidth"));
getChild<LLSpinCtrl>("custom_snapshot_height")->setValue(gSavedSettings.getS32("FSLastSnapshotToFacebookHeight"));
// </FS:Ansariel>
// Update filter list
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
for (U32 i = 0; i < filter_list.size(); i++)
{
filterbox->add(filter_list[i]);
}
return LLPanel::postBuild();
}
// virtual
S32 LLFacebookPhotoPanel::notify(const LLSD& info)
{
if (info.has("snapshot-updating"))
{
// Disable the Post button and whatever else while the snapshot is not updated
// updateControls();
return 1;
}
if (info.has("snapshot-updated"))
{
// Enable the send/post/save buttons.
updateControls();
// The refresh button is initially hidden. We show it after the first update,
// i.e. after snapshot is taken
LLUICtrl * refresh_button = getRefreshBtn();
if (!refresh_button->getVisible())
{
refresh_button->setVisible(true);
}
return 1;
}
return 0;
}
void LLFacebookPhotoPanel::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);
mFilterComboBox->setEnabled(no_ongoing_connection);
mRefreshBtn->setEnabled(no_ongoing_connection);
mBtnPreview->setEnabled(no_ongoing_connection);
// Reassign the preview floater if we have the focus and the preview exists
if (hasFocus() && isPreviewVisible())
{
attachPreview();
}
// Toggle the button state as appropriate
bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
mBtnPreview->setToggleState(preview_active);
// Display the thumbnail 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 ;
S32 offset_x = thumbnail_rect.mLeft + local_offset_x;
S32 offset_y = thumbnail_rect.mBottom + local_offset_y;
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);
}
// 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* LLFacebookPhotoPanel::getPreviewView()
{
LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
return previewp;
}
void LLFacebookPhotoPanel::onVisibilityChange(BOOL visible)
{
if (visible)
{
if (mPreviewHandle.get())
{
LLSnapshotLivePreview* preview = getPreviewView();
if(preview)
{
LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL;
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();
mQuality = MAX_QUALITY;
previewp->setContainer(this);
previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
previewp->setSnapshotQuality(mQuality, false);
previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
updateControls();
}
}
}
void LLFacebookPhotoPanel::onClickNewSnapshot()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
previewp->updateSnapshot(TRUE);
}
}
void LLFacebookPhotoPanel::onClickBigPreview()
{
// Toggle the preview
if (isPreviewVisible())
{
LLFloaterReg::hideInstance("big_preview");
}
else
{
attachPreview();
LLFloaterReg::showInstance("big_preview");
}
}
bool LLFacebookPhotoPanel::isPreviewVisible()
{
return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
}
void LLFacebookPhotoPanel::attachPreview()
{
if (mBigPreviewFloater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
mBigPreviewFloater->setPreview(previewp);
mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
}
}
void LLFacebookPhotoPanel::onSend()
{
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookPhotoPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookPhotoPanel", boost::bind(&LLFacebookPhotoPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
{
sendPhoto();
}
else
{
LLFacebookConnect::instance().checkConnectionToFacebook(true);
}
}
bool LLFacebookPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
case LLFacebookConnect::FB_CONNECTED:
sendPhoto();
break;
case LLFacebookConnect::FB_POSTED:
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookPhotoPanel");
clearAndClose();
break;
}
return false;
}
void LLFacebookPhotoPanel::sendPhoto()
{
// Get the caption
std::string caption = mCaptionTextBox->getValue().asString();
// Get the image
LLSnapshotLivePreview* previewp = getPreviewView();
// Post to Facebook
LLFacebookConnect::instance().sharePhoto(previewp->getFormattedImage(), caption);
updateControls();
}
void LLFacebookPhotoPanel::clearAndClose()
{
mCaptionTextBox->setValue("");
LLFloater* floater = getParentByType<LLFloater>();
if (floater)
{
floater->closeFloater();
if (mBigPreviewFloater)
{
mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
}
}
}
void LLFacebookPhotoPanel::updateControls()
{
LLSnapshotLivePreview* previewp = getPreviewView();
BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
// *TODO: Separate maximum size for Web images from postcards
LL_DEBUGS() << "Is snapshot up-to-date? " << got_snap << LL_ENDL;
updateResolution(FALSE);
}
void LLFacebookPhotoPanel::updateResolution(BOOL do_update)
{
LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
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];
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
if (previewp && combobox->getCurrentIndex() >= 0)
{
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare; moved up
checkAspectRatio(width);
S32 original_width = 0 , original_height = 0 ;
previewp->getSize(original_width, original_height) ;
if (width == 0 || height == 0)
{
// take resolution from current window size
LL_DEBUGS() << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << LL_ENDL;
previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
}
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare
else if (width == -1 || height == -1)
{
// take resolution from custom size
LLSpinCtrl* width_spinner = getChild<LLSpinCtrl>("custom_snapshot_width");
LLSpinCtrl* height_spinner = getChild<LLSpinCtrl>("custom_snapshot_height");
S32 custom_width = width_spinner->getValue().asInteger();
S32 custom_height = height_spinner->getValue().asInteger();
if (checkImageSize(previewp, custom_width, custom_height, TRUE, previewp->getMaxImageSize()))
{
width_spinner->set(custom_width);
height_spinner->set(custom_height);
}
LL_DEBUGS() << "Setting preview res from custom: " << custom_width << "x" << custom_height << LL_ENDL;
previewp->setSize(custom_width, custom_height);
}
// </FS:Ansariel>
else
{
// use the resolution from the selected pre-canned drop-down choice
LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL;
previewp->setSize(width, height);
}
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare; moved up
//checkAspectRatio(width);
previewp->getSize(width, height);
// Recompute quality setting
mQuality = compute_jpeg_quality(width, height);
previewp->setSnapshotQuality(mQuality, false);
if (original_width != width || original_height != height)
{
previewp->setSize(width, height);
if (do_update)
{
previewp->updateSnapshot(TRUE);
updateControls();
}
}
// Get the old filter, compare to the current one "filter_name" and set if changed
std::string original_filter = previewp->getFilter();
if (original_filter != filter_name)
{
previewp->setFilter(filter_name);
if (do_update)
{
previewp->updateSnapshot(FALSE, TRUE);
updateControls();
}
}
}
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare
BOOL custom_resolution = static_cast<LLComboBox *>(mResolutionComboBox)->getSelectedValue().asString() == "[i-1,i-1]";
getChild<LLSpinCtrl>("custom_snapshot_width")->setEnabled(custom_resolution);
getChild<LLSpinCtrl>("custom_snapshot_height")->setEnabled(custom_resolution);
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setEnabled(custom_resolution);
// </FS:Ansariel>
}
void LLFacebookPhotoPanel::checkAspectRatio(S32 index)
{
LLSnapshotLivePreview *previewp = getPreviewView() ;
BOOL keep_aspect = FALSE;
if (0 == index) // current window size
{
keep_aspect = TRUE;
}
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare
else if (-1 == index)
{
keep_aspect = getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->get();
}
// </FS:Ansariel>
else // predefined resolution
{
keep_aspect = FALSE;
}
if (previewp)
{
previewp->mKeepAspectRatio = keep_aspect;
}
}
LLUICtrl* LLFacebookPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
}
// <FS:Ansariel> FIRE-15112: Allow custom resolution for SLShare
BOOL LLFacebookPhotoPanel::checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, BOOL isWidthChanged, S32 max_value)
{
S32 w = width ;
S32 h = height ;
if(previewp && previewp->mKeepAspectRatio)
{
if(gViewerWindow->getWindowWidthRaw() < 1 || gViewerWindow->getWindowHeightRaw() < 1)
{
return FALSE ;
}
//aspect ratio of the current window
F32 aspect_ratio = (F32)gViewerWindow->getWindowWidthRaw() / gViewerWindow->getWindowHeightRaw() ;
//change another value proportionally
if(isWidthChanged)
{
height = ll_round(width / aspect_ratio) ;
}
else
{
width = ll_round(height * aspect_ratio) ;
}
//bound w/h by the max_value
if(width > max_value || height > max_value)
{
if(width > height)
{
width = max_value ;
height = (S32)(width / aspect_ratio) ;
}
else
{
height = max_value ;
width = (S32)(height * aspect_ratio) ;
}
}
}
return (w != width || h != height) ;
}
// </FS:Ansariel>
////////////////////////
//LLFacebookCheckinPanel//
////////////////////////
LLFacebookCheckinPanel::LLFacebookCheckinPanel() :
mMapUrl(""),
mReloadingMapTexture(false)
{
mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLFacebookCheckinPanel::onSend, this));
}
BOOL LLFacebookCheckinPanel::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 LLFacebookCheckinPanel::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 LLFacebookCheckinPanel::onSend()
{
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookCheckinPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookCheckinPanel", boost::bind(&LLFacebookCheckinPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
{
sendCheckin();
}
else
{
LLFacebookConnect::instance().checkConnectionToFacebook(true);
}
}
bool LLFacebookCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
case LLFacebookConnect::FB_CONNECTED:
sendCheckin();
break;
case LLFacebookConnect::FB_POSTED:
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookCheckinPanel");
clearAndClose();
break;
}
return false;
}
void LLFacebookCheckinPanel::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("");
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
region_name = regionp->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 LLFacebookCheckinPanel::clearAndClose()
{
mMessageTextEditor->setValue("");
LLFloater* floater = getParentByType<LLFloater>();
if (floater)
{
floater->closeFloater();
}
}
///////////////////////////
//LLFacebookFriendsPanel//////
///////////////////////////
LLFacebookFriendsPanel::LLFacebookFriendsPanel() :
mFriendsStatusCaption(NULL),
mSecondLifeFriends(NULL),
mSuggestedFriends(NULL)
{
}
LLFacebookFriendsPanel::~LLFacebookFriendsPanel()
{
LLAvatarTracker::instance().removeObserver(this);
}
BOOL LLFacebookFriendsPanel::postBuild()
{
mFriendsStatusCaption = getChild<LLTextBox>("facebook_friends_status");
mSecondLifeFriends = getChild<LLAvatarList>("second_life_friends");
mSecondLifeFriends->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
mSuggestedFriends = getChild<LLAvatarList>("suggested_friends");
mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu);
setVisibleCallback(boost::bind(&LLFacebookFriendsPanel::updateFacebookList, this, _2));
LLAvatarTracker::instance().addObserver(this);
return LLPanel::postBuild();
}
bool LLFacebookFriendsPanel::updateSuggestedFriendList()
{
const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
uuid_vec_t& second_life_friends = mSecondLifeFriends->getIDs();
second_life_friends.clear();
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();
if (agent_id.notNull())
{
bool second_life_buddy = av_tracker.isBuddy(agent_id);
if (second_life_buddy)
{
second_life_friends.push_back(agent_id);
}
else
{
//FB+SL but not SL friend
suggested_friends.push_back(agent_id);
}
}
}
//Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display)
mSecondLifeFriends->setDirty(true, !mSecondLifeFriends->filterHasMatches());
mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches());
showFriendsAccordionsIfNeeded();
return false;
}
void LLFacebookFriendsPanel::showFriendsAccordionsIfNeeded()
{
// Show / hide the status text : needs to be done *before* showing / hidding the accordions
if (!mSecondLifeFriends->filterHasMatches() && !mSuggestedFriends->filterHasMatches())
{
// Show some explanation text if the lists are empty...
mFriendsStatusCaption->setVisible(true);
if (LLFacebookConnect::instance().isConnected())
{
//...you're connected to FB but have no friends :(
mFriendsStatusCaption->setText(getString("facebook_friends_empty"));
}
else
{
//...you're not connected to FB
mFriendsStatusCaption->setText(getString("facebook_friends_no_connected"));
}
// Hide the lists
getChild<LLAccordionCtrl>("friends_accordion")->setVisible(false);
getChild<LLAccordionCtrlTab>("tab_second_life_friends")->setVisible(false);
getChild<LLAccordionCtrlTab>("tab_suggested_friends")->setVisible(false);
}
else
{
// We have something in the lists, hide the explanatory text
mFriendsStatusCaption->setVisible(false);
// Show the lists
LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
accordion->setVisible(true);
// Expand and show accordions if needed, else - hide them
getChild<LLAccordionCtrlTab>("tab_second_life_friends")->setVisible(mSecondLifeFriends->filterHasMatches());
getChild<LLAccordionCtrlTab>("tab_suggested_friends")->setVisible(mSuggestedFriends->filterHasMatches());
// Rearrange accordions
accordion->arrange();
}
}
void LLFacebookFriendsPanel::changed(U32 mask)
{
if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
{
LLFacebookConnect::instance().loadFacebookFriends();
updateFacebookList(true);
}
}
void LLFacebookFriendsPanel::updateFacebookList(bool visible)
{
if (visible)
{
// We want this to be called to fetch the friends list once a connection is established
LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookFriendsPanel");
LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookFriendsPanel", boost::bind(&LLFacebookFriendsPanel::onConnectedToFacebook, this, _1));
// We then want this to be called to update the displayed lists once the list of friends is received
LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLFacebookFriendsPanel"); // just in case it is already listening
LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLFacebookFriendsPanel", boost::bind(&LLFacebookFriendsPanel::updateSuggestedFriendList, this));
// Try to connect to Facebook
if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) ||
(LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED))
{
LLFacebookConnect::instance().checkConnectionToFacebook();
}
// Loads FB friends
if (LLFacebookConnect::instance().isConnected())
{
LLFacebookConnect::instance().loadFacebookFriends();
}
// Sort the FB friends and update the lists
updateSuggestedFriendList();
}
}
bool LLFacebookFriendsPanel::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;
}
////////////////////////
//LLFloaterFacebook///////
////////////////////////
LLFloaterFacebook::LLFloaterFacebook(const LLSD& key) : LLFloater(key),
mFacebookPhotoPanel(NULL),
mStatusErrorText(NULL),
mStatusLoadingText(NULL),
mStatusLoadingIndicator(NULL)
{
mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFacebook::onCancel, this));
}
void LLFloaterFacebook::onClose(bool app_quitting)
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
LLFloater::onClose(app_quitting);
}
void LLFloaterFacebook::onCancel()
{
LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
if (big_preview_floater)
{
big_preview_floater->closeOnFloaterOwnerClosing(this);
}
closeFloater();
}
BOOL LLFloaterFacebook::postBuild()
{
// Keep tab of the Photo Panel
mFacebookPhotoPanel = static_cast<LLFacebookPhotoPanel*>(getChild<LLUICtrl>("panel_facebook_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();
}
void LLFloaterFacebook::showPhotoPanel()
{
LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFacebookPhotoPanel->getParent());
if (!parent)
{
LL_WARNS() << "Cannot find panel container" << LL_ENDL;
return;
}
parent->selectTabPanel(mFacebookPhotoPanel);
}
void LLFloaterFacebook::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();
}