/** * @file llfloatertwitter.cpp * @brief Implementation of llfloatertwitter * @author cho@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 "llfloatertwitter.h" #include "llagent.h" #include "llagentui.h" #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lltwitterconnect.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 "lltexteditor.h" static LLRegisterPanelClassWrapper t_panel_photo("lltwitterphotopanel"); static LLRegisterPanelClassWrapper t_panel_account("lltwitteraccountpanel"); const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte const std::string DEFAULT_PHOTO_LOCATION_URL = "http://maps.secondlife.com/"; const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=twitter&utm_medium=photo&utm_campaign=slshare"; /////////////////////////// //LLTwitterPhotoPanel/////// /////////////////////////// LLTwitterPhotoPanel::LLTwitterPhotoPanel() : mSnapshotPanel(NULL), mResolutionComboBox(NULL), mRefreshBtn(NULL), mWorkingLabel(NULL), mThumbnailPlaceholder(NULL), mStatusCounterLabel(NULL), mStatusTextBox(NULL), mLocationCheckbox(NULL), mPhotoCheckbox(NULL), mPostButton(NULL) { mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLTwitterPhotoPanel::onSend, this)); mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLTwitterPhotoPanel::onClickNewSnapshot, this)); } LLTwitterPhotoPanel::~LLTwitterPhotoPanel() { if(mPreviewHandle.get()) { mPreviewHandle.get()->die(); } } BOOL LLTwitterPhotoPanel::postBuild() { setVisibleCallback(boost::bind(&LLTwitterPhotoPanel::onVisibilityChange, this, _2)); mSnapshotPanel = getChild("snapshot_panel"); mResolutionComboBox = getChild("resolution_combobox"); mResolutionComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE)); mFilterComboBox = getChild("filters_combobox"); mFilterComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE)); mRefreshBtn = getChild("new_snapshot_btn"); mWorkingLabel = getChild("working_lbl"); mThumbnailPlaceholder = getChild("thumbnail_placeholder"); mStatusCounterLabel = getChild("status_counter_label"); mStatusTextBox = getChild("photo_status"); mLocationCheckbox = getChild("add_location_cb"); mLocationCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddLocationToggled, this)); mPhotoCheckbox = getChild("add_photo_cb"); mPhotoCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddPhotoToggled, this)); mPostButton = getChild("post_photo_btn"); mCancelButton = getChild("cancel_photo_btn"); // Update filter list std::vector filter_list = LLImageFiltersManager::getInstance()->getFiltersList(); LLComboBox* filterbox = static_cast(mFilterComboBox); for (U32 i = 0; i < filter_list.size(); i++) { filterbox->add(filter_list[i]); } return LLPanel::postBuild(); } void LLTwitterPhotoPanel::draw() { LLSnapshotLivePreview * previewp = static_cast(mPreviewHandle.get()); // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts) bool no_ongoing_connection = !(LLTwitterConnect::instance().isTransactionOngoing()); bool photo_checked = mPhotoCheckbox->getValue().asBoolean(); mCancelButton->setEnabled(no_ongoing_connection); mStatusTextBox->setEnabled(no_ongoing_connection); mResolutionComboBox->setEnabled(no_ongoing_connection && photo_checked); mFilterComboBox->setEnabled(no_ongoing_connection && photo_checked); mRefreshBtn->setEnabled(no_ongoing_connection && photo_checked); mLocationCheckbox->setEnabled(no_ongoing_connection); mPhotoCheckbox->setEnabled(no_ongoing_connection); bool add_location = mLocationCheckbox->getValue().asBoolean(); bool add_photo = mPhotoCheckbox->getValue().asBoolean(); updateStatusTextLength(false); // 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()); gGL.matrixMode(LLRender::MM_MODELVIEW); // Apply floater transparency to the texture unless the floater is focused. F32 alpha = (add_photo ? (getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency()) : 0.5f); 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 && ((add_photo && previewp && previewp->getSnapshotUpToDate()) || add_location || !mStatusTextBox->getValue().asString().empty())); // Draw the rest of the panel on top of it LLPanel::draw(); } LLSnapshotLivePreview* LLTwitterPhotoPanel::getPreviewView() { LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get(); return previewp; } void LLTwitterPhotoPanel::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->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->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect()); updateControls(); } } } void LLTwitterPhotoPanel::onAddLocationToggled() { bool add_location = mLocationCheckbox->getValue().asBoolean(); updateStatusTextLength(!add_location); } void LLTwitterPhotoPanel::onAddPhotoToggled() { bool add_photo = mPhotoCheckbox->getValue().asBoolean(); updateStatusTextLength(!add_photo); } void LLTwitterPhotoPanel::onClickNewSnapshot() { LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) { previewp->updateSnapshot(TRUE); } } void LLTwitterPhotoPanel::onSend() { LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel"); // just in case it is already listening LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterPhotoPanel", boost::bind(&LLTwitterPhotoPanel::onTwitterConnectStateChange, this, _1)); // Connect to Twitter if necessary and then post if (LLTwitterConnect::instance().isConnected()) { sendPhoto(); } else { LLTwitterConnect::instance().checkConnectionToTwitter(true); } } bool LLTwitterPhotoPanel::onTwitterConnectStateChange(const LLSD& data) { switch (data.get("enum").asInteger()) { case LLTwitterConnect::TWITTER_CONNECTED: sendPhoto(); break; case LLTwitterConnect::TWITTER_POSTED: LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel"); clearAndClose(); break; } return false; } void LLTwitterPhotoPanel::sendPhoto() { // Get the status text std::string status = mStatusTextBox->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(); // 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_PHOTO_LOCATION_URL; } // Add query parameters so Google Analytics can track incoming clicks! slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS; // Add it to the status (pretty crude, but we don't have a better option with photos) if (status.empty()) status = slurl_string; else status = status + " " + slurl_string; } // Add the photo if required bool add_photo = mPhotoCheckbox->getValue().asBoolean(); if (add_photo) { // Get the image LLSnapshotLivePreview* previewp = getPreviewView(); // Post to Twitter LLTwitterConnect::instance().uploadPhoto(previewp->getFormattedImage(), status); } else { // Just post the status to Twitter LLTwitterConnect::instance().updateStatus(status); } updateControls(); } void LLTwitterPhotoPanel::clearAndClose() { mStatusTextBox->setValue(""); LLFloater* floater = getParentByType(); if (floater) { floater->closeFloater(); } } void LLTwitterPhotoPanel::updateStatusTextLength(BOOL restore_old_status_text) { bool add_location = mLocationCheckbox->getValue().asBoolean(); bool add_photo = mPhotoCheckbox->getValue().asBoolean(); // Restrict the status text length to Twitter's character limit LLTextEditor* status_text_box = dynamic_cast(mStatusTextBox); if (status_text_box) { int max_status_length = 140 - (add_location ? 40 : 0) - (add_photo ? 40 : 0); status_text_box->setMaxTextLength(max_status_length); if (restore_old_status_text) { if (mOldStatusText.length() > status_text_box->getText().length() && status_text_box->getText() == mOldStatusText.substr(0, status_text_box->getText().length())) { status_text_box->setText(mOldStatusText); } if (mOldStatusText.length() <= max_status_length) { mOldStatusText = ""; } } if (status_text_box->getText().length() > max_status_length) { if (mOldStatusText.length() < status_text_box->getText().length() || status_text_box->getText() != mOldStatusText.substr(0, status_text_box->getText().length())) { mOldStatusText = status_text_box->getText(); } status_text_box->setText(mOldStatusText.substr(0, max_status_length)); } // Update the status character counter int characters_remaining = max_status_length - status_text_box->getText().length(); mStatusCounterLabel->setValue(characters_remaining); } } void LLTwitterPhotoPanel::updateControls() { LLSnapshotLivePreview* previewp = getPreviewView(); BOOL got_snap = previewp && previewp->getSnapshotUpToDate(); // *TODO: Separate maximum size for Web images from postcards lldebugs << "Is snapshot up-to-date? " << got_snap << llendl; updateResolution(FALSE); } void LLTwitterPhotoPanel::updateResolution(BOOL do_update) { LLComboBox* combobox = static_cast(mResolutionComboBox); LLComboBox* filterbox = static_cast(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]; const std::string& filter_name = filterbox->getSimple(); LLSnapshotLivePreview * previewp = static_cast(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); 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(); } } } } void LLTwitterPhotoPanel::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* LLTwitterPhotoPanel::getRefreshBtn() { return mRefreshBtn; } /////////////////////////// //LLTwitterAccountPanel////// /////////////////////////// LLTwitterAccountPanel::LLTwitterAccountPanel() : mAccountCaptionLabel(NULL), mAccountNameLabel(NULL), mPanelButtons(NULL), mConnectButton(NULL), mDisconnectButton(NULL) { mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLTwitterAccountPanel::onConnect, this)); mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLTwitterAccountPanel::onDisconnect, this)); setVisibleCallback(boost::bind(&LLTwitterAccountPanel::onVisibilityChange, this, _2)); } BOOL LLTwitterAccountPanel::postBuild() { mAccountCaptionLabel = getChild("account_caption_label"); mAccountNameLabel = getChild("account_name_label"); mPanelButtons = getChild("panel_buttons"); mConnectButton = getChild("connect_btn"); mDisconnectButton = getChild("disconnect_btn"); return LLPanel::postBuild(); } void LLTwitterAccountPanel::draw() { LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState(); //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress bool disconnecting = connection_state == LLTwitterConnect::TWITTER_DISCONNECTING; mDisconnectButton->setEnabled(!disconnecting); //Disable the 'connect' button when a connection is in progress bool connecting = connection_state == LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS; mConnectButton->setEnabled(!connecting); LLPanel::draw(); } void LLTwitterAccountPanel::onVisibilityChange(const LLSD& new_visibility) { bool visible = new_visibility.asBoolean(); if(visible) { LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel"); LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectStateChange, this, _1)); LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel"); LLEventPumps::instance().obtain("TwitterConnectInfo").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectInfoChange, this)); //Connected if(LLTwitterConnect::instance().isConnected()) { showConnectedLayout(); } //Check if connected (show disconnected layout in meantime) else { showDisconnectedLayout(); } if ((LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_NOT_CONNECTED) || (LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_CONNECTION_FAILED)) { LLTwitterConnect::instance().checkConnectionToTwitter(); } } else { LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel"); LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel"); } } bool LLTwitterAccountPanel::onTwitterConnectStateChange(const LLSD& data) { if(LLTwitterConnect::instance().isConnected()) { //In process of disconnecting so leave the layout as is if(data.get("enum").asInteger() != LLTwitterConnect::TWITTER_DISCONNECTING) { showConnectedLayout(); } } else { showDisconnectedLayout(); } return false; } bool LLTwitterAccountPanel::onTwitterConnectInfoChange() { LLSD info = LLTwitterConnect::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 LLTwitterAccountPanel::showConnectButton() { if(!mConnectButton->getVisible()) { mConnectButton->setVisible(TRUE); mDisconnectButton->setVisible(FALSE); } } void LLTwitterAccountPanel::hideConnectButton() { if(mConnectButton->getVisible()) { mConnectButton->setVisible(FALSE); mDisconnectButton->setVisible(TRUE); } } void LLTwitterAccountPanel::showDisconnectedLayout() { mAccountCaptionLabel->setText(getString("twitter_disconnected")); mAccountNameLabel->setText(std::string("")); showConnectButton(); } void LLTwitterAccountPanel::showConnectedLayout() { LLTwitterConnect::instance().loadTwitterInfo(); mAccountCaptionLabel->setText(getString("twitter_connected")); hideConnectButton(); } void LLTwitterAccountPanel::onConnect() { LLTwitterConnect::instance().checkConnectionToTwitter(true); //Clear only the twitter browser cookies so that the twitter login screen appears LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com"); } void LLTwitterAccountPanel::onDisconnect() { LLTwitterConnect::instance().disconnectFromTwitter(); LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com"); } //////////////////////// //LLFloaterTwitter/////// //////////////////////// LLFloaterTwitter::LLFloaterTwitter(const LLSD& key) : LLFloater(key), mTwitterPhotoPanel(NULL), mStatusErrorText(NULL), mStatusLoadingText(NULL), mStatusLoadingIndicator(NULL) { mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterTwitter::onCancel, this)); } void LLFloaterTwitter::onCancel() { closeFloater(); } BOOL LLFloaterTwitter::postBuild() { // Keep tab of the Photo Panel mTwitterPhotoPanel = static_cast(getChild("panel_twitter_photo")); // Connection status widgets mStatusErrorText = getChild("connection_error_text"); mStatusLoadingText = getChild("connection_loading_text"); mStatusLoadingIndicator = getChild("connection_loading_indicator"); return LLFloater::postBuild(); } void LLFloaterTwitter::showPhotoPanel() { LLTabContainer* parent = dynamic_cast(mTwitterPhotoPanel->getParent()); if (!parent) { llwarns << "Cannot find panel container" << llendl; return; } parent->selectTabPanel(mTwitterPhotoPanel); } // static void LLFloaterTwitter::preUpdate() { LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance("twitter"); if (instance) { //Will set file size text to 'unknown' instance->mTwitterPhotoPanel->updateControls(); } } // static void LLFloaterTwitter::postUpdate() { LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance("twitter"); if (instance) { //Will set the file size text instance->mTwitterPhotoPanel->updateControls(); // The refresh button is initially hidden. We show it after the first update, // i.e. after snapshot is taken LLUICtrl * refresh_button = instance->mTwitterPhotoPanel->getRefreshBtn(); if (!refresh_button->getVisible()) { refresh_button->setVisible(true); } } } void LLFloaterTwitter::draw() { if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator) { mStatusErrorText->setVisible(false); mStatusLoadingText->setVisible(false); mStatusLoadingIndicator->setVisible(false); LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState(); std::string status_text; switch (connection_state) { case LLTwitterConnect::TWITTER_NOT_CONNECTED: // No status displayed when first opening the panel and no connection done case LLTwitterConnect::TWITTER_CONNECTED: // When successfully connected, no message is displayed case LLTwitterConnect::TWITTER_POSTED: // No success message to show since we actually close the floater after successful posting completion break; case LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS: // Connection loading indicator mStatusLoadingText->setVisible(true); status_text = LLTrans::getString("SocialTwitterConnecting"); mStatusLoadingText->setValue(status_text); mStatusLoadingIndicator->setVisible(true); break; case LLTwitterConnect::TWITTER_POSTING: // Posting indicator mStatusLoadingText->setVisible(true); status_text = LLTrans::getString("SocialTwitterPosting"); mStatusLoadingText->setValue(status_text); mStatusLoadingIndicator->setVisible(true); break; case LLTwitterConnect::TWITTER_CONNECTION_FAILED: // Error connecting to the service mStatusErrorText->setVisible(true); status_text = LLTrans::getString("SocialTwitterErrorConnecting"); mStatusErrorText->setValue(status_text); break; case LLTwitterConnect::TWITTER_POST_FAILED: // Error posting to the service mStatusErrorText->setVisible(true); status_text = LLTrans::getString("SocialTwitterErrorPosting"); mStatusErrorText->setValue(status_text); break; case LLTwitterConnect::TWITTER_DISCONNECTING: // Disconnecting loading indicator mStatusLoadingText->setVisible(true); status_text = LLTrans::getString("SocialTwitterDisconnecting"); mStatusLoadingText->setValue(status_text); mStatusLoadingIndicator->setVisible(true); break; case LLTwitterConnect::TWITTER_DISCONNECT_FAILED: // Error disconnecting from the service mStatusErrorText->setVisible(true); status_text = LLTrans::getString("SocialTwitterErrorDisconnecting"); mStatusErrorText->setValue(status_text); break; } } LLFloater::draw(); }