summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/newview/CMakeLists.txt13
-rwxr-xr-xindra/newview/app_settings/settings.xml55
-rw-r--r--indra/newview/llassetuploadresponders.cpp10
-rw-r--r--indra/newview/llfloaterpostcard.cpp384
-rw-r--r--indra/newview/llfloaterpostcard.h79
-rw-r--r--indra/newview/llfloatersnapshot.cpp1038
-rw-r--r--indra/newview/llfloatersnapshot.h29
-rw-r--r--indra/newview/llpanelsnapshot.cpp195
-rw-r--r--indra/newview/llpanelsnapshot.h69
-rw-r--r--indra/newview/llpanelsnapshotinventory.cpp109
-rw-r--r--indra/newview/llpanelsnapshotlocal.cpp154
-rw-r--r--indra/newview/llpanelsnapshotoptions.cpp104
-rw-r--r--indra/newview/llpanelsnapshotpostcard.cpp269
-rw-r--r--indra/newview/llpanelsnapshotprofile.cpp100
-rw-r--r--indra/newview/llpostcard.cpp155
-rw-r--r--indra/newview/llpostcard.h48
-rw-r--r--indra/newview/llsidetraypanelcontainer.cpp7
-rw-r--r--indra/newview/llsidetraypanelcontainer.h5
-rw-r--r--indra/newview/llviewerfloaterreg.cpp2
-rw-r--r--indra/newview/llviewermedia.cpp7
-rw-r--r--indra/newview/llviewermenufile.cpp18
-rw-r--r--indra/newview/llviewermessage.cpp4
-rw-r--r--indra/newview/llviewerwindow.cpp6
-rw-r--r--indra/newview/llviewerwindow.h2
-rw-r--r--indra/newview/llwebprofile.cpp305
-rw-r--r--indra/newview/llwebprofile.h69
-rw-r--r--indra/newview/skins/default/textures/snapshot_download.pngbin0 -> 1226 bytes
-rw-r--r--indra/newview/skins/default/textures/snapshot_email.pngbin0 -> 1231 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_snapshot.xml656
-rw-r--r--indra/newview/skins/default/xui/en/panel_postcard_message.xml (renamed from indra/newview/skins/default/xui/en/floater_postcard.xml)104
-rw-r--r--indra/newview/skins/default/xui/en/panel_postcard_settings.xml102
-rw-r--r--indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml146
-rw-r--r--indra/newview/skins/default/xui/en/panel_snapshot_local.xml194
-rw-r--r--indra/newview/skins/default/xui/en/panel_snapshot_options.xml148
-rw-r--r--indra/newview/skins/default/xui/en/panel_snapshot_postcard.xml107
-rw-r--r--indra/newview/skins/default/xui/en/panel_snapshot_profile.xml165
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml8
38 files changed, 3524 insertions, 1346 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index bef775cdb8..ff9cf3199e 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -219,7 +219,6 @@ set(viewer_SOURCE_FILES
llfloateropenobject.cpp
llfloaterpay.cpp
llfloaterperms.cpp
- llfloaterpostcard.cpp
llfloaterpostprocess.cpp
llfloaterpreference.cpp
llfloaterproperties.cpp
@@ -396,6 +395,12 @@ set(viewer_SOURCE_FILES
llpanelprimmediacontrols.cpp
llpanelprofile.cpp
llpanelprofileview.cpp
+ llpanelsnapshot.cpp
+ llpanelsnapshotinventory.cpp
+ llpanelsnapshotlocal.cpp
+ llpanelsnapshotoptions.cpp
+ llpanelsnapshotpostcard.cpp
+ llpanelsnapshotprofile.cpp
llpanelteleporthistory.cpp
llpaneltiptoast.cpp
llpanelvoiceeffect.cpp
@@ -414,6 +419,7 @@ set(viewer_SOURCE_FILES
llpopupview.cpp
llpolymesh.cpp
llpolymorph.cpp
+ llpostcard.cpp
llpreview.cpp
llpreviewanim.cpp
llpreviewgesture.cpp
@@ -603,6 +609,7 @@ set(viewer_SOURCE_FILES
llwearablelist.cpp
llwearabletype.cpp
llweb.cpp
+ llwebprofile.cpp
llwebsharing.cpp
llwind.cpp
llwindowlistener.cpp
@@ -786,7 +793,6 @@ set(viewer_HEADER_FILES
llfloateropenobject.h
llfloaterpay.h
llfloaterperms.h
- llfloaterpostcard.h
llfloaterpostprocess.h
llfloaterpreference.h
llfloaterproperties.h
@@ -957,6 +963,7 @@ set(viewer_HEADER_FILES
llpanelprimmediacontrols.h
llpanelprofile.h
llpanelprofileview.h
+ llpanelsnapshot.h
llpanelteleporthistory.h
llpaneltiptoast.h
llpanelvoicedevicesettings.h
@@ -975,6 +982,7 @@ set(viewer_HEADER_FILES
llpolymesh.h
llpolymorph.h
llpopupview.h
+ llpostcard.h
llpreview.h
llpreviewanim.h
llpreviewgesture.h
@@ -1164,6 +1172,7 @@ set(viewer_HEADER_FILES
llwearablelist.h
llwearabletype.h
llweb.h
+ llwebprofile.h
llwebsharing.h
llwind.h
llwindowlistener.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 5c0ea2f774..55e28cd60e 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1605,17 +1605,6 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>CloseSnapshotOnKeep</key>
- <map>
- <key>Comment</key>
- <string>Close snapshot window after saving snapshot</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
<key>CmdLineDisableVoice</key>
<map>
<key>Comment</key>
@@ -4667,6 +4656,17 @@
<string>0.0.0</string>
</map>
+ <key>LastSnapshotToProfileHeight</key>
+ <map>
+ <key>Comment</key>
+ <string>The height of the last profile snapshot, in px</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>768</integer>
+ </map>
<key>LastSnapshotToEmailHeight</key>
<map>
<key>Comment</key>
@@ -4678,6 +4678,17 @@
<key>Value</key>
<integer>768</integer>
</map>
+ <key>LastSnapshotToProfileWidth</key>
+ <map>
+ <key>Comment</key>
+ <string>The width of the last profile snapshot, in px</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1024</integer>
+ </map>
<key>LastSnapshotToEmailWidth</key>
<map>
<key>Comment</key>
@@ -4733,17 +4744,6 @@
<key>Value</key>
<integer>512</integer>
</map>
- <key>LastSnapshotType</key>
- <map>
- <key>Comment</key>
- <string>Select this as next type of snapshot to take (0 = postcard, 1 = texture, 2 = local image)</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>S32</string>
- <key>Value</key>
- <integer>0</integer>
- </map>
<key>LeftClickShowMenu</key>
<map>
<key>Comment</key>
@@ -10608,6 +10608,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SnapshotProfileLastResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Take next profile snapshot at this resolution</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>SnapshotPostcardLastResolution</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index 966f5b941e..40a4d665f8 100644
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -78,6 +78,8 @@ void on_new_single_inventory_upload_complete(
const LLSD& server_response,
S32 upload_price)
{
+ bool success = false;
+
if ( upload_price > 0 )
{
// this upload costed us L$, update our balance
@@ -152,6 +154,7 @@ void on_new_single_inventory_upload_complete(
gInventory.updateItem(item);
gInventory.notifyObservers();
+ success = true;
// Show the preview panel for textures and sounds to let
// user know that the image (or snapshot) arrived intact.
@@ -175,6 +178,13 @@ void on_new_single_inventory_upload_complete(
// remove the "Uploading..." message
LLUploadDialog::modalUploadFinished();
+
+ // Let the Snapshot floater know we have finished uploading a snapshot to inventory.
+ LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot");
+ if (asset_type == LLAssetType::AT_TEXTURE && floater_snapshot)
+ {
+ floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory")));
+ }
}
LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
deleted file mode 100644
index 3bcbb987f7..0000000000
--- a/indra/newview/llfloaterpostcard.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-/**
- * @file llfloaterpostcard.cpp
- * @brief Postcard send floater, allows setting name, e-mail address, etc.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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 "llfloaterpostcard.h"
-
-#include "llfontgl.h"
-#include "llsys.h"
-#include "llgl.h"
-#include "v3dmath.h"
-#include "lldir.h"
-
-#include "llagent.h"
-#include "llui.h"
-#include "lllineeditor.h"
-#include "llbutton.h"
-#include "lltexteditor.h"
-#include "llfloaterreg.h"
-#include "llnotificationsutil.h"
-#include "llviewercontrol.h"
-#include "llviewernetwork.h"
-#include "lluictrlfactory.h"
-#include "lluploaddialog.h"
-#include "llviewerstats.h"
-#include "llviewerwindow.h"
-#include "llstatusbar.h"
-#include "llviewerregion.h"
-#include "lleconomy.h"
-#include "message.h"
-
-#include "llimagejpeg.h"
-#include "llimagej2c.h"
-#include "llvfile.h"
-#include "llvfs.h"
-#include "llviewertexture.h"
-#include "llassetuploadresponders.h"
-#include "llagentui.h"
-
-#include <boost/regex.hpp> //boost.regex lib
-
-///----------------------------------------------------------------------------
-/// Local function declarations, constants, enums, and typedefs
-///----------------------------------------------------------------------------
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterPostcard
-///----------------------------------------------------------------------------
-
-LLFloaterPostcard::LLFloaterPostcard(const LLSD& key)
-: LLFloater(key),
- mJPEGImage(NULL),
- mViewerImage(NULL),
- mHasFirstMsgFocus(false)
-{
-}
-
-// Destroys the object
-LLFloaterPostcard::~LLFloaterPostcard()
-{
- mJPEGImage = NULL; // deletes image
-}
-
-BOOL LLFloaterPostcard::postBuild()
-{
- // pick up the user's up-to-date email address
- gAgent.sendAgentUserInfoRequest();
-
- childSetAction("cancel_btn", onClickCancel, this);
- childSetAction("send_btn", onClickSend, this);
-
- getChildView("from_form")->setEnabled(FALSE);
-
- std::string name_string;
- LLAgentUI::buildFullname(name_string);
- getChild<LLUICtrl>("name_form")->setValue(LLSD(name_string));
-
- // For the first time a user focusess to .the msg box, all text will be selected.
- getChild<LLUICtrl>("msg_form")->setFocusChangedCallback(boost::bind(onMsgFormFocusRecieved, _1, this));
-
- getChild<LLUICtrl>("to_form")->setFocus(TRUE);
-
- return TRUE;
-}
-
-// static
-LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLViewerTexture *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global)
-{
- // Take the images from the caller
- // It's now our job to clean them up
- LLFloaterPostcard* instance = LLFloaterReg::showTypedInstance<LLFloaterPostcard>("postcard", LLSD(img->getID()));
-
- if (instance) // may be 0 if we're in mouselook mode
- {
- instance->mJPEGImage = jpeg;
- instance->mViewerImage = img;
- instance->mImageScale = image_scale;
- instance->mPosTakenGlobal = pos_taken_global;
- }
-
- return instance;
-}
-
-void LLFloaterPostcard::draw()
-{
- LLGLSUIDefault gls_ui;
- LLFloater::draw();
-
- if(!isMinimized() && mViewerImage.notNull() && mJPEGImage.notNull())
- {
- // Force the texture to be 100% opaque when the floater is focused.
- F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
- LLRect rect(getRect());
-
- // first set the max extents of our preview
- rect.translate(-rect.mLeft, -rect.mBottom);
- rect.mLeft += 320;
- rect.mRight -= 10;
- rect.mTop -= 27;
- rect.mBottom = rect.mTop - 130;
-
- // then fix the aspect ratio
- F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
- if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
- {
- rect.mRight = LLRect::tCoordType((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
- }
- else
- {
- rect.mBottom = LLRect::tCoordType((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
- }
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f) % alpha);
- rect.stretch(-1);
- }
- {
-
- glMatrixMode(GL_TEXTURE);
- glPushMatrix();
- {
- glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
- glMatrixMode(GL_MODELVIEW);
- gl_draw_scaled_image(rect.mLeft,
- rect.mBottom,
- rect.getWidth(),
- rect.getHeight(),
- mViewerImage.get(),
- LLColor4::white % alpha);
- }
- glMatrixMode(GL_TEXTURE);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- }
- }
-}
-
-// static
-void LLFloaterPostcard::onClickCancel(void* data)
-{
- if (data)
- {
- LLFloaterPostcard *self = (LLFloaterPostcard *)data;
-
- self->closeFloater(false);
- }
-}
-
-class LLSendPostcardResponder : public LLAssetUploadResponder
-{
-public:
- LLSendPostcardResponder(const LLSD &post_data,
- const LLUUID& vfile_id,
- LLAssetType::EType asset_type):
- LLAssetUploadResponder(post_data, vfile_id, asset_type)
- {
- }
- // *TODO define custom uploadFailed here so it's not such a generic message
- void uploadComplete(const LLSD& content)
- {
- // we don't care about what the server returns from this post, just clean up the UI
- LLUploadDialog::modalUploadFinished();
- }
-};
-
-// static
-void LLFloaterPostcard::onClickSend(void* data)
-{
- if (data)
- {
- LLFloaterPostcard *self = (LLFloaterPostcard *)data;
-
- std::string from(self->getChild<LLUICtrl>("from_form")->getValue().asString());
- std::string to(self->getChild<LLUICtrl>("to_form")->getValue().asString());
-
- boost::regex emailFormat("[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(,[ \t]*[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})*");
-
- if (to.empty() || !boost::regex_match(to, emailFormat))
- {
- LLNotificationsUtil::add("PromptRecipientEmail");
- return;
- }
-
- if (from.empty() || !boost::regex_match(from, emailFormat))
- {
- LLNotificationsUtil::add("PromptSelfEmail");
- return;
- }
-
- std::string subject(self->getChild<LLUICtrl>("subject_form")->getValue().asString());
- if(subject.empty() || !self->mHasFirstMsgFocus)
- {
- LLNotificationsUtil::add("PromptMissingSubjMsg", LLSD(), LLSD(), boost::bind(&LLFloaterPostcard::missingSubjMsgAlertCallback, self, _1, _2));
- return;
- }
-
- if (self->mJPEGImage.notNull())
- {
- self->sendPostcard();
- }
- else
- {
- LLNotificationsUtil::add("ErrorProcessingSnapshot");
- }
- }
-}
-
-// static
-void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
-{
- LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;
-
- LLUploadDialog::modalUploadFinished();
-
- if (result)
- {
- LLSD args;
- args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
- LLNotificationsUtil::add("ErrorUploadingPostcard", args);
- }
- else
- {
- // only create the postcard once the upload succeeds
-
- // request the postcard
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("SendPostcard");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->addUUID("AssetID", self->mAssetID);
- msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
- msg->addString("To", self->getChild<LLUICtrl>("to_form")->getValue().asString());
- msg->addString("From", self->getChild<LLUICtrl>("from_form")->getValue().asString());
- msg->addString("Name", self->getChild<LLUICtrl>("name_form")->getValue().asString());
- msg->addString("Subject", self->getChild<LLUICtrl>("subject_form")->getValue().asString());
- msg->addString("Msg", self->getChild<LLUICtrl>("msg_form")->getValue().asString());
- msg->addBOOL("AllowPublish", FALSE);
- msg->addBOOL("MaturePublish", FALSE);
- gAgent.sendReliableMessage();
- }
-
- self->closeFloater();
-}
-
-// static
-void LLFloaterPostcard::updateUserInfo(const std::string& email)
-{
- LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("postcard");
- for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
- iter != inst_list.end(); ++iter)
- {
- LLFloater* instance = *iter;
- const std::string& text = instance->getChild<LLUICtrl>("from_form")->getValue().asString();
- if (text.empty())
- {
- // there's no text in this field yet, pre-populate
- instance->getChild<LLUICtrl>("from_form")->setValue(LLSD(email));
- }
- }
-}
-
-void LLFloaterPostcard::onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data)
-{
- LLFloaterPostcard* self = (LLFloaterPostcard *)data;
- if(self)
- {
- LLTextEditor* msgForm = self->getChild<LLTextEditor>("msg_form");
- if(msgForm && msgForm == receiver && msgForm->hasFocus() && !(self->mHasFirstMsgFocus))
- {
- self->mHasFirstMsgFocus = true;
- msgForm->setText(LLStringUtil::null);
- }
- }
-}
-
-bool LLFloaterPostcard::missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if(0 == option)
- {
- // User clicked OK
- if((getChild<LLUICtrl>("subject_form")->getValue().asString()).empty())
- {
- // Stuff the subject back into the form.
- getChild<LLUICtrl>("subject_form")->setValue(getString("default_subject"));
- }
-
- if(!mHasFirstMsgFocus)
- {
- // The user never switched focus to the messagee window.
- // Using the default string.
- getChild<LLUICtrl>("msg_form")->setValue(getString("default_message"));
- }
-
- sendPostcard();
- }
- return false;
-}
-
-void LLFloaterPostcard::sendPostcard()
-{
- mTransactionID.generate();
- mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
- LLVFile::writeFile(mJPEGImage->getData(), mJPEGImage->getDataSize(), gVFS, mAssetID, LLAssetType::AT_IMAGE_JPEG);
-
- // upload the image
- std::string url = gAgent.getRegion()->getCapability("SendPostcard");
- if(!url.empty())
- {
- llinfos << "Send Postcard via capability" << llendl;
- LLSD body = LLSD::emptyMap();
- // the capability already encodes: agent ID, region ID
- body["pos-global"] = mPosTakenGlobal.getValue();
- body["to"] = getChild<LLUICtrl>("to_form")->getValue().asString();
- body["from"] = getChild<LLUICtrl>("from_form")->getValue().asString();
- body["name"] = getChild<LLUICtrl>("name_form")->getValue().asString();
- body["subject"] = getChild<LLUICtrl>("subject_form")->getValue().asString();
- body["msg"] = getChild<LLUICtrl>("msg_form")->getValue().asString();
- LLHTTPClient::post(url, body, new LLSendPostcardResponder(body, mAssetID, LLAssetType::AT_IMAGE_JPEG));
- }
- else
- {
- gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE);
- }
-
- // give user feedback of the event
- gViewerWindow->playSnapshotAnimAndSound();
- LLUploadDialog::modalUploadDialog(getString("upload_message"));
-
- // don't destroy the window until the upload is done
- // this way we keep the information in the form
- setVisible(FALSE);
-
- // also remove any dependency on another floater
- // so that we can be sure to outlive it while we
- // need to.
- LLFloater* dependee = getDependee();
- if (dependee)
- dependee->removeDependentFloater(this);
-}
diff --git a/indra/newview/llfloaterpostcard.h b/indra/newview/llfloaterpostcard.h
deleted file mode 100644
index 472592154f..0000000000
--- a/indra/newview/llfloaterpostcard.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @file llfloaterpostcard.h
- * @brief Postcard send floater, allows setting name, e-mail address, etc.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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_LLFLOATERPOSTCARD_H
-#define LL_LLFLOATERPOSTCARD_H
-
-#include "llfloater.h"
-#include "llcheckboxctrl.h"
-
-#include "llpointer.h"
-
-class LLTextEditor;
-class LLLineEditor;
-class LLButton;
-class LLViewerTexture;
-class LLImageJPEG;
-
-class LLFloaterPostcard
-: public LLFloater
-{
-public:
- LLFloaterPostcard(const LLSD& key);
- virtual ~LLFloaterPostcard();
-
- virtual BOOL postBuild();
- virtual void draw();
-
- static LLFloaterPostcard* showFromSnapshot(LLImageJPEG *jpeg, LLViewerTexture *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
-
- static void onClickCancel(void* data);
- static void onClickSend(void* data);
-
- static void uploadCallback(const LLUUID& asset_id,
- void *user_data,
- S32 result, LLExtStat ext_status);
-
- static void updateUserInfo(const std::string& email);
-
- static void onMsgFormFocusRecieved(LLFocusableElement* receiver, void* data);
- bool missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response);
-
- void sendPostcard();
-
-private:
-
- LLPointer<LLImageJPEG> mJPEGImage;
- LLPointer<LLViewerTexture> mViewerImage;
- LLTransactionID mTransactionID;
- LLAssetID mAssetID;
- LLVector2 mImageScale;
- LLVector3d mPosTakenGlobal;
- bool mHasFirstMsgFocus;
-};
-
-
-#endif // LL_LLFLOATERPOSTCARD_H
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 8105844b0d..48e6cca623 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -42,6 +42,8 @@
#include "llcombobox.h"
#include "lleconomy.h"
#include "lllandmarkactions.h"
+#include "llpanelsnapshot.h"
+#include "llsidetraypanelcontainer.h"
#include "llsliderctrl.h"
#include "llspinctrl.h"
#include "llviewercontrol.h"
@@ -50,9 +52,7 @@
#include "llviewercamera.h"
#include "llviewerwindow.h"
#include "llviewermenufile.h" // upload_new_resource()
-#include "llfloaterpostcard.h"
#include "llcheckboxctrl.h"
-#include "llradiogroup.h"
#include "llslurl.h"
#include "lltoolfocus.h"
#include "lltoolmgr.h"
@@ -76,18 +76,17 @@
#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"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
-S32 LLFloaterSnapshot::sUIWinHeightLong = 530 ;
-S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 240 ;
-S32 LLFloaterSnapshot::sUIWinWidth = 215 ;
-
+LLUICtrl* LLFloaterSnapshot::sThumbnailPlaceholder = NULL;
LLSnapshotFloaterView* gSnapshotFloaterView = NULL;
const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f;
@@ -101,6 +100,9 @@ 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 std::string lastSnapshotWidthName(S32 shot_type);
+static std::string lastSnapshotHeightName(S32 shot_type);
+
static LLDefaultChildRegistry::Register<LLSnapshotFloaterView> r("snapshot_floater_view");
///----------------------------------------------------------------------------
@@ -108,6 +110,7 @@ static LLDefaultChildRegistry::Register<LLSnapshotFloaterView> r("snapshot_float
///----------------------------------------------------------------------------
class LLSnapshotLivePreview : public LLView
{
+ LOG_CLASS(LLSnapshotLivePreview);
public:
enum ESnapshotType
{
@@ -154,6 +157,7 @@ public:
F32 getAspect() ;
LLRect getImageRect();
BOOL isImageScaled();
+ const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
@@ -161,10 +165,12 @@ public:
void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
void saveWeb();
- LLFloaterPostcard* savePostcard();
void saveTexture();
BOOL saveLocal();
+ LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
+ LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
+
BOOL setThumbnailImageSize() ;
void generateThumbnailImage(BOOL force_update = FALSE) ;
void resetThumbnailImage() { mThumbnailImage = NULL ; }
@@ -327,7 +333,8 @@ BOOL LLSnapshotLivePreview::isImageScaled()
}
void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
-{
+{
+ lldebugs << "updateSnapshot: mSnapshotUpToDate = " << mSnapshotUpToDate << llendl;
if (mSnapshotUpToDate)
{
S32 old_image_index = mCurImageIndex;
@@ -367,6 +374,7 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail
{
mSnapshotDelayTimer.start();
mSnapshotDelayTimer.setTimerExpirySec(delay);
+ LLFloaterSnapshot::preUpdate();
}
if(new_thumbnail)
{
@@ -629,8 +637,10 @@ BOOL LLSnapshotLivePreview::setThumbnailImageSize()
F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
// UI size for thumbnail
- S32 max_width = LLFloaterSnapshot::getUIWinWidth() - 20;
- S32 max_height = 90;
+ // *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)
{
@@ -746,7 +756,15 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
//static
BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
{
- LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+
+#if 1 // XXX tmp
+ if (previewp->mWidth[previewp->mCurImageIndex] == 0 || previewp->mHeight[previewp->mCurImageIndex] == 0)
+ {
+ llwarns << "Incorrect dimensions: " << previewp->mWidth[previewp->mCurImageIndex] << "x" << previewp->mHeight[previewp->mCurImageIndex] << llendl;
+ return FALSE;
+ }
+#endif
LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
@@ -774,6 +792,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
// time to produce a snapshot
+ lldebugs << "producing snapshot" << llendl;
if (!previewp->mPreviewImage)
{
previewp->mPreviewImage = new LLImageRaw;
@@ -809,6 +828,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
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(),
@@ -829,18 +849,8 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
// delete any existing image
previewp->mFormattedImage = NULL;
// now create the new one of the appropriate format.
- // note: postcards and web hardcoded to use jpeg always.
- LLFloaterSnapshot::ESnapshotFormat format;
-
- if (previewp->getSnapshotType() == SNAPSHOT_POSTCARD ||
- previewp->getSnapshotType() == SNAPSHOT_WEB)
- {
- format = LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG;
- }
- else
- {
- format = previewp->getSnapshotFormat();
- }
+ LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
+ lldebugs << "Encoding new image of format " << format << llendl;
switch(format)
{
@@ -920,12 +930,15 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
{
previewp->generateThumbnailImage() ;
}
+ lldebugs << "done creating snapshot" << llendl;
+ LLFloaterSnapshot::postUpdate();
return TRUE;
}
void LLSnapshotLivePreview::setSize(S32 w, S32 h)
{
+ lldebugs << "setSize(" << w << ", " << h << ")" << llendl;
mWidth[mCurImageIndex] = w;
mHeight[mCurImageIndex] = h;
}
@@ -936,40 +949,9 @@ void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
h = mHeight[mCurImageIndex];
}
-LLFloaterPostcard* LLSnapshotLivePreview::savePostcard()
-{
- if(mViewerImage[mCurImageIndex].isNull())
- {
- //this should never happen!!
- llwarns << "The snapshot image has not been generated!" << llendl ;
- return NULL ;
- }
-
- // calculate and pass in image scale in case image data only use portion
- // of viewerimage buffer
- LLVector2 image_scale(1.f, 1.f);
- if (!isImageScaled())
- {
- image_scale.setVec(llmin(1.f, (F32)mWidth[mCurImageIndex] / (F32)getCurrentImage()->getWidth()), llmin(1.f, (F32)mHeight[mCurImageIndex] / (F32)getCurrentImage()->getHeight()));
- }
-
- LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get());
- if(!jpg)
- {
- llwarns << "Formatted image not a JPEG" << llendl;
- return NULL;
- }
- LLFloaterPostcard* floater = LLFloaterPostcard::showFromSnapshot(jpg, mViewerImage[mCurImageIndex], image_scale, mPosTakenGlobal);
- // relinquish lifetime of jpeg image to postcard floater
- mFormattedImage = NULL;
- mDataSize = 0;
- updateSnapshot(FALSE, FALSE);
-
- return floater;
-}
-
void LLSnapshotLivePreview::saveTexture()
{
+ lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl;
// gen a new uuid for this asset
LLTransactionID tid;
tid.generate();
@@ -982,6 +964,7 @@ void LLSnapshotLivePreview::saveTexture()
mPreviewImage->getComponents());
scaled->biasedScaleToPowerOfTwo(512);
+ lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl;
if (formatted->encode(scaled, 0.0f))
{
@@ -1020,9 +1003,10 @@ void LLSnapshotLivePreview::saveTexture()
BOOL LLSnapshotLivePreview::saveLocal()
{
- BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
+ BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage, true);
// Relinquish image memory. Save button will be disabled as a side-effect.
+ lldebugs << "resetting formatted image after saving to disk" << llendl;
mFormattedImage = NULL;
mDataSize = 0;
updateSnapshot(FALSE, FALSE);
@@ -1067,11 +1051,20 @@ void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& meta
class LLFloaterSnapshot::Impl
{
+ LOG_CLASS(LLFloaterSnapshot::Impl);
public:
+ typedef enum e_status
+ {
+ STATUS_READY,
+ STATUS_WORKING,
+ STATUS_FINISHED
+ } EStatus;
+
Impl()
: mAvatarPauseHandles(),
mLastToolset(NULL),
- mAspectRatioCheckOff(false)
+ mAspectRatioCheckOff(false),
+ mStatus(STATUS_READY)
{
}
~Impl()
@@ -1080,43 +1073,55 @@ public:
mAvatarPauseHandles.clear();
}
- static void onClickDiscard(void* data);
- static void onClickKeep(void* data);
- static void onCommitSave(LLUICtrl* ctrl, void* data);
static void onClickNewSnapshot(void* data);
static void onClickAutoSnap(LLUICtrl *ctrl, void* data);
//static void onClickAdvanceSnap(LLUICtrl *ctrl, void* data);
- static void onClickLess(void* data) ;
static void onClickMore(void* data) ;
static void onClickUICheck(LLUICtrl *ctrl, void* data);
static void onClickHUDCheck(LLUICtrl *ctrl, void* data);
- static void onClickKeepOpenCheck(LLUICtrl *ctrl, void* data);
+#if 0
static void onClickKeepAspectCheck(LLUICtrl *ctrl, void* data);
- static void onCommitQuality(LLUICtrl* ctrl, void* data);
+#endif
+ static void applyKeepAspectCheck(LLFloaterSnapshot* view, BOOL checked);
static void onCommitResolution(LLUICtrl* ctrl, void* data) { updateResolution(ctrl, data); }
static void updateResolution(LLUICtrl* ctrl, void* data, BOOL do_update = TRUE);
static void onCommitFreezeFrame(LLUICtrl* ctrl, void* data);
static void onCommitLayerTypes(LLUICtrl* ctrl, void*data);
+ static void onImageQualityChange(LLFloaterSnapshot* view, S32 quality_val);
+ static void onImageFormatChange(LLFloaterSnapshot* view);
+#if 0
static void onCommitSnapshotType(LLUICtrl* ctrl, void* data);
- static void onCommitSnapshotFormat(LLUICtrl* ctrl, void* data);
static void onCommitCustomResolution(LLUICtrl *ctrl, void* data);
+#endif
+ static void applyCustomResolution(LLFloaterSnapshot* view, S32 w, S32 h);
+ static void onSnapshotUploadFinished(bool status);
+ static void onSendingPostcardFinished(bool status);
static void resetSnapshotSizeOnUI(LLFloaterSnapshot *view, S32 width, S32 height) ;
static BOOL checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, BOOL isWidthChanged, S32 max_value);
+ static LLPanelSnapshot* getActivePanel(LLFloaterSnapshot* floater, bool ok_if_not_found = true);
+ static LLSnapshotLivePreview::ESnapshotType getActiveSnapshotType(LLFloaterSnapshot* floater);
+ static LLFloaterSnapshot::ESnapshotFormat getImageFormat(LLFloaterSnapshot* floater);
+ static LLSpinCtrl* getWidthSpinner(LLFloaterSnapshot* floater);
+ static LLSpinCtrl* getHeightSpinner(LLFloaterSnapshot* floater);
+ static void enableAspectRatioCheckbox(LLFloaterSnapshot* floater, BOOL enable);
+ static void setAspectRatioCheckboxValue(LLFloaterSnapshot* floater, BOOL checked);
+
static LLSnapshotLivePreview* getPreviewView(LLFloaterSnapshot *floater);
static void setResolution(LLFloaterSnapshot* floater, const std::string& comboname);
static void updateControls(LLFloaterSnapshot* floater);
static void updateLayout(LLFloaterSnapshot* floater);
- static void updateResolutionTextEntry(LLFloaterSnapshot* floater);
+ static void setStatus(EStatus status, bool ok = true, const std::string& msg = LLStringUtil::null);
+ EStatus getStatus() const { return mStatus; }
private:
- static LLSnapshotLivePreview::ESnapshotType getTypeIndex(LLFloaterSnapshot* floater);
- static LLSD getTypeName(LLSnapshotLivePreview::ESnapshotType index);
- static ESnapshotFormat getFormatIndex(LLFloaterSnapshot* floater);
static LLViewerWindow::ESnapshotType getLayerType(LLFloaterSnapshot* floater);
static void comboSetCustom(LLFloaterSnapshot *floater, const std::string& comboname);
static void checkAutoSnapshot(LLSnapshotLivePreview* floater, BOOL update_thumbnail = FALSE);
static void checkAspectRatio(LLFloaterSnapshot *view, S32 index) ;
+ static void setWorking(LLFloaterSnapshot* floater, bool working);
+ static void setFinished(LLFloaterSnapshot* floater, bool finished, bool ok = true, const std::string& msg = LLStringUtil::null);
+
public:
std::vector<LLAnimPauseRequest> mAvatarPauseHandles;
@@ -1124,84 +1129,97 @@ public:
LLToolset* mLastToolset;
LLHandle<LLView> mPreviewHandle;
bool mAspectRatioCheckOff ;
+ EStatus mStatus;
};
// static
-LLSnapshotLivePreview* LLFloaterSnapshot::Impl::getPreviewView(LLFloaterSnapshot *floater)
+LLPanelSnapshot* LLFloaterSnapshot::Impl::getActivePanel(LLFloaterSnapshot* floater, bool ok_if_not_found)
{
- LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)floater->impl.mPreviewHandle.get();
- return previewp;
+ LLSideTrayPanelContainer* panel_container = floater->getChild<LLSideTrayPanelContainer>("panel_container");
+ LLPanelSnapshot* active_panel = dynamic_cast<LLPanelSnapshot*>(panel_container->getCurrentPanel());
+ if (!ok_if_not_found)
+ {
+ llassert_always(active_panel != NULL);
+ }
+ return active_panel;
}
// static
-LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getTypeIndex(LLFloaterSnapshot* floater)
+LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getActiveSnapshotType(LLFloaterSnapshot* floater)
{
- LLSnapshotLivePreview::ESnapshotType index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
- LLSD value = floater->getChild<LLUICtrl>("snapshot_type_radio")->getValue();
+ LLSnapshotLivePreview::ESnapshotType type = LLSnapshotLivePreview::SNAPSHOT_WEB;
+ std::string name;
+ LLPanelSnapshot* spanel = getActivePanel(floater);
- const std::string id = value.asString();
- if (id == "postcard")
+ if (spanel)
{
- index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
+ name = spanel->getName();
}
- else if (id == "texture")
+
+ if (name == "panel_snapshot_postcard")
{
- index = LLSnapshotLivePreview::SNAPSHOT_TEXTURE;
+ type = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
}
- else if (id == "local")
+ else if (name == "panel_snapshot_inventory")
{
- index = LLSnapshotLivePreview::SNAPSHOT_LOCAL;
+ type = LLSnapshotLivePreview::SNAPSHOT_TEXTURE;
}
- else if (id == "share_to_web")
+ else if (name == "panel_snapshot_local")
{
- index = LLSnapshotLivePreview::SNAPSHOT_WEB;
+ type = LLSnapshotLivePreview::SNAPSHOT_LOCAL;
}
- return index;
+ return type;
}
// static
-LLSD LLFloaterSnapshot::Impl::getTypeName(LLSnapshotLivePreview::ESnapshotType index)
+LLFloaterSnapshot::ESnapshotFormat LLFloaterSnapshot::Impl::getImageFormat(LLFloaterSnapshot* floater)
{
- std::string id;
- switch (index)
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ // FIXME: if the default is not PNG, profile uploads may fail.
+ return active_panel ? active_panel->getImageFormat() : LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG;
+}
+
+// static
+LLSpinCtrl* LLFloaterSnapshot::Impl::getWidthSpinner(LLFloaterSnapshot* floater)
+{
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ return active_panel ? active_panel->getWidthSpinner() : floater->getChild<LLSpinCtrl>("snapshot_width");
+}
+
+// static
+LLSpinCtrl* LLFloaterSnapshot::Impl::getHeightSpinner(LLFloaterSnapshot* floater)
+{
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ return active_panel ? active_panel->getHeightSpinner() : floater->getChild<LLSpinCtrl>("snapshot_height");
+}
+
+// static
+void LLFloaterSnapshot::Impl::enableAspectRatioCheckbox(LLFloaterSnapshot* floater, BOOL enable)
+{
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ if (active_panel)
{
- case LLSnapshotLivePreview::SNAPSHOT_WEB:
- id = "share_to_web";
- break;
- case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
- id = "postcard";
- break;
- case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
- id = "texture";
- break;
- case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
- default:
- id = "local";
- break;
+ active_panel->enableAspectRatioCheckbox(enable);
}
- return LLSD(id);
}
// static
-LLFloaterSnapshot::ESnapshotFormat LLFloaterSnapshot::Impl::getFormatIndex(LLFloaterSnapshot* floater)
+void LLFloaterSnapshot::Impl::setAspectRatioCheckboxValue(LLFloaterSnapshot* floater, BOOL checked)
{
- ESnapshotFormat index = SNAPSHOT_FORMAT_PNG;
- if(floater->hasChild("local_format_combo"))
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ if (active_panel)
{
- LLComboBox* local_format_combo = floater->findChild<LLComboBox>("local_format_combo");
- const std::string id = local_format_combo->getSelectedItemLabel();
- if (id == "PNG")
- index = SNAPSHOT_FORMAT_PNG;
- else if (id == "JPEG")
- index = SNAPSHOT_FORMAT_JPEG;
- else if (id == "BMP")
- index = SNAPSHOT_FORMAT_BMP;
+ active_panel->getChild<LLUICtrl>(active_panel->getAspectRatioCBName())->setValue(checked);
}
- return index;
}
-
+// static
+LLSnapshotLivePreview* LLFloaterSnapshot::Impl::getPreviewView(LLFloaterSnapshot *floater)
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)floater->impl.mPreviewHandle.get();
+ return previewp;
+}
// static
LLViewerWindow::ESnapshotType LLFloaterSnapshot::Impl::getLayerType(LLFloaterSnapshot* floater)
@@ -1229,12 +1247,27 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
{
LLSnapshotLivePreview* previewp = getPreviewView(floaterp);
- S32 delta_height = gSavedSettings.getBOOL("AdvanceSnapshot") ? 0 : floaterp->getUIWinHeightShort() - floaterp->getUIWinHeightLong() ;
+ BOOL advanced = gSavedSettings.getBOOL("AdvanceSnapshot");
+
+ // Show/hide advanced options.
+ LLPanel* advanced_options_panel = floaterp->getChild<LLPanel>("advanced_options_panel");
+ floaterp->getChild<LLButton>("advanced_options_btn")->setToggleState(advanced);
+ if (advanced != advanced_options_panel->getVisible())
+ {
+ S32 panel_width = advanced_options_panel->getRect().getWidth();
+ floaterp->getChild<LLPanel>("advanced_options_panel")->setVisible(advanced);
+ S32 floater_width = floaterp->getRect().getWidth();
+ floater_width += (advanced ? panel_width : -panel_width);
+ floaterp->reshape(floater_width, floaterp->getRect().getHeight());
+ }
- if(!gSavedSettings.getBOOL("AdvanceSnapshot")) //set to original window resolution
+ if(!advanced) //set to original window resolution
{
previewp->mKeepAspectRatio = TRUE;
+ floaterp->getChild<LLComboBox>("profile_size_combo")->setCurrentByIndex(0);
+ gSavedSettings.setS32("SnapshotProfileLastResolution", 0);
+
floaterp->getChild<LLComboBox>("postcard_size_combo")->setCurrentByIndex(0);
gSavedSettings.setS32("SnapshotPostcardLastResolution", 0);
@@ -1256,7 +1289,8 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
floaterp->getParent()->setMouseOpaque(TRUE);
// shrink to smaller layout
- floaterp->reshape(floaterp->getRect().getWidth(), floaterp->getUIWinHeightLong() + delta_height);
+ // *TODO: unneeded?
+ floaterp->reshape(floaterp->getRect().getWidth(), floaterp->getRect().getHeight());
// can see and interact with fullscreen preview now
if (previewp)
@@ -1286,7 +1320,8 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
else // turning off freeze frame mode
{
floaterp->getParent()->setMouseOpaque(FALSE);
- floaterp->reshape(floaterp->getRect().getWidth(), floaterp->getUIWinHeightLong() + delta_height);
+ // *TODO: unneeded?
+ floaterp->reshape(floaterp->getRect().getWidth(), floaterp->getRect().getHeight());
if (previewp)
{
previewp->setVisible(FALSE);
@@ -1315,83 +1350,74 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
// static
void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
{
- LLRadioGroup* snapshot_type_radio = floater->getChild<LLRadioGroup>("snapshot_type_radio");
- LLSnapshotLivePreview::ESnapshotType shot_type = (LLSnapshotLivePreview::ESnapshotType)gSavedSettings.getS32("LastSnapshotType");
- snapshot_type_radio->setSelectedByValue(getTypeName(shot_type), true);
-
+ LLSnapshotLivePreview::ESnapshotType shot_type = getActiveSnapshotType(floater);
ESnapshotFormat shot_format = (ESnapshotFormat)gSavedSettings.getS32("SnapshotFormat");
LLViewerWindow::ESnapshotType layer_type = getLayerType(floater);
+#if 0
floater->getChildView("share_to_web")->setVisible( gSavedSettings.getBOOL("SnapshotSharingEnabled"));
+#endif
+#if 0
floater->getChildView("postcard_size_combo")->setVisible( FALSE);
floater->getChildView("texture_size_combo")->setVisible( FALSE);
floater->getChildView("local_size_combo")->setVisible( FALSE);
+#endif
+ floater->getChild<LLComboBox>("profile_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotProfileLastResolution"));
floater->getChild<LLComboBox>("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution"));
floater->getChild<LLComboBox>("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution"));
floater->getChild<LLComboBox>("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution"));
floater->getChild<LLComboBox>("local_format_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFormat"));
// *TODO: Separate settings for Web images from postcards
- floater->getChildView("send_btn")->setVisible( shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
- shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB);
- floater->getChildView("upload_btn")->setVisible(shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE);
- floater->getChildView("save_btn")->setVisible( shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);
- floater->getChildView("keep_aspect_check")->setEnabled(shot_type != LLSnapshotLivePreview::SNAPSHOT_TEXTURE && !floater->impl.mAspectRatioCheckOff);
+ enableAspectRatioCheckbox(floater, !floater->impl.mAspectRatioCheckOff);
+ setAspectRatioCheckboxValue(floater, gSavedSettings.getBOOL("KeepAspectForSnapshot"));
floater->getChildView("layer_types")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);
- BOOL is_advance = gSavedSettings.getBOOL("AdvanceSnapshot");
- BOOL is_local = shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL;
- BOOL show_slider = (shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
- shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB ||
- (is_local && shot_format == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG));
-
- floater->getChildView("more_btn")->setVisible( !is_advance); // the only item hidden in advanced mode
- floater->getChildView("less_btn")->setVisible( is_advance);
- floater->getChildView("type_label2")->setVisible( is_advance);
- floater->getChildView("format_label")->setVisible( is_advance && is_local);
- floater->getChildView("local_format_combo")->setVisible( is_advance && is_local);
- floater->getChildView("layer_types")->setVisible( is_advance);
- floater->getChildView("layer_type_label")->setVisible( is_advance);
- floater->getChildView("snapshot_width")->setVisible( is_advance);
- floater->getChildView("snapshot_height")->setVisible( is_advance);
- floater->getChildView("keep_aspect_check")->setVisible( is_advance);
- floater->getChildView("ui_check")->setVisible( is_advance);
- floater->getChildView("hud_check")->setVisible( is_advance);
- floater->getChildView("keep_open_check")->setVisible( is_advance);
- floater->getChildView("freeze_frame_check")->setVisible( is_advance);
- floater->getChildView("auto_snapshot_check")->setVisible( is_advance);
- floater->getChildView("image_quality_slider")->setVisible( is_advance && show_slider);
-
- if (gSavedSettings.getBOOL("RenderUIInSnapshot") || gSavedSettings.getBOOL("RenderHUDInSnapshot"))
- { //clamp snapshot resolution to window size when showing UI or HUD in snapshot
-
- LLSpinCtrl* width_ctrl = floater->getChild<LLSpinCtrl>("snapshot_width");
- LLSpinCtrl* height_ctrl = floater->getChild<LLSpinCtrl>("snapshot_height");
-
- S32 width = gViewerWindow->getWindowWidthRaw();
- S32 height = gViewerWindow->getWindowHeightRaw();
-
- width_ctrl->setMaxValue(width);
-
- height_ctrl->setMaxValue(height);
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ if (active_panel)
+ {
+ LLSpinCtrl* width_ctrl = getWidthSpinner(floater);
+ LLSpinCtrl* height_ctrl = getHeightSpinner(floater);
- if (width_ctrl->getValue().asInteger() > width)
+ // Initialize spinners.
+ if (width_ctrl->getValue().asInteger() == 0)
{
- width_ctrl->forceSetValue(width);
+ S32 w = gSavedSettings.getS32(lastSnapshotWidthName(shot_type));
+ lldebugs << "Initializing width spinner (" << width_ctrl->getName() << "): " << w << llendl;
+ width_ctrl->setValue(w);
}
- if (height_ctrl->getValue().asInteger() > height)
+ if (height_ctrl->getValue().asInteger() == 0)
{
- height_ctrl->forceSetValue(height);
+ S32 h = gSavedSettings.getS32(lastSnapshotHeightName(shot_type));
+ lldebugs << "Initializing height spinner (" << height_ctrl->getName() << "): " << h << llendl;
+ height_ctrl->setValue(h);
+ }
+
+ if (gSavedSettings.getBOOL("RenderUIInSnapshot") || gSavedSettings.getBOOL("RenderHUDInSnapshot"))
+ { //clamp snapshot resolution to window size when showing UI or HUD in snapshot
+ S32 width = gViewerWindow->getWindowWidthRaw();
+ S32 height = gViewerWindow->getWindowHeightRaw();
+
+ width_ctrl->setMaxValue(width);
+
+ height_ctrl->setMaxValue(height);
+
+ if (width_ctrl->getValue().asInteger() > width)
+ {
+ width_ctrl->forceSetValue(width);
+ }
+ if (height_ctrl->getValue().asInteger() > height)
+ {
+ height_ctrl->forceSetValue(height);
+ }
+ }
+ else
+ {
+ width_ctrl->setMaxValue(6016);
+ height_ctrl->setMaxValue(6016);
}
- }
- else
- {
- LLSpinCtrl* width = floater->getChild<LLSpinCtrl>("snapshot_width");
- width->setMaxValue(6016);
- LLSpinCtrl* height = floater->getChild<LLSpinCtrl>("snapshot_height");
- height->setMaxValue(6016);
}
LLSnapshotLivePreview* previewp = getPreviewView(floater);
@@ -1399,11 +1425,7 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
// *TODO: Separate maximum size for Web images from postcards
- floater->getChildView("send_btn")->setEnabled((shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
- shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB) &&
- got_snap && previewp->getDataSize() <= MAX_POSTCARD_DATASIZE);
- floater->getChildView("upload_btn")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE && got_snap);
- floater->getChildView("save_btn")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL && got_snap);
+ //lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
LLLocale locale(LLLocale::USER_LOCALE);
std::string bytes_string;
@@ -1411,9 +1433,17 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
{
LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 );
}
- S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
- floater->getChild<LLUICtrl>("texture")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost));
- floater->getChild<LLUICtrl>("upload_btn")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost));
+
+ // Update displayed image resolution.
+ LLTextBox* image_res_tb = floater->getChild<LLTextBox>("image_res_text");
+ image_res_tb->setVisible(got_snap);
+ if (got_snap)
+ {
+ LLPointer<LLImageRaw> img = previewp->getEncodedImage();
+ image_res_tb->setTextArg("[WIDTH]", llformat("%d", img->getWidth()));
+ image_res_tb->setTextArg("[HEIGHT]", llformat("%d", img->getHeight()));
+ }
+
floater->getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown"));
floater->getChild<LLUICtrl>("file_size_label")->setColor(
shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
@@ -1422,144 +1452,75 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
switch(shot_type)
{
- // *TODO: Separate settings for Web images from postcards
case LLSnapshotLivePreview::SNAPSHOT_WEB:
+ layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ floater->getChild<LLUICtrl>("layer_types")->setValue("colors");
+ setResolution(floater, "profile_size_combo");
+ break;
case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
floater->getChild<LLUICtrl>("layer_types")->setValue("colors");
- if(is_advance)
- {
- setResolution(floater, "postcard_size_combo");
- }
+ setResolution(floater, "postcard_size_combo");
break;
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
floater->getChild<LLUICtrl>("layer_types")->setValue("colors");
- if(is_advance)
- {
- setResolution(floater, "texture_size_combo");
- }
+ setResolution(floater, "texture_size_combo");
break;
case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
- if(is_advance)
- {
- setResolution(floater, "local_size_combo");
- }
+ setResolution(floater, "local_size_combo");
break;
default:
break;
}
- updateResolutionTextEntry(floater);
-
if (previewp)
{
+ lldebugs << "Setting snapshot type (" << shot_type << "), format (" << shot_format << ")" << llendl;
previewp->setSnapshotType(shot_type);
previewp->setSnapshotFormat(shot_format);
previewp->setSnapshotBufferType(layer_type);
}
-}
-
-// static
-void LLFloaterSnapshot::Impl::updateResolutionTextEntry(LLFloaterSnapshot* floater)
-{
- LLSpinCtrl* width_spinner = floater->getChild<LLSpinCtrl>("snapshot_width");
- LLSpinCtrl* height_spinner = floater->getChild<LLSpinCtrl>("snapshot_height");
- if(getTypeIndex(floater) == LLSnapshotLivePreview::SNAPSHOT_TEXTURE)
+ LLPanelSnapshot* current_panel = Impl::getActivePanel(floater);
+ if (current_panel)
{
- width_spinner->setAllowEdit(FALSE);
- height_spinner->setAllowEdit(FALSE);
- }
- else
- {
- width_spinner->setAllowEdit(TRUE);
- height_spinner->setAllowEdit(TRUE);
+ LLSD info;
+ info["have-snapshot"] = got_snap;
+ current_panel->updateControls(info);
}
}
// static
-void LLFloaterSnapshot::Impl::checkAutoSnapshot(LLSnapshotLivePreview* previewp, BOOL update_thumbnail)
+void LLFloaterSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg)
{
- if (previewp)
+ LLFloaterSnapshot* floater = LLFloaterSnapshot::getInstance();
+ switch (status)
{
- BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
- previewp->updateSnapshot(autosnap, update_thumbnail, autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f);
- }
-}
-
-// static
-void LLFloaterSnapshot::Impl::onClickDiscard(void* data)
-{
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
-
- if (view)
- {
- view->closeFloater();
+ case STATUS_READY:
+ setWorking(floater, false);
+ setFinished(floater, false);
+ break;
+ case STATUS_WORKING:
+ setWorking(floater, true);
+ setFinished(floater, false);
+ break;
+ case STATUS_FINISHED:
+ setWorking(floater, false);
+ setFinished(floater, true, ok, msg);
+ break;
}
-}
-
-// static
-void LLFloaterSnapshot::Impl::onCommitSave(LLUICtrl* ctrl, void* data)
-{
- if (ctrl->getValue().asString() == "save as")
- {
- gViewerWindow->resetSnapshotLoc();
- }
- onClickKeep(data);
+ floater->impl.mStatus = status;
}
// static
-void LLFloaterSnapshot::Impl::onClickKeep(void* data)
+void LLFloaterSnapshot::Impl::checkAutoSnapshot(LLSnapshotLivePreview* previewp, BOOL update_thumbnail)
{
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
- LLSnapshotLivePreview* previewp = getPreviewView(view);
-
if (previewp)
{
- switch (previewp->getSnapshotType())
- {
- case LLSnapshotLivePreview::SNAPSHOT_WEB:
- previewp->saveWeb();
- break;
-
- case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
- {
- LLFloaterPostcard* floater = previewp->savePostcard();
- // if still in snapshot mode, put postcard floater in snapshot floaterview
- // and link it to snapshot floater
- if (floater && !gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
- {
- gFloaterView->removeChild(floater);
- gSnapshotFloaterView->addChild(floater);
- view->addDependentFloater(floater, FALSE);
- }
- }
- break;
-
- case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
- previewp->saveTexture();
- break;
-
- case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
- previewp->saveLocal();
- break;
-
- default:
- break;
- }
-
- if (gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
- {
- view->closeFloater();
- }
- else
- {
- checkAutoSnapshot(previewp);
- }
-
- updateControls(view);
+ BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
+ previewp->updateSnapshot(autosnap, update_thumbnail, autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f);
}
}
@@ -1590,32 +1551,19 @@ void LLFloaterSnapshot::Impl::onClickAutoSnap(LLUICtrl *ctrl, void* data)
void LLFloaterSnapshot::Impl::onClickMore(void* data)
{
- gSavedSettings.setBOOL( "AdvanceSnapshot", TRUE );
+ BOOL visible = gSavedSettings.getBOOL("AdvanceSnapshot");
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
if (view)
{
+ gSavedSettings.setBOOL("AdvanceSnapshot", !visible);
+#if 0
view->translate( 0, view->getUIWinHeightShort() - view->getUIWinHeightLong() );
view->reshape(view->getRect().getWidth(), view->getUIWinHeightLong());
+#endif
updateControls(view) ;
updateLayout(view) ;
- if(getPreviewView(view))
- {
- getPreviewView(view)->setThumbnailImageSize() ;
- }
- }
-}
-void LLFloaterSnapshot::Impl::onClickLess(void* data)
-{
- gSavedSettings.setBOOL( "AdvanceSnapshot", FALSE );
-
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
- if (view)
- {
- view->translate( 0, view->getUIWinHeightLong() - view->getUIWinHeightShort() );
- view->reshape(view->getRect().getWidth(), view->getUIWinHeightShort());
- updateControls(view) ;
- updateLayout(view) ;
+ // *TODO: redundant?
if(getPreviewView(view))
{
getPreviewView(view)->setThumbnailImageSize() ;
@@ -1651,21 +1599,21 @@ void LLFloaterSnapshot::Impl::onClickHUDCheck(LLUICtrl *ctrl, void* data)
}
}
+#if 0
// static
-void LLFloaterSnapshot::Impl::onClickKeepOpenCheck(LLUICtrl* ctrl, void* data)
+void LLFloaterSnapshot::Impl::onClickKeepAspectCheck(LLUICtrl* ctrl, void* data)
{
LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
-
- gSavedSettings.setBOOL( "CloseSnapshotOnKeep", !check->get() );
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ applyKeepAspectCheck(view, check->get());
}
+#endif
// static
-void LLFloaterSnapshot::Impl::onClickKeepAspectCheck(LLUICtrl* ctrl, void* data)
+void LLFloaterSnapshot::Impl::applyKeepAspectCheck(LLFloaterSnapshot* view, BOOL checked)
{
- LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
- gSavedSettings.setBOOL( "KeepAspectForSnapshot", check->get() );
-
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ gSavedSettings.setBOOL("KeepAspectForSnapshot", checked);
+
if (view)
{
LLSnapshotLivePreview* previewp = getPreviewView(view) ;
@@ -1688,20 +1636,6 @@ void LLFloaterSnapshot::Impl::onClickKeepAspectCheck(LLUICtrl* ctrl, void* data)
}
// static
-void LLFloaterSnapshot::Impl::onCommitQuality(LLUICtrl* ctrl, void* data)
-{
- LLSliderCtrl* slider = (LLSliderCtrl*)ctrl;
- S32 quality_val = llfloor((F32)slider->getValue().asReal());
-
- LLSnapshotLivePreview* previewp = getPreviewView((LLFloaterSnapshot *)data);
- if (previewp)
- {
- previewp->setSnapshotQuality(quality_val);
- }
- checkAutoSnapshot(previewp, TRUE);
-}
-
-// static
void LLFloaterSnapshot::Impl::onCommitFreezeFrame(LLUICtrl* ctrl, void* data)
{
LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
@@ -1723,18 +1657,16 @@ void LLFloaterSnapshot::Impl::checkAspectRatio(LLFloaterSnapshot *view, S32 inde
LLSnapshotLivePreview *previewp = getPreviewView(view) ;
// Don't round texture sizes; textures are commonly stretched in world, profiles, etc and need to be "squashed" during upload, not cropped here
-#if 0
- if(LLSnapshotLivePreview::SNAPSHOT_TEXTURE == getTypeIndex(view))
+ if(LLSnapshotLivePreview::SNAPSHOT_TEXTURE == getActiveSnapshotType(view))
{
previewp->mKeepAspectRatio = FALSE ;
return ;
}
-#endif
if(0 == index) //current window size
{
view->impl.mAspectRatioCheckOff = true ;
- view->getChildView("keep_aspect_check")->setEnabled(FALSE) ;
+ enableAspectRatioCheckbox(view, FALSE);
if(previewp)
{
@@ -1744,20 +1676,17 @@ void LLFloaterSnapshot::Impl::checkAspectRatio(LLFloaterSnapshot *view, S32 inde
else if(-1 == index) //custom
{
view->impl.mAspectRatioCheckOff = false ;
- //if(LLSnapshotLivePreview::SNAPSHOT_TEXTURE != gSavedSettings.getS32("LastSnapshotType"))
- {
- view->getChildView("keep_aspect_check")->setEnabled(TRUE) ;
+ enableAspectRatioCheckbox(view, TRUE);
- if(previewp)
- {
- previewp->mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
- }
+ if(previewp)
+ {
+ previewp->mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
}
}
else
{
view->impl.mAspectRatioCheckOff = true ;
- view->getChildView("keep_aspect_check")->setEnabled(FALSE) ;
+ enableAspectRatioCheckbox(view, FALSE);
if(previewp)
{
@@ -1768,23 +1697,63 @@ void LLFloaterSnapshot::Impl::checkAspectRatio(LLFloaterSnapshot *view, S32 inde
return ;
}
-static std::string lastSnapshotWidthName()
+// static
+void LLFloaterSnapshot::Impl::setWorking(LLFloaterSnapshot* floater, bool working)
+{
+ LLUICtrl* working_lbl = floater->getChild<LLUICtrl>("working_lbl");
+ working_lbl->setVisible(working);
+ floater->getChild<LLUICtrl>("working_indicator")->setVisible(working);
+
+ if (working)
+ {
+ const std::string panel_name = getActivePanel(floater, false)->getName();
+ const std::string prefix = panel_name.substr(std::string("panel_snapshot_").size());
+ std::string progress_text = floater->getString(prefix + "_" + "progress_str");
+ working_lbl->setValue(progress_text);
+ }
+
+ // All controls should be disable while posting.
+ floater->setCtrlsEnabled(!working);
+ LLPanelSnapshot* active_panel = getActivePanel(floater);
+ if (active_panel)
+ {
+ active_panel->setCtrlsEnabled(!working);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::setFinished(LLFloaterSnapshot* floater, bool finished, bool ok, const std::string& msg)
{
- switch(gSavedSettings.getS32("LastSnapshotType"))
+ floater->getChild<LLUICtrl>("succeeded_panel")->setVisible(finished && ok);
+ floater->getChild<LLUICtrl>("failed_panel")->setVisible(finished && !ok);
+
+ if (finished)
{
- // *TODO: Separate settings for Web snapshots and postcards
- case LLSnapshotLivePreview::SNAPSHOT_WEB: return "LastSnapshotToEmailWidth";
+ LLUICtrl* finished_lbl = floater->getChild<LLUICtrl>(ok ? "succeeded_lbl" : "failed_lbl");
+ std::string result_text = floater->getString(msg + "_" + (ok ? "succeeded_str" : "failed_str"));
+ finished_lbl->setValue(result_text);
+
+ LLSideTrayPanelContainer* panel_container = floater->getChild<LLSideTrayPanelContainer>("panel_container");
+ panel_container->openPreviousPanel();
+ panel_container->getCurrentPanel()->onOpen(LLSD());
+ }
+}
+
+static std::string lastSnapshotWidthName(S32 shot_type)
+{
+ switch (shot_type)
+ {
+ case LLSnapshotLivePreview::SNAPSHOT_WEB: return "LastSnapshotToProfileWidth";
case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailWidth";
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: return "LastSnapshotToInventoryWidth";
default: return "LastSnapshotToDiskWidth";
}
}
-static std::string lastSnapshotHeightName()
+static std::string lastSnapshotHeightName(S32 shot_type)
{
- switch(gSavedSettings.getS32("LastSnapshotType"))
+ switch (shot_type)
{
- // *TODO: Separate settings for Web snapshots and postcards
- case LLSnapshotLivePreview::SNAPSHOT_WEB: return "LastSnapshotToEmailHeight";
+ case LLSnapshotLivePreview::SNAPSHOT_WEB: return "LastSnapshotToProfileHeight";
case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailHeight";
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: return "LastSnapshotToInventoryHeight";
default: return "LastSnapshotToDiskHeight";
@@ -1799,10 +1768,12 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, BOOL
if (!view || !combobox)
{
+ llassert(view && combobox);
return;
}
// save off all selected resolution values
+ gSavedSettings.setS32("SnapshotProfileLastResolution", view->getChild<LLComboBox>("profile_size_combo")->getCurrentIndex());
gSavedSettings.setS32("SnapshotPostcardLastResolution", view->getChild<LLComboBox>("postcard_size_combo")->getCurrentIndex());
gSavedSettings.setS32("SnapshotTextureLastResolution", view->getChild<LLComboBox>("texture_size_combo")->getCurrentIndex());
gSavedSettings.setS32("SnapshotLocalLastResolution", view->getChild<LLComboBox>("local_size_combo")->getCurrentIndex());
@@ -1824,16 +1795,45 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, BOOL
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 if (width == -1 || height == -1)
{
// load last custom value
- previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName()));
+#if 1
+ S32 new_width = 0, new_height = 0;
+ LLPanelSnapshot* spanel = getActivePanel(view);
+ if (spanel)
+ {
+ lldebugs << "Loading typed res from panel " << spanel->getName() << llendl;
+ new_width = spanel->getTypedPreviewWidth();
+ new_height = spanel->getTypedPreviewHeight();
+ }
+ else
+ {
+ const S32 shot_type = getActiveSnapshotType(view);
+ lldebugs << "Loading saved res for shot_type " << shot_type << llendl;
+ new_width = gSavedSettings.getS32(lastSnapshotWidthName(shot_type));
+ new_height = gSavedSettings.getS32(lastSnapshotHeightName(shot_type));
+ }
+
+ llassert(new_width > 0 && new_height > 0);
+ previewp->setSize(new_width, new_height);
+#else
+ LLPanelSnapshot* spanel = getActivePanel(view);
+ if (spanel)
+ {
+ lldebugs << "Setting custom preview res : " << spanel->getTypedPreviewWidth() << "x" << spanel->getTypedPreviewHeight() << llendl;
+ previewp->setSize(spanel->getTypedPreviewWidth(), spanel->getTypedPreviewHeight());
+ }
+ //previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName()));
+#endif
}
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);
}
@@ -1853,10 +1853,10 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, BOOL
resetSnapshotSizeOnUI(view, width, height) ;
}
- if(view->getChild<LLUICtrl>("snapshot_width")->getValue().asInteger() != width || view->getChild<LLUICtrl>("snapshot_height")->getValue().asInteger() != height)
+ if(getWidthSpinner(view)->getValue().asInteger() != width || getHeightSpinner(view)->getValue().asInteger() != height)
{
- view->getChild<LLUICtrl>("snapshot_width")->setValue(width);
- view->getChild<LLUICtrl>("snapshot_height")->setValue(height);
+ getWidthSpinner(view)->setValue(width);
+ getHeightSpinner(view)->setValue(height);
}
if(original_width != width || original_height != height)
@@ -1892,32 +1892,41 @@ void LLFloaterSnapshot::Impl::onCommitLayerTypes(LLUICtrl* ctrl, void*data)
}
}
-//static
-void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)
+// static
+void LLFloaterSnapshot::Impl::onImageQualityChange(LLFloaterSnapshot* view, S32 quality_val)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ previewp->setSnapshotQuality(quality_val);
+ }
+ checkAutoSnapshot(previewp, TRUE);
+}
+
+// static
+void LLFloaterSnapshot::Impl::onImageFormatChange(LLFloaterSnapshot* view)
{
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
if (view)
{
- gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view));
+ gSavedSettings.setS32("SnapshotFormat", getImageFormat(view));
getPreviewView(view)->updateSnapshot(TRUE);
updateControls(view);
}
}
-
+#if 0
//static
-void LLFloaterSnapshot::Impl::onCommitSnapshotFormat(LLUICtrl* ctrl, void* data)
+void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)
{
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
if (view)
{
- gSavedSettings.setS32("SnapshotFormat", getFormatIndex(view));
+ gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view));
getPreviewView(view)->updateSnapshot(TRUE);
updateControls(view);
}
}
-
-
+#endif
// Sets the named size combo to "custom" mode.
// static
@@ -1931,6 +1940,10 @@ void LLFloaterSnapshot::Impl::comboSetCustom(LLFloaterSnapshot* floater, const s
{
gSavedSettings.setS32("SnapshotPostcardLastResolution", combo->getCurrentIndex());
}
+ else if(comboname == "profile_size_combo")
+ {
+ gSavedSettings.setS32("SnapshotProfileLastResolution", combo->getCurrentIndex());
+ }
else if(comboname == "texture_size_combo")
{
gSavedSettings.setS32("SnapshotTextureLastResolution", combo->getCurrentIndex());
@@ -2027,21 +2040,29 @@ BOOL LLFloaterSnapshot::Impl::checkImageSize(LLSnapshotLivePreview* previewp, S3
//static
void LLFloaterSnapshot::Impl::resetSnapshotSizeOnUI(LLFloaterSnapshot *view, S32 width, S32 height)
{
- view->getChild<LLSpinCtrl>("snapshot_width")->forceSetValue(width);
- view->getChild<LLSpinCtrl>("snapshot_height")->forceSetValue(height);
- gSavedSettings.setS32(lastSnapshotWidthName(), width);
- gSavedSettings.setS32(lastSnapshotHeightName(), height);
+ getWidthSpinner(view)->forceSetValue(width);
+ getHeightSpinner(view)->forceSetValue(height);
+ gSavedSettings.setS32(lastSnapshotWidthName(getActiveSnapshotType(view)), width);
+ gSavedSettings.setS32(lastSnapshotHeightName(getActiveSnapshotType(view)), height);
}
+#if 0
//static
void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* data)
{
- LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ S32 w = llfloor((F32)getWidthSpinner(view)->getValue().asReal());
+ S32 h = llfloor((F32)getHeightSpinner(view)->getValue().asReal());
+ applyCustomResolution(view, w, h);
+}
+#endif
+
+// static
+void LLFloaterSnapshot::Impl::applyCustomResolution(LLFloaterSnapshot* view, S32 w, S32 h)
+{
+ lldebugs << "applyCustomResolution(" << w << ", " << h << ")" << llendl;
if (view)
{
- S32 w = llfloor((F32)view->getChild<LLUICtrl>("snapshot_width")->getValue().asReal());
- S32 h = llfloor((F32)view->getChild<LLUICtrl>("snapshot_height")->getValue().asReal());
-
LLSnapshotLivePreview* previewp = getPreviewView(view);
if (previewp)
{
@@ -2073,7 +2094,7 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat
}
}
#endif
- previewp->setMaxImageSize((S32)((LLSpinCtrl *)ctrl)->getMaxValue()) ;
+ previewp->setMaxImageSize((S32) getWidthSpinner(view)->getMaxValue()) ;
// Check image size changes the value of height and width
if(checkImageSize(previewp, w, h, w != curw, previewp->getMaxImageSize())
@@ -2085,19 +2106,33 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat
previewp->setSize(w,h);
checkAutoSnapshot(previewp, FALSE);
previewp->updateSnapshot(FALSE, TRUE);
+ comboSetCustom(view, "profile_size_combo");
comboSetCustom(view, "postcard_size_combo");
comboSetCustom(view, "texture_size_combo");
comboSetCustom(view, "local_size_combo");
}
}
- gSavedSettings.setS32(lastSnapshotWidthName(), w);
- gSavedSettings.setS32(lastSnapshotHeightName(), h);
+ gSavedSettings.setS32(lastSnapshotWidthName(getActiveSnapshotType(view)), w);
+ gSavedSettings.setS32(lastSnapshotHeightName(getActiveSnapshotType(view)), h);
updateControls(view);
}
}
+// static
+void LLFloaterSnapshot::Impl::onSnapshotUploadFinished(bool status)
+{
+ setStatus(STATUS_FINISHED, status, "profile");
+}
+
+
+// static
+void LLFloaterSnapshot::Impl::onSendingPostcardFinished(bool status)
+{
+ setStatus(STATUS_FINISHED, status, "postcard");
+}
+
///----------------------------------------------------------------------------
/// Class LLFloaterSnapshot
///----------------------------------------------------------------------------
@@ -2134,24 +2169,18 @@ BOOL LLFloaterSnapshot::postBuild()
LLWebSharing::instance().init();
}
+#if 0
childSetCommitCallback("snapshot_type_radio", Impl::onCommitSnapshotType, this);
- childSetCommitCallback("local_format_combo", Impl::onCommitSnapshotFormat, this);
+#endif
childSetAction("new_snapshot_btn", Impl::onClickNewSnapshot, this);
- childSetAction("more_btn", Impl::onClickMore, this);
- childSetAction("less_btn", Impl::onClickLess, this);
-
- childSetAction("upload_btn", Impl::onClickKeep, this);
- childSetAction("send_btn", Impl::onClickKeep, this);
- childSetCommitCallback("save_btn", Impl::onCommitSave, this);
- childSetAction("discard_btn", Impl::onClickDiscard, this);
-
- childSetCommitCallback("image_quality_slider", Impl::onCommitQuality, this);
- getChild<LLUICtrl>("image_quality_slider")->setValue(gSavedSettings.getS32("SnapshotQuality"));
+ childSetAction("advanced_options_btn", Impl::onClickMore, this);
+#if 0
childSetCommitCallback("snapshot_width", Impl::onCommitCustomResolution, this);
childSetCommitCallback("snapshot_height", Impl::onCommitCustomResolution, this);
+#endif
childSetCommitCallback("ui_check", Impl::onClickUICheck, this);
getChild<LLUICtrl>("ui_check")->setValue(gSavedSettings.getBOOL("RenderUIInSnapshot"));
@@ -2159,18 +2188,19 @@ BOOL LLFloaterSnapshot::postBuild()
childSetCommitCallback("hud_check", Impl::onClickHUDCheck, this);
getChild<LLUICtrl>("hud_check")->setValue(gSavedSettings.getBOOL("RenderHUDInSnapshot"));
- childSetCommitCallback("keep_open_check", Impl::onClickKeepOpenCheck, this);
- getChild<LLUICtrl>("keep_open_check")->setValue(!gSavedSettings.getBOOL("CloseSnapshotOnKeep"));
-
+#if 0
childSetCommitCallback("keep_aspect_check", Impl::onClickKeepAspectCheck, this);
- getChild<LLUICtrl>("keep_aspect_check")->setValue(gSavedSettings.getBOOL("KeepAspectForSnapshot"));
+#endif
+ impl.setAspectRatioCheckboxValue(this, gSavedSettings.getBOOL("KeepAspectForSnapshot"));
childSetCommitCallback("layer_types", Impl::onCommitLayerTypes, this);
getChild<LLUICtrl>("layer_types")->setValue("colors");
getChildView("layer_types")->setEnabled(FALSE);
- getChild<LLUICtrl>("snapshot_width")->setValue(gSavedSettings.getS32(lastSnapshotWidthName()));
- getChild<LLUICtrl>("snapshot_height")->setValue(gSavedSettings.getS32(lastSnapshotHeightName()));
+#if 0 // leads to crash later if one of the settings values is 0
+ impl.getWidthSpinner(this)->setValue(gSavedSettings.getS32(lastSnapshotWidthName()));
+ impl.getHeightSpinner(this)->setValue(gSavedSettings.getS32(lastSnapshotHeightName()));
+#endif
getChild<LLUICtrl>("freeze_frame_check")->setValue(gSavedSettings.getBOOL("UseFreezeFrame"));
childSetCommitCallback("freeze_frame_check", Impl::onCommitFreezeFrame, this);
@@ -2178,10 +2208,16 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot"));
childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
+ childSetCommitCallback("profile_size_combo", Impl::onCommitResolution, this);
childSetCommitCallback("postcard_size_combo", Impl::onCommitResolution, this);
childSetCommitCallback("texture_size_combo", Impl::onCommitResolution, this);
childSetCommitCallback("local_size_combo", Impl::onCommitResolution, this);
+ LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1));
+ LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1));
+
+ sThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+
// create preview window
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
@@ -2221,19 +2257,30 @@ void LLFloaterSnapshot::draw()
{
if(previewp->getThumbnailImage())
{
- LLRect thumbnail_rect = getChild<LLUICtrl>("thumbnail_placeholder")->getRect();
-
- S32 offset_x = (getRect().getWidth() - previewp->getThumbnailWidth()) / 2 ;
+ bool working = impl.getStatus() == Impl::STATUS_WORKING;
+ const LLRect& thumbnail_rect = getThumbnailPlaceholderRect();
+ S32 offset_x = thumbnail_rect.mLeft + (thumbnail_rect.getWidth() - previewp->getThumbnailWidth()) / 2 ;
S32 offset_y = thumbnail_rect.mBottom + (thumbnail_rect.getHeight() - previewp->getThumbnailHeight()) / 2 ;
glMatrixMode(GL_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = working ? LLColor4::grey4 : LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y,
previewp->getThumbnailWidth(), previewp->getThumbnailHeight(),
- previewp->getThumbnailImage(), LLColor4::white % alpha);
+ previewp->getThumbnailImage(), color % alpha);
previewp->drawPreviewRect(offset_x, offset_y) ;
+
+ // Draw progress indicators on top of the preview.
+ if (working)
+ {
+ gGL.pushUIMatrix();
+ const LLRect& r = getThumbnailPlaceholderRect();
+ LLUI::translate((F32) r.mLeft, (F32) r.mBottom);
+ sThumbnailPlaceholder->draw();
+ gGL.popUIMatrix();
+ }
}
}
}
@@ -2249,6 +2296,9 @@ void LLFloaterSnapshot::onOpen(const LLSD& key)
gSnapshotFloaterView->setEnabled(TRUE);
gSnapshotFloaterView->setVisible(TRUE);
gSnapshotFloaterView->adjustToFitScreen(this, FALSE);
+
+ // Initialize default tab.
+ getChild<LLSideTrayPanelContainer>("panel_container")->getCurrentPanel()->onOpen(LLSD());
}
void LLFloaterSnapshot::onClose(bool app_quitting)
@@ -2256,6 +2306,62 @@ void LLFloaterSnapshot::onClose(bool app_quitting)
getParent()->setMouseOpaque(FALSE);
}
+// virtual
+S32 LLFloaterSnapshot::notify(const LLSD& info)
+{
+ // A child panel wants to change snapshot resolution.
+ if (info.has("combo-res-change"))
+ {
+ std::string combo_name = info["combo-res-change"]["control-name"].asString();
+ impl.updateResolution(getChild<LLUICtrl>(combo_name), this);
+ return 1;
+ }
+
+ if (info.has("custom-res-change"))
+ {
+ LLSD res = info["custom-res-change"];
+ impl.applyCustomResolution(this, res["w"].asInteger(), res["h"].asInteger());
+ return 1;
+ }
+
+ if (info.has("keep-aspect-change"))
+ {
+ impl.applyKeepAspectCheck(this, info["keep-aspect-change"].asBoolean());
+ return 1;
+ }
+
+ if (info.has("image-quality-change"))
+ {
+ impl.onImageQualityChange(this, info["image-quality-change"].asInteger());
+ return 1;
+ }
+
+ if (info.has("image-format-change"))
+ {
+ impl.onImageFormatChange(this);
+ return 1;
+ }
+
+ if (info.has("set-ready"))
+ {
+ impl.setStatus(Impl::STATUS_READY);
+ return 1;
+ }
+
+ if (info.has("set-working"))
+ {
+ impl.setStatus(Impl::STATUS_WORKING);
+ return 1;
+ }
+
+ if (info.has("set-finished"))
+ {
+ LLSD data = info["set-finished"];
+ impl.setStatus(Impl::STATUS_FINISHED, data["ok"].asBoolean(), data["msg"].asString());
+ return 1;
+ }
+ return 0;
+}
//static
void LLFloaterSnapshot::update()
@@ -2276,6 +2382,160 @@ void LLFloaterSnapshot::update()
}
}
+// static
+LLFloaterSnapshot* LLFloaterSnapshot::getInstance()
+{
+ return LLFloaterReg::getTypedInstance<LLFloaterSnapshot>("snapshot");
+}
+
+// static
+void LLFloaterSnapshot::saveTexture()
+{
+ lldebugs << "saveTexture" << llendl;
+
+ // FIXME: duplicated code
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (!instance)
+ {
+ llassert(instance != NULL);
+ return;
+ }
+ LLSnapshotLivePreview* previewp = Impl::getPreviewView(instance);
+ if (!previewp)
+ {
+ llassert(previewp != NULL);
+ return;
+ }
+
+ previewp->saveTexture();
+}
+
+// static
+void LLFloaterSnapshot::saveLocal()
+{
+ lldebugs << "saveLocal" << llendl;
+ // FIXME: duplicated code
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (!instance)
+ {
+ llassert(instance != NULL);
+ return;
+ }
+ LLSnapshotLivePreview* previewp = Impl::getPreviewView(instance);
+ if (!previewp)
+ {
+ llassert(previewp != NULL);
+ return;
+ }
+
+ previewp->saveLocal();
+}
+
+// static
+void LLFloaterSnapshot::preUpdate()
+{
+ // FIXME: duplicated code
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (instance)
+ {
+ // Disable the send/post/save buttons until snapshot is ready.
+ Impl::updateControls(instance);
+ }
+}
+
+// static
+void LLFloaterSnapshot::postUpdate()
+{
+ // FIXME: duplicated code
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (instance)
+ {
+ // Enable the send/post/save buttons.
+ Impl::updateControls(instance);
+ }
+}
+
+// static
+void LLFloaterSnapshot::postSave()
+{
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (!instance)
+ {
+ llassert(instance != NULL);
+ return;
+ }
+
+ instance->impl.updateControls(instance);
+ instance->impl.setStatus(Impl::STATUS_WORKING);
+}
+
+// static
+void LLFloaterSnapshot::postPanelSwitch()
+{
+ LLFloaterSnapshot* instance = getInstance();
+ instance->impl.updateControls(instance);
+}
+
+// static
+LLPointer<LLImageFormatted> LLFloaterSnapshot::getImageData()
+{
+ // FIXME: May not work for textures.
+
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (!instance)
+ {
+ llassert(instance != NULL);
+ return NULL;
+ }
+
+ LLSnapshotLivePreview* previewp = Impl::getPreviewView(instance);
+ if (!previewp)
+ {
+ llassert(previewp != NULL);
+ return NULL;
+ }
+
+ LLPointer<LLImageFormatted> img = previewp->getFormattedImage();
+ if (!img.get())
+ {
+ llwarns << "Empty snapshot image data" << llendl;
+ llassert(img.get() != NULL);
+ }
+
+ return img;
+}
+
+// static
+const LLVector3d& LLFloaterSnapshot::getPosTakenGlobal()
+{
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (!instance)
+ {
+ llassert(instance != NULL);
+ return LLVector3d::zero;
+ }
+
+ LLSnapshotLivePreview* previewp = Impl::getPreviewView(instance);
+ if (!previewp)
+ {
+ llassert(previewp != NULL);
+ return LLVector3d::zero;
+ }
+
+ return previewp->getPosTakenGlobal();
+}
+
+// static
+void LLFloaterSnapshot::setAgentEmail(const std::string& email)
+{
+ LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
+ if (instance)
+ {
+ LLSideTrayPanelContainer* panel_container = instance->getChild<LLSideTrayPanelContainer>("panel_container");
+ LLPanel* postcard_panel = panel_container->getPanelByName("panel_snapshot_postcard");
+ postcard_panel->notify(LLSD().with("agent-email", email));
+ }
+}
///----------------------------------------------------------------------------
/// Class LLSnapshotFloaterView
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
index c92d9efde5..2c79c749d6 100644
--- a/indra/newview/llfloatersnapshot.h
+++ b/indra/newview/llfloatersnapshot.h
@@ -27,11 +27,15 @@
#ifndef LL_LLFLOATERSNAPSHOT_H
#define LL_LLFLOATERSNAPSHOT_H
+#include "llimage.h"
#include "llfloater.h"
+class LLSpinCtrl;
class LLFloaterSnapshot : public LLFloater
{
+ LOG_CLASS(LLFloaterSnapshot);
+
public:
typedef enum e_snapshot_format
{
@@ -47,20 +51,29 @@ public:
/*virtual*/ void draw();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ S32 notify(const LLSD& info);
static void update();
-
- static S32 getUIWinHeightLong() {return sUIWinHeightLong ;}
- static S32 getUIWinHeightShort() {return sUIWinHeightShort ;}
- static S32 getUIWinWidth() {return sUIWinWidth ;}
+
+ // TODO: create a snapshot model instead
+ static LLFloaterSnapshot* getInstance();
+ static void saveTexture();
+ static void saveLocal();
+ static void preUpdate();
+ static void postUpdate();
+ static void postSave();
+ static void postPanelSwitch();
+ static LLPointer<LLImageFormatted> getImageData();
+ static const LLVector3d& getPosTakenGlobal();
+ static void setAgentEmail(const std::string& email);
+
+ static const LLRect& getThumbnailPlaceholderRect() { return sThumbnailPlaceholder->getRect(); }
private:
+ static LLUICtrl* sThumbnailPlaceholder;
+
class Impl;
Impl& impl;
-
- static S32 sUIWinHeightLong ;
- static S32 sUIWinHeightShort ;
- static S32 sUIWinWidth ;
};
class LLSnapshotFloaterView : public LLFloaterView
diff --git a/indra/newview/llpanelsnapshot.cpp b/indra/newview/llpanelsnapshot.cpp
new file mode 100644
index 0000000000..fdae521ac5
--- /dev/null
+++ b/indra/newview/llpanelsnapshot.cpp
@@ -0,0 +1,195 @@
+/**
+ * @file llpanelsnapshot.cpp
+ * @brief Snapshot panel base class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llpanelsnapshot.h"
+
+// libs
+#include "llcombobox.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltrans.h"
+
+// newview
+#include "llsidetraypanelcontainer.h"
+#include "llviewercontrol.h" // gSavedSettings
+
+// virtual
+BOOL LLPanelSnapshot::postBuild()
+{
+ getChild<LLUICtrl>(getImageSizeComboName())->setCommitCallback(boost::bind(&LLPanelSnapshot::onResolutionComboCommit, this, _1));
+ getChild<LLUICtrl>(getWidthSpinnerName())->setCommitCallback(boost::bind(&LLPanelSnapshot::onCustomResolutionCommit, this));
+ getChild<LLUICtrl>(getHeightSpinnerName())->setCommitCallback(boost::bind(&LLPanelSnapshot::onCustomResolutionCommit, this));
+ getChild<LLUICtrl>(getAspectRatioCBName())->setCommitCallback(boost::bind(&LLPanelSnapshot::onKeepAspectRatioCommit, this, _1));
+
+ updateControls(LLSD());
+ return TRUE;
+}
+
+// virtual
+void LLPanelSnapshot::onOpen(const LLSD& key)
+{
+ S32 old_format = gSavedSettings.getS32("SnapshotFormat");
+ S32 new_format = (S32) getImageFormat();
+
+ gSavedSettings.setS32("SnapshotFormat", new_format);
+ setCtrlsEnabled(true);
+
+ // Switching panels will likely change image format.
+ // Not updating preview right away may lead to errors,
+ // e.g. attempt to send a large BMP image by email.
+ if (old_format != new_format)
+ {
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("image-format-change", true));
+ }
+
+ updateCustomResControls();
+}
+
+LLFloaterSnapshot::ESnapshotFormat LLPanelSnapshot::getImageFormat() const
+{
+ return LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG;
+}
+
+LLSpinCtrl* LLPanelSnapshot::getWidthSpinner()
+{
+ return getChild<LLSpinCtrl>(getWidthSpinnerName());
+}
+
+LLSpinCtrl* LLPanelSnapshot::getHeightSpinner()
+{
+ return getChild<LLSpinCtrl>(getHeightSpinnerName());
+}
+
+S32 LLPanelSnapshot::getTypedPreviewWidth() const
+{
+ return getChild<LLUICtrl>(getWidthSpinnerName())->getValue().asInteger();
+}
+
+S32 LLPanelSnapshot::getTypedPreviewHeight() const
+{
+ return getChild<LLUICtrl>(getHeightSpinnerName())->getValue().asInteger();
+}
+
+void LLPanelSnapshot::enableAspectRatioCheckbox(BOOL enable)
+{
+ getChild<LLUICtrl>(getAspectRatioCBName())->setEnabled(enable);
+}
+
+LLSideTrayPanelContainer* LLPanelSnapshot::getParentContainer()
+{
+ LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return NULL;
+ }
+
+ return parent;
+}
+
+// virtual
+void LLPanelSnapshot::updateCustomResControls()
+{
+ LLComboBox* combo = getChild<LLComboBox>(getImageSizeComboName());
+ S32 selected_idx = combo->getFirstSelectedIndex();
+ const bool enable = selected_idx == (combo->getItemCount() - 1); // Current Window or Custom selected
+
+ getChild<LLUICtrl>(getWidthSpinnerName())->setEnabled(enable);
+ getChild<LLSpinCtrl>(getWidthSpinnerName())->setAllowEdit(enable);
+ getChild<LLUICtrl>(getHeightSpinnerName())->setEnabled(enable);
+ getChild<LLSpinCtrl>(getHeightSpinnerName())->setAllowEdit(enable);
+ enableAspectRatioCheckbox(enable);
+}
+
+void LLPanelSnapshot::updateImageQualityLevel()
+{
+ LLSliderCtrl* quality_slider = getChild<LLSliderCtrl>("image_quality_slider");
+ S32 quality_val = llfloor((F32) quality_slider->getValue().asReal());
+
+ std::string quality_lvl;
+
+ if (quality_val < 20)
+ {
+ quality_lvl = LLTrans::getString("snapshot_quality_very_low");
+ }
+ else if (quality_val < 40)
+ {
+ quality_lvl = LLTrans::getString("snapshot_quality_low");
+ }
+ else if (quality_val < 60)
+ {
+ quality_lvl = LLTrans::getString("snapshot_quality_medium");
+ }
+ else if (quality_val < 80)
+ {
+ quality_lvl = LLTrans::getString("snapshot_quality_high");
+ }
+ else
+ {
+ quality_lvl = LLTrans::getString("snapshot_quality_very_high");
+ }
+
+ getChild<LLTextBox>("image_quality_level")->setTextArg("[QLVL]", quality_lvl);
+}
+
+void LLPanelSnapshot::goBack()
+{
+ LLSideTrayPanelContainer* parent = getParentContainer();
+ if (parent)
+ {
+ parent->openPreviousPanel();
+ parent->getCurrentPanel()->onOpen(LLSD());
+ }
+}
+
+void LLPanelSnapshot::cancel()
+{
+ goBack();
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("set-ready", true));
+}
+
+void LLPanelSnapshot::onCustomResolutionCommit()
+{
+ LLSD info;
+ info["w"] = getChild<LLUICtrl>(getWidthSpinnerName())->getValue().asInteger();
+ info["h"] = getChild<LLUICtrl>(getHeightSpinnerName())->getValue().asInteger();
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("custom-res-change", info));
+}
+
+void LLPanelSnapshot::onResolutionComboCommit(LLUICtrl* ctrl)
+{
+ updateCustomResControls();
+
+ LLSD info;
+ info["combo-res-change"]["control-name"] = ctrl->getName();
+ LLFloaterSnapshot::getInstance()->notify(info);
+}
+
+void LLPanelSnapshot::onKeepAspectRatioCommit(LLUICtrl* ctrl)
+{
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("keep-aspect-change", ctrl->getValue().asBoolean()));
+}
diff --git a/indra/newview/llpanelsnapshot.h b/indra/newview/llpanelsnapshot.h
new file mode 100644
index 0000000000..a49782a3e0
--- /dev/null
+++ b/indra/newview/llpanelsnapshot.h
@@ -0,0 +1,69 @@
+/**
+ * @file llpanelsnapshot.h
+ * @brief Snapshot panel base class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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_LLPANELSNAPSHOT_H
+#define LL_LLPANELSNAPSHOT_H
+
+#include "llfloatersnapshot.h"
+
+class LLSideTrayPanelContainer;
+
+/**
+ * Snapshot panel base class.
+ */
+class LLPanelSnapshot: public LLPanel
+{
+public:
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ virtual std::string getWidthSpinnerName() const = 0;
+ virtual std::string getHeightSpinnerName() const = 0;
+ virtual std::string getAspectRatioCBName() const = 0;
+ virtual std::string getImageSizeComboName() const = 0;
+
+ virtual S32 getTypedPreviewWidth() const;
+ virtual S32 getTypedPreviewHeight() const;
+ virtual LLSpinCtrl* getWidthSpinner();
+ virtual LLSpinCtrl* getHeightSpinner();
+ virtual void enableAspectRatioCheckbox(BOOL enable);
+ virtual LLFloaterSnapshot::ESnapshotFormat getImageFormat() const;
+ virtual void updateControls(const LLSD& info) = 0; ///< Update controls from saved settings
+
+protected:
+ LLSideTrayPanelContainer* getParentContainer();
+ virtual void updateCustomResControls();
+ void updateImageQualityLevel();
+ void goBack(); ///< Switch to the default (Snapshot Options) panel
+ void cancel();
+
+ // common UI callbacks
+ void onCustomResolutionCommit();
+ void onResolutionComboCommit(LLUICtrl* ctrl);
+ void onKeepAspectRatioCommit(LLUICtrl* ctrl);
+};
+
+#endif // LL_LLPANELSNAPSHOT_H
diff --git a/indra/newview/llpanelsnapshotinventory.cpp b/indra/newview/llpanelsnapshotinventory.cpp
new file mode 100644
index 0000000000..63ccbc1b02
--- /dev/null
+++ b/indra/newview/llpanelsnapshotinventory.cpp
@@ -0,0 +1,109 @@
+/**
+ * @file llpanelsnapshotinventory.cpp
+ * @brief The panel provides UI for saving snapshot as an inventory texture.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llcombobox.h"
+#include "lleconomy.h"
+#include "llsidetraypanelcontainer.h"
+#include "llspinctrl.h"
+
+#include "llfloatersnapshot.h" // FIXME: replace with a snapshot storage model
+#include "llpanelsnapshot.h"
+#include "llviewercontrol.h" // gSavedSettings
+
+/**
+ * The panel provides UI for saving snapshot as an inventory texture.
+ */
+class LLPanelSnapshotInventory
+: public LLPanelSnapshot
+{
+ LOG_CLASS(LLPanelSnapshotInventory);
+
+public:
+ LLPanelSnapshotInventory();
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+private:
+ /*virtual*/ void updateCustomResControls(); ///< Show/hide custom resolution controls (spinners and checkbox)
+ /*virtual*/ std::string getWidthSpinnerName() const { return "inventory_snapshot_width"; }
+ /*virtual*/ std::string getHeightSpinnerName() const { return "inventory_snapshot_height"; }
+ /*virtual*/ std::string getAspectRatioCBName() const { return "inventory_keep_aspect_check"; }
+ /*virtual*/ std::string getImageSizeComboName() const { return "texture_size_combo"; }
+ /*virtual*/ void updateControls(const LLSD& info);
+
+ void onSend();
+};
+
+static LLRegisterPanelClassWrapper<LLPanelSnapshotInventory> panel_class("llpanelsnapshotinventory");
+
+LLPanelSnapshotInventory::LLPanelSnapshotInventory()
+{
+ mCommitCallbackRegistrar.add("Inventory.Save", boost::bind(&LLPanelSnapshotInventory::onSend, this));
+ mCommitCallbackRegistrar.add("Inventory.Cancel", boost::bind(&LLPanelSnapshotInventory::cancel, this));
+}
+
+// virtual
+BOOL LLPanelSnapshotInventory::postBuild()
+{
+ return LLPanelSnapshot::postBuild();
+}
+
+// virtual
+void LLPanelSnapshotInventory::onOpen(const LLSD& key)
+{
+ getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()));
+ LLPanelSnapshot::onOpen(key);
+}
+
+// virtual
+void LLPanelSnapshotInventory::updateCustomResControls()
+{
+ LLComboBox* combo = getChild<LLComboBox>(getImageSizeComboName());
+ S32 selected_idx = combo->getFirstSelectedIndex();
+ const bool show = selected_idx == (combo->getItemCount() - 1); // Custom selected
+
+ getChild<LLUICtrl>(getWidthSpinnerName())->setVisible(show);
+ getChild<LLUICtrl>(getHeightSpinnerName())->setVisible(show);
+ getChild<LLUICtrl>(getAspectRatioCBName())->setVisible(show);
+
+ // enable controls if possible
+ LLPanelSnapshot::updateCustomResControls();
+}
+
+// virtual
+void LLPanelSnapshotInventory::updateControls(const LLSD& info)
+{
+ const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true;
+ getChild<LLUICtrl>("save_btn")->setEnabled(have_snapshot);
+}
+
+void LLPanelSnapshotInventory::onSend()
+{
+ LLFloaterSnapshot::saveTexture();
+ LLFloaterSnapshot::postSave();
+}
diff --git a/indra/newview/llpanelsnapshotlocal.cpp b/indra/newview/llpanelsnapshotlocal.cpp
new file mode 100644
index 0000000000..eaa27b8d41
--- /dev/null
+++ b/indra/newview/llpanelsnapshotlocal.cpp
@@ -0,0 +1,154 @@
+/**
+ * @file llpanelsnapshotlocal.cpp
+ * @brief The panel provides UI for saving snapshot to a local folder.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llcombobox.h"
+#include "llsidetraypanelcontainer.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+
+#include "llfloatersnapshot.h" // FIXME: replace with a snapshot storage model
+#include "llpanelsnapshot.h"
+#include "llviewercontrol.h" // gSavedSettings
+
+/**
+ * The panel provides UI for saving snapshot to a local folder.
+ */
+class LLPanelSnapshotLocal
+: public LLPanelSnapshot
+{
+ LOG_CLASS(LLPanelSnapshotLocal);
+
+public:
+ LLPanelSnapshotLocal();
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+private:
+ /*virtual*/ std::string getWidthSpinnerName() const { return "local_snapshot_width"; }
+ /*virtual*/ std::string getHeightSpinnerName() const { return "local_snapshot_height"; }
+ /*virtual*/ std::string getAspectRatioCBName() const { return "local_keep_aspect_check"; }
+ /*virtual*/ std::string getImageSizeComboName() const { return "local_size_combo"; }
+ /*virtual*/ LLFloaterSnapshot::ESnapshotFormat getImageFormat() const;
+ /*virtual*/ void updateControls(const LLSD& info);
+
+ void onFormatComboCommit(LLUICtrl* ctrl);
+ void onQualitySliderCommit(LLUICtrl* ctrl);
+ void onSend();
+};
+
+static LLRegisterPanelClassWrapper<LLPanelSnapshotLocal> panel_class("llpanelsnapshotlocal");
+
+LLPanelSnapshotLocal::LLPanelSnapshotLocal()
+{
+ mCommitCallbackRegistrar.add("Local.Save", boost::bind(&LLPanelSnapshotLocal::onSend, this));
+ mCommitCallbackRegistrar.add("Local.Cancel", boost::bind(&LLPanelSnapshotLocal::cancel, this));
+}
+
+// virtual
+BOOL LLPanelSnapshotLocal::postBuild()
+{
+ getChild<LLUICtrl>("image_quality_slider")->setCommitCallback(boost::bind(&LLPanelSnapshotLocal::onQualitySliderCommit, this, _1));
+ getChild<LLUICtrl>("local_format_combo")->setCommitCallback(boost::bind(&LLPanelSnapshotLocal::onFormatComboCommit, this, _1));
+
+ return LLPanelSnapshot::postBuild();
+}
+
+// virtual
+void LLPanelSnapshotLocal::onOpen(const LLSD& key)
+{
+ LLPanelSnapshot::onOpen(key);
+}
+
+// virtual
+LLFloaterSnapshot::ESnapshotFormat LLPanelSnapshotLocal::getImageFormat() const
+{
+ LLFloaterSnapshot::ESnapshotFormat fmt = LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG;
+
+ LLComboBox* local_format_combo = getChild<LLComboBox>("local_format_combo");
+ const std::string id = local_format_combo->getValue().asString();
+ if (id == "PNG")
+ {
+ fmt = LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG;
+ }
+ else if (id == "JPEG")
+ {
+ fmt = LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG;
+ }
+ else if (id == "BMP")
+ {
+ fmt = LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP;
+ }
+
+ return fmt;
+}
+
+// virtual
+void LLPanelSnapshotLocal::updateControls(const LLSD& info)
+{
+ LLFloaterSnapshot::ESnapshotFormat fmt =
+ (LLFloaterSnapshot::ESnapshotFormat) gSavedSettings.getS32("SnapshotFormat");
+ getChild<LLComboBox>("local_format_combo")->selectNthItem((S32) fmt);
+
+ const bool show_quality_ctrls = (fmt == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ getChild<LLUICtrl>("image_quality_slider")->setVisible(show_quality_ctrls);
+ getChild<LLUICtrl>("image_quality_level")->setVisible(show_quality_ctrls);
+
+ getChild<LLUICtrl>("image_quality_slider")->setValue(gSavedSettings.getS32("SnapshotQuality"));
+ updateImageQualityLevel();
+
+ const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true;
+ getChild<LLUICtrl>("save_btn")->setEnabled(have_snapshot);
+}
+
+void LLPanelSnapshotLocal::onFormatComboCommit(LLUICtrl* ctrl)
+{
+ // will call updateControls()
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("image-format-change", true));
+}
+
+void LLPanelSnapshotLocal::onQualitySliderCommit(LLUICtrl* ctrl)
+{
+ updateImageQualityLevel();
+
+ LLSliderCtrl* slider = (LLSliderCtrl*)ctrl;
+ S32 quality_val = llfloor((F32)slider->getValue().asReal());
+ LLSD info;
+ info["image-quality-change"] = quality_val;
+ LLFloaterSnapshot::getInstance()->notify(info);
+}
+
+void LLPanelSnapshotLocal::onSend()
+{
+ LLFloaterSnapshot* floater = LLFloaterSnapshot::getInstance();
+
+ floater->notify(LLSD().with("set-working", true));
+ LLFloaterSnapshot::saveLocal();
+ LLFloaterSnapshot::postSave();
+ goBack();
+ floater->notify(LLSD().with("set-finished", LLSD().with("ok", true).with("msg", "local")));
+}
diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp
new file mode 100644
index 0000000000..df904b6836
--- /dev/null
+++ b/indra/newview/llpanelsnapshotoptions.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file llpanelsnapshotoptions.cpp
+ * @brief Snapshot posting options panel.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "lleconomy.h"
+#include "llpanel.h"
+#include "llsidetraypanelcontainer.h"
+
+#include "llfloatersnapshot.h" // FIXME: create a snapshot model
+
+/**
+ * Provides several ways to save a snapshot.
+ */
+class LLPanelSnapshotOptions
+: public LLPanel
+{
+ LOG_CLASS(LLPanelSnapshotOptions);
+
+public:
+ LLPanelSnapshotOptions();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+private:
+ void openPanel(const std::string& panel_name);
+ void onSaveToProfile();
+ void onSaveToEmail();
+ void onSaveToInventory();
+ void onSaveToComputer();
+};
+
+static LLRegisterPanelClassWrapper<LLPanelSnapshotOptions> panel_class("llpanelsnapshotoptions");
+
+LLPanelSnapshotOptions::LLPanelSnapshotOptions()
+{
+ mCommitCallbackRegistrar.add("Snapshot.SaveToProfile", boost::bind(&LLPanelSnapshotOptions::onSaveToProfile, this));
+ mCommitCallbackRegistrar.add("Snapshot.SaveToEmail", boost::bind(&LLPanelSnapshotOptions::onSaveToEmail, this));
+ mCommitCallbackRegistrar.add("Snapshot.SaveToInventory", boost::bind(&LLPanelSnapshotOptions::onSaveToInventory, this));
+ mCommitCallbackRegistrar.add("Snapshot.SaveToComputer", boost::bind(&LLPanelSnapshotOptions::onSaveToComputer, this));
+}
+
+// virtual
+void LLPanelSnapshotOptions::onOpen(const LLSD& key)
+{
+ S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+ getChild<LLUICtrl>("save_to_inventory_btn")->setLabelArg("[AMOUNT]", llformat("%d", upload_cost));
+}
+
+void LLPanelSnapshotOptions::openPanel(const std::string& panel_name)
+{
+ LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->openPanel(panel_name);
+ parent->getCurrentPanel()->onOpen(LLSD());
+ LLFloaterSnapshot::postPanelSwitch();
+}
+
+void LLPanelSnapshotOptions::onSaveToProfile()
+{
+ openPanel("panel_snapshot_profile");
+}
+
+void LLPanelSnapshotOptions::onSaveToEmail()
+{
+ openPanel("panel_snapshot_postcard");
+}
+
+void LLPanelSnapshotOptions::onSaveToInventory()
+{
+ openPanel("panel_snapshot_inventory");
+}
+
+void LLPanelSnapshotOptions::onSaveToComputer()
+{
+ openPanel("panel_snapshot_local");
+}
diff --git a/indra/newview/llpanelsnapshotpostcard.cpp b/indra/newview/llpanelsnapshotpostcard.cpp
new file mode 100644
index 0000000000..6867c7af4e
--- /dev/null
+++ b/indra/newview/llpanelsnapshotpostcard.cpp
@@ -0,0 +1,269 @@
+/**
+ * @file llpanelsnapshotpostcard.cpp
+ * @brief Postcard sending panel.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llcombobox.h"
+#include "llnotificationsutil.h"
+#include "llsidetraypanelcontainer.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltexteditor.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llfloatersnapshot.h" // FIXME: replace with a snapshot storage model
+#include "llpanelsnapshot.h"
+#include "llpostcard.h"
+#include "llviewercontrol.h" // gSavedSettings
+#include "llviewerwindow.h"
+
+#include <boost/regex.hpp>
+
+/**
+ * Sends postcard via email.
+ */
+class LLPanelSnapshotPostcard
+: public LLPanelSnapshot
+{
+ LOG_CLASS(LLPanelSnapshotPostcard);
+
+public:
+ LLPanelSnapshotPostcard();
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+ /*virtual*/ S32 notify(const LLSD& info);
+
+private:
+ /*virtual*/ std::string getWidthSpinnerName() const { return "postcard_snapshot_width"; }
+ /*virtual*/ std::string getHeightSpinnerName() const { return "postcard_snapshot_height"; }
+ /*virtual*/ std::string getAspectRatioCBName() const { return "postcard_keep_aspect_check"; }
+ /*virtual*/ std::string getImageSizeComboName() const { return "postcard_size_combo"; }
+ /*virtual*/ LLFloaterSnapshot::ESnapshotFormat getImageFormat() const { return LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG; }
+ /*virtual*/ void updateControls(const LLSD& info);
+
+ bool missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response);
+ void sendPostcard();
+
+ void onMsgFormFocusRecieved();
+ void onFormatComboCommit(LLUICtrl* ctrl);
+ void onQualitySliderCommit(LLUICtrl* ctrl);
+ void onTabButtonPress(S32 btn_idx);
+ void onSend();
+
+ bool mHasFirstMsgFocus;
+ std::string mAgentEmail;
+};
+
+static LLRegisterPanelClassWrapper<LLPanelSnapshotPostcard> panel_class("llpanelsnapshotpostcard");
+
+LLPanelSnapshotPostcard::LLPanelSnapshotPostcard()
+: mHasFirstMsgFocus(false)
+{
+ mCommitCallbackRegistrar.add("Postcard.Send", boost::bind(&LLPanelSnapshotPostcard::onSend, this));
+ mCommitCallbackRegistrar.add("Postcard.Cancel", boost::bind(&LLPanelSnapshotPostcard::cancel, this));
+ mCommitCallbackRegistrar.add("Postcard.Message", boost::bind(&LLPanelSnapshotPostcard::onTabButtonPress, this, 0));
+ mCommitCallbackRegistrar.add("Postcard.Settings", boost::bind(&LLPanelSnapshotPostcard::onTabButtonPress, this, 1));
+
+}
+
+// virtual
+BOOL LLPanelSnapshotPostcard::postBuild()
+{
+ // pick up the user's up-to-date email address
+ gAgent.sendAgentUserInfoRequest();
+
+ std::string name_string;
+ LLAgentUI::buildFullname(name_string);
+ getChild<LLUICtrl>("name_form")->setValue(LLSD(name_string));
+
+ // For the first time a user focuses to .the msg box, all text will be selected.
+ getChild<LLUICtrl>("msg_form")->setFocusChangedCallback(boost::bind(&LLPanelSnapshotPostcard::onMsgFormFocusRecieved, this));
+
+ getChild<LLUICtrl>("to_form")->setFocus(TRUE);
+
+ getChild<LLUICtrl>("image_quality_slider")->setCommitCallback(boost::bind(&LLPanelSnapshotPostcard::onQualitySliderCommit, this, _1));
+
+ getChild<LLButton>("message_btn")->setToggleState(TRUE);
+
+ return LLPanelSnapshot::postBuild();
+}
+
+// virtual
+void LLPanelSnapshotPostcard::onOpen(const LLSD& key)
+{
+ LLPanelSnapshot::onOpen(key);
+}
+
+// virtual
+S32 LLPanelSnapshotPostcard::notify(const LLSD& info)
+{
+ if (!info.has("agent-email"))
+ {
+ llassert(info.has("agent-email"));
+ return 0;
+ }
+
+ if (mAgentEmail.empty())
+ {
+ mAgentEmail = info["agent-email"].asString();
+ }
+
+ return 1;
+}
+
+// virtual
+void LLPanelSnapshotPostcard::updateControls(const LLSD& info)
+{
+ getChild<LLUICtrl>("image_quality_slider")->setValue(gSavedSettings.getS32("SnapshotQuality"));
+ updateImageQualityLevel();
+
+ const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true;
+ getChild<LLUICtrl>("send_btn")->setEnabled(have_snapshot);
+}
+
+bool LLPanelSnapshotPostcard::missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if(0 == option)
+ {
+ // User clicked OK
+ if((getChild<LLUICtrl>("subject_form")->getValue().asString()).empty())
+ {
+ // Stuff the subject back into the form.
+ getChild<LLUICtrl>("subject_form")->setValue(getString("default_subject"));
+ }
+
+ if (!mHasFirstMsgFocus)
+ {
+ // The user never switched focus to the message window.
+ // Using the default string.
+ getChild<LLUICtrl>("msg_form")->setValue(getString("default_message"));
+ }
+
+ sendPostcard();
+ }
+ return false;
+}
+
+
+void LLPanelSnapshotPostcard::sendPostcard()
+{
+ std::string to(getChild<LLUICtrl>("to_form")->getValue().asString());
+ std::string subject(getChild<LLUICtrl>("subject_form")->getValue().asString());
+
+ LLSD postcard = LLSD::emptyMap();
+ postcard["pos-global"] = LLFloaterSnapshot::getPosTakenGlobal().getValue();
+ postcard["to"] = to;
+ postcard["from"] = mAgentEmail;
+ postcard["name"] = getChild<LLUICtrl>("name_form")->getValue().asString();
+ postcard["subject"] = subject;
+ postcard["msg"] = getChild<LLUICtrl>("msg_form")->getValue().asString();
+ LLPostCard::send(LLFloaterSnapshot::getImageData(), postcard);
+
+ // Give user feedback of the event.
+ gViewerWindow->playSnapshotAnimAndSound();
+
+ LLFloaterSnapshot::postSave();
+}
+
+void LLPanelSnapshotPostcard::onMsgFormFocusRecieved()
+{
+ LLTextEditor* msg_form = getChild<LLTextEditor>("msg_form");
+ if (msg_form->hasFocus() && !mHasFirstMsgFocus)
+ {
+ mHasFirstMsgFocus = true;
+ msg_form->setText(LLStringUtil::null);
+ }
+}
+
+void LLPanelSnapshotPostcard::onFormatComboCommit(LLUICtrl* ctrl)
+{
+ // will call updateControls()
+ LLFloaterSnapshot::getInstance()->notify(LLSD().with("image-format-change", true));
+}
+
+void LLPanelSnapshotPostcard::onQualitySliderCommit(LLUICtrl* ctrl)
+{
+ updateImageQualityLevel();
+
+ LLSliderCtrl* slider = (LLSliderCtrl*)ctrl;
+ S32 quality_val = llfloor((F32)slider->getValue().asReal());
+ LLSD info;
+ info["image-quality-change"] = quality_val;
+ LLFloaterSnapshot::getInstance()->notify(info); // updates the "SnapshotQuality" setting
+}
+
+void LLPanelSnapshotPostcard::onTabButtonPress(S32 btn_idx)
+{
+ LLButton* buttons[2] = {
+ getChild<LLButton>("message_btn"),
+ getChild<LLButton>("settings_btn"),
+ };
+
+ // Switch between Message and Settings tabs.
+ LLButton* clicked_btn = buttons[btn_idx];
+ LLButton* other_btn = buttons[!btn_idx];
+ LLSideTrayPanelContainer* container =
+ getChild<LLSideTrayPanelContainer>("postcard_panel_container");
+
+ container->selectTab(clicked_btn->getToggleState() ? btn_idx : !btn_idx);
+ //clicked_btn->setEnabled(FALSE);
+ other_btn->toggleState();
+ //other_btn->setEnabled(TRUE);
+
+ lldebugs << "Button #" << btn_idx << " (" << clicked_btn->getName() << ") clicked" << llendl;
+}
+
+void LLPanelSnapshotPostcard::onSend()
+{
+ // Validate input.
+ std::string to(getChild<LLUICtrl>("to_form")->getValue().asString());
+
+ boost::regex email_format("[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(,[ \t]*[A-Za-z0-9.%+-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})*");
+
+ if (to.empty() || !boost::regex_match(to, email_format))
+ {
+ LLNotificationsUtil::add("PromptRecipientEmail");
+ return;
+ }
+
+ if (mAgentEmail.empty() || !boost::regex_match(mAgentEmail, email_format))
+ {
+ LLNotificationsUtil::add("PromptSelfEmail");
+ return;
+ }
+
+ std::string subject(getChild<LLUICtrl>("subject_form")->getValue().asString());
+ if(subject.empty() || !mHasFirstMsgFocus)
+ {
+ LLNotificationsUtil::add("PromptMissingSubjMsg", LLSD(), LLSD(), boost::bind(&LLPanelSnapshotPostcard::missingSubjMsgAlertCallback, this, _1, _2));
+ return;
+ }
+
+ // Send postcard.
+ sendPostcard();
+}
diff --git a/indra/newview/llpanelsnapshotprofile.cpp b/indra/newview/llpanelsnapshotprofile.cpp
new file mode 100644
index 0000000000..89245fc804
--- /dev/null
+++ b/indra/newview/llpanelsnapshotprofile.cpp
@@ -0,0 +1,100 @@
+/**
+ * @file llpanelsnapshotprofile.cpp
+ * @brief Posts a snapshot to My Profile feed.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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"
+
+// libs
+#include "llcombobox.h"
+#include "llfloaterreg.h"
+#include "llpanel.h"
+#include "llspinctrl.h"
+
+// newview
+#include "llfloatersnapshot.h"
+#include "llpanelsnapshot.h"
+#include "llsidetraypanelcontainer.h"
+#include "llwebprofile.h"
+
+/**
+ * Posts a snapshot to My Profile feed.
+ */
+class LLPanelSnapshotProfile
+: public LLPanelSnapshot
+{
+ LOG_CLASS(LLPanelSnapshotProfile);
+
+public:
+ LLPanelSnapshotProfile();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+private:
+ /*virtual*/ std::string getWidthSpinnerName() const { return "profile_snapshot_width"; }
+ /*virtual*/ std::string getHeightSpinnerName() const { return "profile_snapshot_height"; }
+ /*virtual*/ std::string getAspectRatioCBName() const { return "profile_keep_aspect_check"; }
+ /*virtual*/ std::string getImageSizeComboName() const { return "profile_size_combo"; }
+ /*virtual*/ LLFloaterSnapshot::ESnapshotFormat getImageFormat() const { return LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG; }
+ /*virtual*/ void updateControls(const LLSD& info);
+
+ void onSend();
+};
+
+static LLRegisterPanelClassWrapper<LLPanelSnapshotProfile> panel_class("llpanelsnapshotprofile");
+
+LLPanelSnapshotProfile::LLPanelSnapshotProfile()
+{
+ mCommitCallbackRegistrar.add("PostToProfile.Send", boost::bind(&LLPanelSnapshotProfile::onSend, this));
+ mCommitCallbackRegistrar.add("PostToProfile.Cancel", boost::bind(&LLPanelSnapshotProfile::cancel, this));
+}
+
+// virtual
+BOOL LLPanelSnapshotProfile::postBuild()
+{
+ return LLPanelSnapshot::postBuild();
+}
+
+// virtual
+void LLPanelSnapshotProfile::onOpen(const LLSD& key)
+{
+ LLPanelSnapshot::onOpen(key);
+}
+
+// virtual
+void LLPanelSnapshotProfile::updateControls(const LLSD& info)
+{
+ const bool have_snapshot = info.has("have-snapshot") ? info["have-snapshot"].asBoolean() : true;
+ getChild<LLUICtrl>("post_btn")->setEnabled(have_snapshot);
+}
+
+void LLPanelSnapshotProfile::onSend()
+{
+ std::string caption = getChild<LLUICtrl>("caption")->getValue().asString();
+ bool add_location = getChild<LLUICtrl>("add_location_cb")->getValue().asBoolean();
+
+ LLWebProfile::uploadImage(LLFloaterSnapshot::getImageData(), caption, add_location);
+ LLFloaterSnapshot::postSave();
+}
diff --git a/indra/newview/llpostcard.cpp b/indra/newview/llpostcard.cpp
new file mode 100644
index 0000000000..4f2d6da7e5
--- /dev/null
+++ b/indra/newview/llpostcard.cpp
@@ -0,0 +1,155 @@
+/**
+ * @file llpostcard.cpp
+ * @brief Sending postcards.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llpostcard.h"
+
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llviewerregion.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llassetuploadresponders.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// misc
+
+static void postcard_upload_callback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status)
+{
+ LLSD* postcard_data = (LLSD*)user_data;
+
+ if (result)
+ {
+ // TODO: display the error messages in UI
+ llwarns << "Failed to send postcard: " << LLAssetStorage::getErrorString(result) << llendl;
+ LLPostCard::reportPostResult(false);
+ }
+ else
+ {
+ // only create the postcard once the upload succeeds
+
+ // request the postcard
+ const LLSD& data = *postcard_data;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SendPostcard");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("AssetID", data["asset-id"].asUUID());
+ msg->addVector3d("PosGlobal", LLVector3d(data["pos-global"]));
+ msg->addString("To", data["to"]);
+ msg->addString("From", data["from"]);
+ msg->addString("Name", data["name"]);
+ msg->addString("Subject", data["subject"]);
+ msg->addString("Msg", data["msg"]);
+ msg->addBOOL("AllowPublish", FALSE);
+ msg->addBOOL("MaturePublish", FALSE);
+ gAgent.sendReliableMessage();
+
+ LLPostCard::reportPostResult(true);
+ }
+
+ delete postcard_data;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// LLPostcardSendResponder
+
+class LLPostcardSendResponder : public LLAssetUploadResponder
+{
+ LOG_CLASS(LLPostcardSendResponder);
+
+public:
+ LLPostcardSendResponder(const LLSD &post_data,
+ const LLUUID& vfile_id,
+ LLAssetType::EType asset_type):
+ LLAssetUploadResponder(post_data, vfile_id, asset_type)
+ {
+ }
+
+ /*virtual*/ void uploadComplete(const LLSD& content)
+ {
+ llinfos << "Postcard sent" << llendl;
+ LL_DEBUGS("Snapshots") << "content: " << content << llendl;
+ LLPostCard::reportPostResult(true);
+ }
+
+ /*virtual*/ void uploadFailure(const LLSD& content)
+ {
+ llwarns << "Sending postcard failed: " << content << llendl;
+ LLPostCard::reportPostResult(false);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// LLPostCard
+
+LLPostCard::result_callback_t LLPostCard::mResultCallback;
+
+// static
+void LLPostCard::send(LLPointer<LLImageFormatted> image, const LLSD& postcard_data)
+{
+ LLTransactionID transaction_id;
+ LLAssetID asset_id;
+
+ transaction_id.generate();
+ asset_id = transaction_id.makeAssetID(gAgent.getSecureSessionID());
+ LLVFile::writeFile(image->getData(), image->getDataSize(), gVFS, asset_id, LLAssetType::AT_IMAGE_JPEG);
+
+ // upload the image
+ std::string url = gAgent.getRegion()->getCapability("SendPostcard");
+ if (!url.empty())
+ {
+ llinfos << "Sending postcard via capability" << llendl;
+ // the capability already encodes: agent ID, region ID
+ LL_DEBUGS("Snapshots") << "url: " << url << llendl;
+ LL_DEBUGS("Snapshots") << "body: " << postcard_data << llendl;
+ LL_DEBUGS("Snapshots") << "data size: " << image->getDataSize() << llendl;
+ LLHTTPClient::post(url, postcard_data,
+ new LLPostcardSendResponder(postcard_data, asset_id, LLAssetType::AT_IMAGE_JPEG));
+ }
+ else
+ {
+ llinfos << "Sending postcard" << llendl;
+ LLSD* data = new LLSD(postcard_data);
+ (*data)["asset-id"] = asset_id;
+ gAssetStorage->storeAssetData(transaction_id, LLAssetType::AT_IMAGE_JPEG,
+ &postcard_upload_callback, (void *)data, FALSE);
+ }
+}
+
+// static
+void LLPostCard::reportPostResult(bool ok)
+{
+ if (mResultCallback)
+ {
+ mResultCallback(ok);
+ }
+}
diff --git a/indra/newview/llpostcard.h b/indra/newview/llpostcard.h
new file mode 100644
index 0000000000..0eb118b906
--- /dev/null
+++ b/indra/newview/llpostcard.h
@@ -0,0 +1,48 @@
+/**
+ * @file llpostcard.h
+ * @brief Sending postcards.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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_LLPOSTCARD_H
+#define LL_LLPOSTCARD_H
+
+#include "llimage.h"
+#include "lluuid.h"
+
+class LLPostCard
+{
+ LOG_CLASS(LLPostCard);
+
+public:
+ typedef boost::function<void(bool ok)> result_callback_t;
+
+ static void send(LLPointer<LLImageFormatted> image, const LLSD& postcard_data);
+ static void setPostResultCallback(result_callback_t cb) { mResultCallback = cb; }
+ static void reportPostResult(bool ok);
+
+private:
+ static result_callback_t mResultCallback;
+};
+
+#endif // LL_LLPOSTCARD_H
diff --git a/indra/newview/llsidetraypanelcontainer.cpp b/indra/newview/llsidetraypanelcontainer.cpp
index 95a12c7c23..e340333c2c 100644
--- a/indra/newview/llsidetraypanelcontainer.cpp
+++ b/indra/newview/llsidetraypanelcontainer.cpp
@@ -62,6 +62,13 @@ void LLSideTrayPanelContainer::onOpen(const LLSD& key)
getCurrentPanel()->onOpen(key);
}
+void LLSideTrayPanelContainer::openPanel(const std::string& panel_name, const LLSD& key)
+{
+ LLSD combined_key = key;
+ combined_key[PARAM_SUB_PANEL_NAME] = panel_name;
+ onOpen(combined_key);
+}
+
void LLSideTrayPanelContainer::openPreviousPanel()
{
if(!mDefaultPanelName.empty())
diff --git a/indra/newview/llsidetraypanelcontainer.h b/indra/newview/llsidetraypanelcontainer.h
index 14269b002b..93a85ed374 100644
--- a/indra/newview/llsidetraypanelcontainer.h
+++ b/indra/newview/llsidetraypanelcontainer.h
@@ -57,6 +57,11 @@ public:
/*virtual*/ void onOpen(const LLSD& key);
/**
+ * Opens given subpanel.
+ */
+ void openPanel(const std::string& panel_name, const LLSD& key = LLSD::emptyMap());
+
+ /**
* Opens previous panel from panel navigation history.
*/
void openPreviousPanel();
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c761969fcf..74c4f6d2dc 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -85,7 +85,6 @@
#include "llfloateropenobject.h"
#include "llfloaterpay.h"
#include "llfloaterperms.h"
-#include "llfloaterpostcard.h"
#include "llfloaterpostprocess.h"
#include "llfloaterpreference.h"
#include "llfloaterproperties.h"
@@ -245,7 +244,6 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("people", "floater_people.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("places", "floater_places.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
- LLFloaterReg::add("postcard", "floater_postcard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPostcard>);
LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
LLFloaterReg::add("prefs_proxy", "floater_preferences_proxy.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreferenceProxy>);
LLFloaterReg::add("prefs_hardware_settings", "floater_hardware_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHardwareSettings>);
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 41b4dc01e8..5afd481dda 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -50,6 +50,7 @@
#include "llvoavatar.h"
#include "llvoavatarself.h"
#include "llviewerregion.h"
+#include "llwebprofile.h"
#include "llwebsharing.h" // For LLWebSharing::setOpenIDCookie(), *TODO: find a better way to do this!
#include "llfilepicker.h"
#include "llnotifications.h"
@@ -319,6 +320,10 @@ public:
std::string cookie = content["set-cookie"].asString();
LLViewerMedia::getCookieStore()->setCookiesFromHost(cookie, mHost);
+
+ // Set cookie for snapshot publishing.
+ std::string auth_cookie = cookie.substr(0, cookie.find(";")); // strip path
+ LLWebProfile::setAuthCookie(auth_cookie);
}
void completedRaw(
@@ -1484,6 +1489,8 @@ void LLViewerMedia::setOpenIDCookie()
std::string profile_url = getProfileURL("");
LLURL raw_profile_url( profile_url.c_str() );
+ LL_DEBUGS("MediaAuth") << "Requesting " << profile_url << llendl;
+ LL_DEBUGS("MediaAuth") << "sOpenIDCookie = [" << sOpenIDCookie << "]" << llendl;
LLHTTPClient::get(profile_url,
new LLViewerMediaWebProfileResponder(raw_profile_url.getAuthority()),
headers);
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index b9293b3b31..7e830e14bf 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -528,23 +528,7 @@ class LLFileTakeSnapshotToDisk : public view_listener_t
{
gViewerWindow->playSnapshotAnimAndSound();
- LLPointer<LLImageFormatted> formatted;
- switch(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat")))
- {
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
- formatted = new LLImageJPEG(gSavedSettings.getS32("SnapshotQuality"));
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
- formatted = new LLImagePNG;
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
- formatted = new LLImageBMP;
- break;
- default:
- llwarns << "Unknown Local Snapshot format" << llendl;
- return true;
- }
-
+ LLPointer<LLImageFormatted> formatted = new LLImagePNG;
formatted->enableOverSize() ;
formatted->encode(raw, 0);
formatted->disableOverSize() ;
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index a9ca70fd26..7cae19a1d2 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -59,9 +59,9 @@
#include "llfloaterland.h"
#include "llfloaterregioninfo.h"
#include "llfloaterlandholdings.h"
-#include "llfloaterpostcard.h"
#include "llfloaterpreference.h"
#include "llfloatersidepanelcontainer.h"
+#include "llfloatersnapshot.h"
#include "llhudeffecttrail.h"
#include "llhudmanager.h"
#include "llinventoryfunctions.h"
@@ -6470,7 +6470,7 @@ void process_user_info_reply(LLMessageSystem* msg, void**)
msg->getString( "UserData", "DirectoryVisibility", dir_visibility);
LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email);
- LLFloaterPostcard::updateUserInfo(email);
+ LLFloaterSnapshot::setAgentEmail(email);
}
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 6fcbc401af..c20bc5f02f 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -4020,10 +4020,11 @@ BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d
}
// Saves an image to the harddrive as "SnapshotX" where X >= 1.
-BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image)
+BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image, bool force_picker)
{
if (!image)
{
+ llwarns << "No image to save" << llendl;
return FALSE;
}
@@ -4043,7 +4044,7 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image)
pick_type = LLFilePicker::FFSAVE_ALL; // ???
// Get a base file location if needed.
- if ( ! isSnapshotLocSet())
+ if (force_picker || !isSnapshotLocSet())
{
std::string proposed_name( sSnapshotBaseName );
@@ -4083,6 +4084,7 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image)
}
while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+ llinfos << "Saving snapshot to " << filepath << llendl;
return image->save(filepath);
}
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index d10b06f121..0cb7f82b58 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -324,7 +324,7 @@ public:
BOOL thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) ;
BOOL isSnapshotLocSet() const { return ! sSnapshotDir.empty(); }
void resetSnapshotLoc() const { sSnapshotDir.clear(); }
- BOOL saveImageNumbered(LLImageFormatted *image);
+ BOOL saveImageNumbered(LLImageFormatted *image, bool force_picker = false);
// Reset the directory where snapshots are saved.
// Client will open directory picker on next snapshot save.
diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp
new file mode 100644
index 0000000000..641f338f2c
--- /dev/null
+++ b/indra/newview/llwebprofile.cpp
@@ -0,0 +1,305 @@
+/**
+ * @file llwebprofile.cpp
+ * @brief Web profile access.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "llwebprofile.h"
+
+// libs
+#include "llbufferstream.h"
+#include "llhttpclient.h"
+#include "llimagepng.h"
+#include "llplugincookiestore.h"
+
+// newview
+#include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions
+#include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals
+
+// third-party
+#include "reader.h" // JSON
+
+/*
+ * Workflow:
+ * 1. LLViewerMedia::setOpenIDCookie()
+ * -> GET https://my-demo.secondlife.com/ via LLViewerMediaWebProfileResponder
+ * -> LLWebProfile::setAuthCookie()
+ * 2. LLWebProfile::uploadImage()
+ * -> GET "https://my-demo.secondlife.com/snapshots/s3_upload_config" via ConfigResponder
+ * 3. LLWebProfile::post()
+ * -> POST <config_url> via PostImageResponder
+ * -> redirect
+ * -> GET <redirect_url> via PostImageRedirectResponder
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+// LLWebProfileResponders::ConfigResponder
+
+class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLWebProfileResponders::ConfigResponder);
+
+public:
+ ConfigResponder(LLPointer<LLImageFormatted> imagep)
+ : mImagep(imagep)
+ {
+ }
+
+ /*virtual*/ void completedRaw(
+ U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (status != 200)
+ {
+ llwarns << "Failed to get upload config (" << status << ")" << llendl;
+ LLWebProfile::reportImageUploadStatus(false);
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body, root))
+ {
+ llwarns << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << llendl;
+ LLWebProfile::reportImageUploadStatus(false);
+ return;
+ }
+
+ // *TODO: 404 = not supported by the grid
+ // *TODO: increase timeout or handle 499 Expired
+
+ // Convert config to LLSD.
+ const Json::Value data = root["data"];
+ const std::string upload_url = root["url"].asString();
+ LLSD config;
+ config["acl"] = data["acl"].asString();
+ config["AWSAccessKeyId"] = data["AWSAccessKeyId"].asString();
+ config["Content-Type"] = data["Content-Type"].asString();
+ config["key"] = data["key"].asString();
+ config["policy"] = data["policy"].asString();
+ config["success_action_redirect"] = data["success_action_redirect"].asString();
+ config["signature"] = data["signature"].asString();
+ config["add_loc"] = data.get("add_loc", "0").asString();
+ config["caption"] = data.get("caption", "").asString();
+
+ // Do the actual image upload using the configuration.
+ LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << llendl;
+ LLWebProfile::post(mImagep, config, upload_url);
+ }
+
+private:
+ LLPointer<LLImageFormatted> mImagep;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// LLWebProfilePostImageRedirectResponder
+class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder);
+
+public:
+ /*virtual*/ void completedRaw(
+ U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (status != 200)
+ {
+ llwarns << "Failed to upload image: " << status << " " << reason << llendl;
+ LLWebProfile::reportImageUploadStatus(false);
+ return;
+ }
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+ llinfos << "Image uploaded." << llendl;
+ LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl;
+ LLWebProfile::reportImageUploadStatus(true);
+ }
+
+private:
+ LLPointer<LLImageFormatted> mImagep;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// LLWebProfileResponders::PostImageResponder
+class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLWebProfileResponders::PostImageResponder);
+
+public:
+ /*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ // Viewer seems to fail to follow a 303 redirect on POST request
+ // (URLRequest Error: 65, Send failed since rewinding of the data stream failed).
+ // Handle it manually.
+ if (status == 303)
+ {
+ LLSD headers = LLViewerMedia::getHeaders();
+ headers["Cookie"] = LLWebProfile::getAuthCookie();
+ const std::string& redir_url = content["location"];
+ LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl;
+ LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers);
+ }
+ else
+ {
+ llwarns << "Unexpected POST status: " << status << " " << reason << llendl;
+ LL_DEBUGS("Snapshots") << "headers: [" << content << "]" << llendl;
+ LLWebProfile::reportImageUploadStatus(false);
+ }
+ }
+
+ // Override just to suppress warnings.
+ /*virtual*/ void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// LLWebProfile
+
+std::string LLWebProfile::sAuthCookie;
+LLWebProfile::status_callback_t LLWebProfile::mStatusCallback;
+
+// static
+void LLWebProfile::uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location)
+{
+ // Get upload configuration data.
+ std::string config_url(getProfileURL(LLStringUtil::null) + "snapshots/s3_upload_config");
+ config_url += "?caption=" + LLURI::escape(caption);
+ config_url += "&add_loc=" + std::string(add_location ? "1" : "0");
+
+ LL_DEBUGS("Snapshots") << "Requesting " << config_url << llendl;
+ LLSD headers = LLViewerMedia::getHeaders();
+ headers["Cookie"] = getAuthCookie();
+ LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers);
+}
+
+// static
+void LLWebProfile::setAuthCookie(const std::string& cookie)
+{
+ LL_DEBUGS("Snapshots") << "Setting auth cookie: " << cookie << llendl;
+ sAuthCookie = cookie;
+}
+
+// static
+void LLWebProfile::post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url)
+{
+ if (dynamic_cast<LLImagePNG*>(image.get()) == 0)
+ {
+ llwarns << "Image to upload is not a PNG" << llendl;
+ llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0);
+ return;
+ }
+
+ const std::string boundary = "----------------------------0123abcdefab";
+
+ LLSD headers = LLViewerMedia::getHeaders();
+ headers["Cookie"] = getAuthCookie();
+ 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=\"key\"\r\n\r\n"
+ << config["key"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
+ << config["AWSAccessKeyId"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
+ << config["acl"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n"
+ << config["Content-Type"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n"
+ << config["policy"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n"
+ << config["signature"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n"
+ << config["success_action_redirect"].asString() << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n"
+ << "Content-Type: image/png\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);
+
+ // Send request, successful upload will trigger posting metadata.
+ LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers);
+}
+
+// static
+void LLWebProfile::reportImageUploadStatus(bool ok)
+{
+ if (mStatusCallback)
+ {
+ mStatusCallback(ok);
+ }
+}
+
+// static
+std::string LLWebProfile::getAuthCookie()
+{
+ // This is needed to test image uploads on Linux viewer built with OpenSSL 1.0.0 (0.9.8 works fine).
+ const char* debug_cookie = getenv("LL_SNAPSHOT_COOKIE");
+ return debug_cookie ? debug_cookie : sAuthCookie;
+}
diff --git a/indra/newview/llwebprofile.h b/indra/newview/llwebprofile.h
new file mode 100644
index 0000000000..10279bffac
--- /dev/null
+++ b/indra/newview/llwebprofile.h
@@ -0,0 +1,69 @@
+/**
+ * @file llwebprofile.h
+ * @brief Web profile access.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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_LLWEBPROFILE_H
+#define LL_LLWEBPROFILE_H
+
+#include "llimage.h"
+
+namespace LLWebProfileResponders
+{
+ class ConfigResponder;
+ class PostImageResponder;
+ class PostImageRedirectResponder;
+};
+
+/**
+ * @class LLWebProfile
+ *
+ * Manages interaction with, a web service allowing the upload of snapshot images
+ * taken within the viewer.
+ */
+class LLWebProfile
+{
+ LOG_CLASS(LLWebProfile);
+
+public:
+ typedef boost::function<void(bool ok)> status_callback_t;
+
+ static void uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location);
+ static void setAuthCookie(const std::string& cookie);
+ static void setImageUploadResultCallback(status_callback_t cb) { mStatusCallback = cb; }
+
+private:
+ friend class LLWebProfileResponders::ConfigResponder;
+ friend class LLWebProfileResponders::PostImageResponder;
+ friend class LLWebProfileResponders::PostImageRedirectResponder;
+
+ static void post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url);
+ static void reportImageUploadStatus(bool ok);
+ static std::string getAuthCookie();
+
+ static std::string sAuthCookie;
+ static status_callback_t mStatusCallback;
+};
+
+#endif // LL_LLWEBPROFILE_H
diff --git a/indra/newview/skins/default/textures/snapshot_download.png b/indra/newview/skins/default/textures/snapshot_download.png
new file mode 100644
index 0000000000..6aa1abded5
--- /dev/null
+++ b/indra/newview/skins/default/textures/snapshot_download.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/snapshot_email.png b/indra/newview/skins/default/textures/snapshot_email.png
new file mode 100644
index 0000000000..dee784a9bf
--- /dev/null
+++ b/indra/newview/skins/default/textures/snapshot_email.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index bb91d32c6c..bcdd73aeb8 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -547,6 +547,10 @@ with the same filename but different name
<texture name="Unknown_Icon" file_name="icons/unknown_icon.png" preload="true" />
<texture name="Snapshot_Off" file_name="bottomtray/Snapshot_Off.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />
+ <texture name="Snapshot_Download" file_name="snapshot_download.png" preload="false" />
+ <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="startup_logo" file_name="windows/startup_logo.png" preload="true" />
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 91b4ed6954..d7a1510c1c 100644
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -5,400 +5,308 @@
can_minimize="true"
can_close="true"
follows="left|top"
- height="520"
+ height="600"
layout="topleft"
name="Snapshot"
help_topic="snapshot"
save_rect="true"
save_visibility="true"
title="SNAPSHOT PREVIEW"
- width="245">
+ width="470">
<floater.string
name="unknown">
unknown
</floater.string>
- <radio_group
- height="70"
- label="Snapshot type"
- layout="topleft"
- left="10"
- name="snapshot_type_radio"
- top="20"
- width="205">
-<!--
- <radio_item
- height="16"
- label="Share to Web"
- layout="topleft"
- name="share_to_web"
- top_pad="0" />
--->
- <radio_item
- height="16"
- label="Email"
- layout="topleft"
- name="postcard"
- top_pad="2" />
- <radio_item
- height="16"
- label="My inventory (L$[AMOUNT])"
- layout="topleft"
- name="texture"
- top_pad="2" />
- <radio_item
- height="16"
- label="Save to my computer"
- layout="topleft"
- name="local"
- top_pad="2" />
- </radio_group>
- <ui_ctrl
- height="90"
- width="125"
+ <string
+ name="postcard_progress_str">
+ Sending Email
+ </string>
+ <string
+ name="profile_progress_str">
+ Posting
+ </string>
+ <string
+ name="inventory_progress_str">
+ Saving to Inventory
+ </string>
+ <string
+ name="local_progress_str">
+ Saving to Computer
+ </string>
+ <string
+ name="profile_succeeded_str">
+ Your Profile Feed has been updated!
+ </string>
+ <string
+ name="postcard_succeeded_str">
+ Email Sent!
+ </string>
+ <string
+ name="inventory_succeeded_str">
+ Saved to Inventory!
+ </string>
+ <string
+ name="local_succeeded_str">
+ Saved to Computer!
+ </string>
+ <string
+ name="profile_failed_str">
+ Failed to update your Profile Feed.
+ </string>
+ <string
+ name="postcard_failed_str">
+ Failed to send email.
+ </string>
+ <string
+ name="inventory_failed_str">
+ Failed to save to inventory.
+ </string>
+ <string
+ name="local_failed_str">
+ Failed to save to computer.
+ </string>
+ <view_border
+ bevel_style="in"
+ follows="left|top"
+ height="21"
+ left="10"
layout="topleft"
- name="thumbnail_placeholder"
- top_pad="6"
+ name="img_info_border"
+ top="22"
+ width="50"
+ />
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Snapshot_Off"
+ layout="topleft"
+ left_delta="-5"
+ mouse_opaque="true"
+ name="refresh_icon"
+ top_delta="3"
+ width="36" />
+ <button
follows="left|top"
- left="10"
- />
- <text
- type="string"
- font="SansSerifSmall"
- length="1"
- follows="left|top"
- height="14"
- layout="topleft"
- right="-5"
- left_delta="0"
- halign="right"
- name="file_size_label"
- top_pad="8"
- width="195">
- [SIZE] KB
- </text>
- <button
- follows="left|top"
- height="22"
- image_overlay="Refresh_Off"
- layout="topleft"
- left="10"
- name="new_snapshot_btn"
- width="23" />
- <button
- follows="left|top"
- height="23"
- label="Send"
- layout="topleft"
- left_pad="5"
- right="-5"
- name="send_btn"
- width="100" />
- <button
- follows="left|top"
- height="23"
- label="Save (L$[AMOUNT])"
- layout="topleft"
- right="-5"
- name="upload_btn"
- top_delta="0"
- width="110" />
- <flyout_button
- follows="left|top"
- height="23"
- label="Save"
- layout="topleft"
- right="-5"
- name="save_btn"
- tool_tip="Save image to a file"
- top_delta="0"
- width="100">
- <flyout_button.item
- label="Save"
- name="save_item"
- value="save" />
- <flyout_button.item
- label="Save As..."
- name="saveas_item"
- value="save as" />
- </flyout_button>
- <button
- follows="left|top"
- height="23"
- label="More"
- layout="topleft"
- left="10"
- name="more_btn"
- tool_tip="Advanced options"
- width="80" />
+ height="22"
+ image_overlay="Refresh_Off"
+ layout="topleft"
+ left_delta="31"
+ name="new_snapshot_btn"
+ top_delta="-3"
+ width="23" />
<button
follows="left|top"
height="23"
- label="Less"
+ image_overlay="TabIcon_Close_Off"
+ is_toggle="true"
layout="topleft"
- left_delta="0"
- name="less_btn"
+ left="240"
+ name="advanced_options_btn"
tool_tip="Advanced options"
top_delta="0"
- width="80" />
- <button
- follows="left|top"
- height="23"
- label="Cancel"
- layout="topleft"
- right="-5"
- left_pad="5"
- name="discard_btn"
- width="110" />
- <text
- type="string"
- length="1"
- follows="top|left"
- height="12"
- layout="topleft"
- left="10"
- name="type_label2"
- top_pad="5"
- width="127">
- Size
- </text>
- <text
- type="string"
- length="1"
- follows="top|left"
- height="12"
- layout="topleft"
- left_pad="5"
- name="format_label"
- top_delta="0"
- width="70">
- Format
- </text>
- <combo_box
- height="23"
- label="Resolution"
- layout="topleft"
- left="10"
- name="postcard_size_combo"
- width="120">
- <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.item
- label="Custom"
- name="Custom"
- value="[i-1,i-1]" />
- </combo_box>
- <combo_box
- height="23"
- label="Resolution"
- layout="topleft"
- left_delta="0"
- name="texture_size_combo"
- top_delta="0"
- width="127">
- <combo_box.item
- label="Current Window"
- name="CurrentWindow"
- value="[i0,i0]" />
- <combo_box.item
- label="Small (128x128)"
- name="Small(128x128)"
- value="[i128,i128]" />
- <combo_box.item
- label="Medium (256x256)"
- name="Medium(256x256)"
- value="[i256,i256]" />
- <combo_box.item
- label="Large (512x512)"
- name="Large(512x512)"
- value="[i512,i512]" />
- <combo_box.item
- label="Custom"
- name="Custom"
- value="[i-1,i-1]" />
- </combo_box>
- <combo_box
- height="23"
- label="Resolution"
- layout="topleft"
- left_delta="0"
- name="local_size_combo"
- top_delta="0"
- width="127">
- <combo_box.item
- label="Current Window"
- name="CurrentWindow"
- value="[i0,i0]" />
- <combo_box.item
- label="320x240"
- name="320x240"
- value="[i320,i240]" />
- <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.item
- label="1280x1024"
- name="1280x1024"
- value="[i1280,i1024]" />
- <combo_box.item
- label="1600x1200"
- name="1600x1200"
- value="[i1600,i1200]" />
- <combo_box.item
- label="Custom"
- name="Custom"
- value="[i-1,i-1]" />
- </combo_box>
- <combo_box
- height="23"
- label="Format"
- layout="topleft"
- left_pad="5"
- name="local_format_combo"
- width="70">
- <combo_box.item
- label="PNG"
- name="PNG" />
- <combo_box.item
- label="JPEG"
- name="JPEG" />
- <combo_box.item
- label="BMP"
- name="BMP" />
- </combo_box>
- <spinner
- allow_text_entry="false"
- decimal_digits="0"
- follows="left|top"
- height="20"
- increment="32"
- label="Width"
- label_width="40"
- layout="topleft"
- left="10"
- max_val="6016"
- min_val="32"
- name="snapshot_width"
- top_pad="10"
- width="95" />
- <spinner
- allow_text_entry="false"
- decimal_digits="0"
- follows="left|top"
- height="20"
- increment="32"
- label="Height"
- label_width="40"
- layout="topleft"
- left_pad="5"
- max_val="6016"
- min_val="32"
- name="snapshot_height"
- top_delta="0"
- width="95" />
- <check_box
- bottom_delta="20"
- label="Constrain proportions"
- layout="topleft"
- left="10"
- name="keep_aspect_check" />
- <slider
- decimal_digits="0"
- follows="left|top"
- height="15"
- increment="1"
- initial_value="75"
- label="Image quality"
- label_width="124"
- layout="topleft"
- left_delta="0"
- max_val="100"
- name="image_quality_slider"
- top_pad="5"
- width="228" />
- <text
- type="string"
- length="1"
+ width="23" />
+ <ui_ctrl
+ height="160"
+ width="250"
+ layout="topleft"
+ name="thumbnail_placeholder"
+ top="50"
+ follows="left|top"
+ left="10">
+ <loading_indicator
+ follows="left|top"
+ height="48"
+ layout="topleft"
+ name="working_indicator"
+ left="101"
+ top="46"
+ visible="false"
+ width="48" />
+ <text
+ follows="left|top|right"
+ font="SansSerifBold"
+ height="14"
+ layout="topleft"
+ left="5"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ right="-5"
+ top="98"
+ translate="false"
+ type="string"
+ visible="false"
+ width="130">
+ Working
+ </text>
+ </ui_ctrl>
+ <view_border
+ bevel_style="in"
+ height="21"
+ width="250"
+ layout="topleft"
+ name="img_info_border"
+ top_pad="3"
+ follows="left|top"
+ left_delta="0"
+ />
+ <text
+ type="string"
+ font="SansSerifSmall"
+ length="1"
+ follows="left|top"
+ height="14"
+ layout="topleft"
+ left_delta="5"
+ halign="left"
+ name="image_res_text"
+ top_delta="5"
+ width="100">
+ [WIDTH] x [HEIGHT] px
+ </text>
+ <text
+ follows="left|top"
+ font="SansSerifSmall"
+ height="14"
+ layout="topleft"
+ left="200"
+ length="1"
+ halign="right"
+ name="file_size_label"
+ top_delta="0"
+ type="string"
+ width="50">
+ [SIZE] KB
+ </text>
+ <panel_container
follows="left|top"
- height="13"
+ height="360"
layout="topleft"
- left="10"
- name="layer_type_label"
- top_pad="5"
- width="50">
- Capture:
- </text>
- <combo_box
- height="23"
- label="Image Layers"
- layout="topleft"
- left="30"
- name="layer_types"
- width="145">
- <combo_box.item
- label="Colors"
- name="Colors"
- value="colors" />
- <combo_box.item
- label="Depth"
- name="Depth"
- value="depth" />
- </combo_box>
- <check_box
- label="Interface"
- layout="topleft"
- left="30"
+ left="0"
+ name="panel_container"
+ default_panel_name="panel_snapshot_options"
top_pad="10"
- width="180"
- name="ui_check" />
- <check_box
- label="HUDs"
- layout="topleft"
- left="30"
- top_pad="10"
- width="180"
- name="hud_check" />
- <check_box
- label="Keep open after saving"
- layout="topleft"
- left="10"
- top_pad="8"
- width="180"
- name="keep_open_check" />
- <check_box
- label="Freeze frame (fullscreen)"
+ width="270">
+ <panel
+ class="llpanelsnapshotoptions"
+ filename="panel_snapshot_options.xml"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="panel_snapshot_options"
+ top="0" />
+ <panel
+ class="llpanelsnapshotprofile"
+ follows="all"
+ layout="topleft"
+ name="panel_snapshot_profile"
+ filename="panel_snapshot_profile.xml" />
+ <panel
+ class="llpanelsnapshotpostcard"
+ follows="all"
+ layout="topleft"
+ name="panel_snapshot_postcard"
+ filename="panel_snapshot_postcard.xml" />
+ <panel
+ class="llpanelsnapshotinventory"
+ follows="all"
+ layout="topleft"
+ name="panel_snapshot_inventory"
+ filename="panel_snapshot_inventory.xml" />
+ <panel
+ class="llpanelsnapshotlocal"
+ follows="all"
+ layout="topleft"
+ name="panel_snapshot_local"
+ filename="panel_snapshot_local.xml" />
+ </panel_container>
+ <panel
+ height="295"
layout="topleft"
- left="10"
- top_pad="8"
- width="180"
- name="freeze_frame_check" />
- <check_box
- label="Auto-refresh"
- layout="topleft"
- left="10"
- top_pad="8"
- width="180"
- name="auto_snapshot_check" />
+ left="270"
+ name="advanced_options_panel"
+ top="20"
+ width="200">
+ <text
+ type="string"
+ font="SansSerifSmall"
+ length="1"
+ follows="left|top"
+ height="14"
+ layout="topleft"
+ left="10"
+ halign="left"
+ name="advanced_options_label"
+ right="-10"
+ top="10">
+ ADVANCED OPTIONS
+ </text>
+ <view_border
+ bevel_style="in"
+ follows="left|top|right"
+ height="1"
+ left="10"
+ layout="topleft"
+ name="advanced_options_hr"
+ right="-10"
+ top_pad="5"
+ />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="13"
+ layout="topleft"
+ left="10"
+ name="layer_type_label"
+ top_pad="10"
+ width="50">
+ Capture:
+ </text>
+ <combo_box
+ follows="left|top|right"
+ height="23"
+ label="Image Layers"
+ layout="topleft"
+ left="30"
+ name="layer_types"
+ right="-10">
+ <combo_box.item
+ label="Colors"
+ name="Colors"
+ value="colors" />
+ <combo_box.item
+ label="Depth"
+ name="Depth"
+ value="depth" />
+ </combo_box>
+ <check_box
+ label="Interface"
+ layout="topleft"
+ left="30"
+ top_pad="10"
+ width="180"
+ name="ui_check" />
+ <check_box
+ label="HUDs"
+ layout="topleft"
+ left="30"
+ top_pad="10"
+ width="180"
+ name="hud_check" />
+ <check_box
+ label="Freeze frame (fullscreen)"
+ layout="topleft"
+ left="10"
+ top_pad="8"
+ width="180"
+ name="freeze_frame_check" />
+ <check_box
+ label="Auto-refresh"
+ layout="topleft"
+ left="10"
+ top_pad="8"
+ width="180"
+ name="auto_snapshot_check" />
+ </panel>
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_postcard.xml b/indra/newview/skins/default/xui/en/panel_postcard_message.xml
index adc2433105..e9f322f590 100644
--- a/indra/newview/skins/default/xui/en/floater_postcard.xml
+++ b/indra/newview/skins/default/xui/en/panel_postcard_message.xml
@@ -1,117 +1,87 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater
- legacy_header_height="18"
- can_minimize="false"
- can_resize="true"
+<panel
height="380"
layout="topleft"
- min_height="380"
- min_width="490"
- name="Postcard"
- help_topic="postcard"
- title="EMAIL SNAPSHOT"
+ name="panel_postcard_message"
width="490">
- <floater.string
- name="default_subject">
- Postcard from [SECOND_LIFE].
- </floater.string>
- <floater.string
- name="default_message">
- Check this out!
- </floater.string>
- <floater.string
- name="upload_message">
- Sending...
- </floater.string>
<text
type="string"
length="1"
bottom="35"
follows="top|left"
font="SansSerif"
+ height="16"
layout="topleft"
left="12"
- name="to_label">
- Recipient&apos;s Email:
+ name="to_label"
+ top="10"
+ width="60">
+ To:
</text>
<line_editor
control_name="LastPostcardRecipient"
- follows="left|top"
+ follows="left|top|right"
height="20"
layout="topleft"
- left_delta="148"
+ left_pad="10"
name="to_form"
- top_delta="-4"
- width="150" />
- <text
- type="string"
- length="1"
- bottom_delta="23"
- follows="top|left"
- font="SansSerif"
- layout="topleft"
- left="12"
- name="from_label">
- Your Email:
- </text>
- <line_editor
- follows="left|top"
- height="20"
- layout="topleft"
- left_delta="148"
- name="from_form"
- top_delta="-4"
- width="150" />
+ right="-10"
+ top_delta="-4" />
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
+ height="16"
layout="topleft"
left="12"
- name="name_label">
- Your Name:
+ name="name_label"
+ width="60">
+ From:
</text>
<line_editor
- follows="left|top"
+ follows="left|top|right"
height="20"
layout="topleft"
- left_delta="148"
+ left_pad="10"
max_length_bytes="100"
name="name_form"
- top_delta="-4"
- width="150" />
+ right="-10"
+ top_delta="-4" />
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
+ height="16"
layout="topleft"
left="12"
- name="subject_label">
+ name="subject_label"
+ width="60">
Subject:
</text>
<line_editor
- follows="left|top"
+ follows="left|top|right"
height="20"
label="Type your subject here."
layout="topleft"
- left_delta="148"
+ left_pad="10"
max_length_bytes="100"
name="subject_form"
- top_delta="-4"
- width="150" />
+ right="-10"
+ top_delta="-4" />
<text
type="string"
length="1"
bottom_delta="23"
- follows="top|left"
+ follows="top|left|right"
font="SansSerif"
layout="topleft"
left="12"
- name="msg_label">
+ name="msg_label"
+ right="-10">
Message:
</text>
<text_editor
@@ -123,9 +93,9 @@
left_delta="0"
max_length="700"
name="msg_form"
- word_wrap="true"
+ right="-10"
top_pad="10"
- width="420">
+ word_wrap="true">
Type your message here.
</text_editor>
<button
@@ -136,7 +106,10 @@
name="cancel_btn"
right="-10"
top="350"
- width="100" />
+ width="100">
+ <button.commit_callback
+ function="Postcard.Cancel" />
+ </button>
<button
follows="right|bottom"
height="23"
@@ -145,5 +118,8 @@
left_delta="-106"
name="send_btn"
top_delta="0"
- width="100" />
-</floater>
+ width="100">
+ <button.commit_callback
+ function="Postcard.Send" />
+ </button>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_postcard_settings.xml b/indra/newview/skins/default/xui/en/panel_postcard_settings.xml
new file mode 100644
index 0000000000..84e3593798
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_postcard_settings.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="380"
+ layout="topleft"
+ name="panel_postcard_settings"
+ width="490">
+ <combo_box
+ follows="left|top|right"
+ height="23"
+ label="Resolution"
+ layout="topleft"
+ left="10"
+ name="postcard_size_combo"
+ right="-10"
+ top_pad="10">
+ <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.item
+ label="Custom"
+ name="Custom"
+ value="[i-1,i-1]" />
+ </combo_box>
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Width"
+ label_width="40"
+ layout="topleft"
+ left="10"
+ max_val="6016"
+ min_val="32"
+ name="postcard_snapshot_width"
+ top_pad="10"
+ width="95" />
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Height"
+ label_width="40"
+ layout="topleft"
+ left_pad="5"
+ max_val="6016"
+ min_val="32"
+ name="postcard_snapshot_height"
+ top_delta="0"
+ width="95" />
+ <check_box
+ bottom_delta="20"
+ follows="left|top"
+ label="Constrain proportions"
+ layout="topleft"
+ left="10"
+ name="postcard_keep_aspect_check" />
+ <slider
+ decimal_digits="0"
+ follows="left|top"
+ height="15"
+ increment="1"
+ initial_value="75"
+ label="Image quality"
+ label_width="80"
+ layout="topleft"
+ left="10"
+ max_val="100"
+ name="image_quality_slider"
+ top_pad="7"
+ width="200" />
+ <text
+ type="string"
+ follows="left|top"
+ font="SansSerifSmall"
+ length="1"
+ height="14"
+ layout="topleft"
+ left_pad="-5"
+ halign="left"
+ name="image_quality_level"
+ top_delta="0"
+ width="60">
+ ([QLVL])
+ </text>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml
new file mode 100644
index 0000000000..7b148fa338
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="380"
+ layout="topleft"
+ name="panel_snapshot_inventory"
+ width="490">
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Snapshot_Inventory"
+ layout="topleft"
+ left="12"
+ mouse_opaque="true"
+ name="title_icon"
+ top="5"
+ width="18" />
+ <text
+ follows="top|left|right"
+ font="SansSerifBold"
+ height="20"
+ layout="topleft"
+ left_pad="12"
+ length="1"
+ name="title"
+ right="-10"
+ text_color="white"
+ type="string"
+ top_delta="5">
+ Save to My Inventory
+ </text>
+ <view_border
+ bevel_style="in"
+ follows="left|top|right"
+ height="1"
+ left="10"
+ layout="topleft"
+ name="hr"
+ right="-10"
+ top_pad="5"
+ />
+ <text
+ bottom="35"
+ follows="top|left|right"
+ font="SansSerif"
+ height="56"
+ layout="topleft"
+ left="12"
+ length="1"
+ name="hint_lbl"
+ top_pad="10"
+ type="string"
+ word_wrap="true">
+ Saving an image to your inventory costs L$[UPLOAD_COST]. To save your image as a texture select one of the square formats.
+ </text>
+ <combo_box
+ follows="top|left|right"
+ height="23"
+ label="Resolution"
+ layout="topleft"
+ left_delta="0"
+ name="texture_size_combo"
+ right="-10"
+ top_pad="10">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="Small (128x128)"
+ name="Small(128x128)"
+ value="[i128,i128]" />
+ <combo_box.item
+ label="Medium (256x256)"
+ name="Medium(256x256)"
+ value="[i256,i256]" />
+ <combo_box.item
+ label="Large (512x512)"
+ name="Large(512x512)"
+ value="[i512,i512]" />
+ <combo_box.item
+ label="Custom"
+ name="Custom"
+ value="[i-1,i-1]" />
+ </combo_box>
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Width"
+ label_width="40"
+ layout="topleft"
+ left="10"
+ max_val="6016"
+ min_val="32"
+ name="inventory_snapshot_width"
+ top_pad="10"
+ width="95" />
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Height"
+ label_width="40"
+ layout="topleft"
+ left_pad="5"
+ max_val="6016"
+ min_val="32"
+ name="inventory_snapshot_height"
+ top_delta="0"
+ width="95" />
+ <check_box
+ bottom_delta="20"
+ follows="left|top"
+ label="Constrain proportions"
+ layout="topleft"
+ left="10"
+ name="inventory_keep_aspect_check" />
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-10"
+ top="350"
+ width="100">
+ <button.commit_callback
+ function="Inventory.Cancel" />
+ </button>
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Save"
+ layout="topleft"
+ left_delta="-106"
+ name="save_btn"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="Inventory.Save" />
+ </button>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_local.xml b/indra/newview/skins/default/xui/en/panel_snapshot_local.xml
new file mode 100644
index 0000000000..4d6c4bcdfa
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_local.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="380"
+ layout="topleft"
+ name="panel_snapshot_local"
+ width="490">
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Snapshot_Download"
+ layout="topleft"
+ left="12"
+ mouse_opaque="true"
+ name="title_icon"
+ top="5"
+ width="18" />
+ <text
+ follows="top|left|right"
+ font="SansSerifBold"
+ height="20"
+ layout="topleft"
+ left_pad="12"
+ length="1"
+ name="title"
+ right="-10"
+ text_color="white"
+ type="string"
+ top_delta="4">
+ Save to My Computer
+ </text>
+ <view_border
+ bevel_style="in"
+ follows="left|top|right"
+ height="1"
+ left="10"
+ layout="topleft"
+ name="hr"
+ right="-10"
+ top_pad="5"
+ />
+ <combo_box
+ follows="left|top|right"
+ height="23"
+ label="Resolution"
+ layout="topleft"
+ left_delta="0"
+ name="local_size_combo"
+ right="-10"
+ top_pad="10">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="320x240"
+ name="320x240"
+ value="[i320,i240]" />
+ <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.item
+ label="1280x1024"
+ name="1280x1024"
+ value="[i1280,i1024]" />
+ <combo_box.item
+ label="1600x1200"
+ name="1600x1200"
+ value="[i1600,i1200]" />
+ <combo_box.item
+ label="Custom"
+ name="Custom"
+ value="[i-1,i-1]" />
+ </combo_box>
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Width"
+ label_width="40"
+ layout="topleft"
+ left="10"
+ max_val="6016"
+ min_val="32"
+ name="local_snapshot_width"
+ top_pad="10"
+ width="95" />
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Height"
+ label_width="40"
+ layout="topleft"
+ left_pad="5"
+ max_val="6016"
+ min_val="32"
+ name="local_snapshot_height"
+ top_delta="0"
+ width="95" />
+ <check_box
+ bottom_delta="20"
+ follows="left|top"
+ label="Constrain proportions"
+ layout="topleft"
+ left="10"
+ name="local_keep_aspect_check" />
+ <combo_box
+ follows="left|top"
+ height="23"
+ label="Format"
+ layout="topleft"
+ left_delta="0"
+ name="local_format_combo"
+ top_pad="10"
+ width="120">
+ <combo_box.item
+ label="PNG (Lossless)"
+ name="PNG"
+ value="PNG" />
+ <combo_box.item
+ label="JPEG"
+ name="JPEG"
+ value="JPEG" />
+ <combo_box.item
+ label="BMP (Lossless)"
+ name="BMP"
+ value="BMP" />
+ </combo_box>
+ <slider
+ decimal_digits="0"
+ follows="left|top"
+ height="15"
+ increment="1"
+ initial_value="75"
+ label="Image quality"
+ label_width="80"
+ layout="topleft"
+ left="10"
+ max_val="100"
+ name="image_quality_slider"
+ top_pad="7"
+ width="200" />
+ <text
+ type="string"
+ follows="left|top"
+ font="SansSerifSmall"
+ length="1"
+ height="14"
+ layout="topleft"
+ left_pad="-5"
+ halign="left"
+ name="image_quality_level"
+ top_delta="0"
+ width="60">
+ ([QLVL])
+ </text>
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-10"
+ top="350"
+ width="100">
+ <button.commit_callback
+ function="Local.Cancel" />
+ </button>
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Save"
+ layout="topleft"
+ left_delta="-106"
+ name="save_btn"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="Local.Save" />
+ </button>
+</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
new file mode 100644
index 0000000000..792f6dbec8
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="all"
+ height="240"
+ layout="topleft"
+ name="panel_snapshot_options"
+ width="490">
+ <button
+ follows="left|top|right"
+ font="SansSerif"
+ halign="left"
+ height="38"
+ image_overlay="Snapshot_Profile"
+ image_overlay_alignment="left"
+ image_top_pad="-2"
+ imgoverlay_label_space="10"
+ label="Post to My Profile Feed"
+ layout="topleft"
+ left="10"
+ name="save_to_profile_btn"
+ pad_left="10"
+ right="-10"
+ top="5">
+ <button.commit_callback
+ function="Snapshot.SaveToProfile" />
+ </button>
+ <button
+ follows="left|top|right"
+ font="SansSerif"
+ halign="left"
+ height="38"
+ image_overlay="Snapshot_Email"
+ image_overlay_alignment="left"
+ image_top_pad="-2"
+ imgoverlay_label_space="10"
+ label="Email"
+ layout="topleft"
+ left_delta="0"
+ name="save_to_email_btn"
+ pad_left="10"
+ right="-10"
+ top_pad="10">
+ <button.commit_callback
+ function="Snapshot.SaveToEmail" />
+ </button>
+ <button
+ follows="left|top|right"
+ font="SansSerif"
+ halign="left"
+ height="38"
+ image_overlay="Snapshot_Inventory"
+ image_overlay_alignment="left"
+ image_top_pad="-2"
+ imgoverlay_label_space="10"
+ label="Save to My Inventory (L$[AMOUNT])"
+ layout="topleft"
+ left_delta="0"
+ name="save_to_inventory_btn"
+ pad_left="10"
+ right="-10"
+ top_pad="10">
+ <button.commit_callback
+ function="Snapshot.SaveToInventory" />
+ </button>
+ <button
+ follows="left|top|right"
+ font="SansSerif"
+ halign="left"
+ height="38"
+ image_overlay="Snapshot_Download"
+ image_overlay_alignment="left"
+ image_top_pad="-2"
+ imgoverlay_label_space="10"
+ label="Save to My Computer"
+ layout="topleft"
+ left_delta="0"
+ name="save_to_computer_btn"
+ pad_left="10"
+ right="-10"
+ top_pad="10">
+ <button.commit_callback
+ function="Snapshot.SaveToComputer" />
+ </button>
+ <panel
+ background_visible="true"
+ bg_alpha_color="0.9 1 0.9 1"
+ bottom="-10"
+ follows="left|bottom|right"
+ font="SansSerifLarge"
+ halign="center"
+ height="20"
+ layout="topleft"
+ left_delta="0"
+ length="1"
+ name="succeeded_panel"
+ right="-10"
+ type="string"
+ visible="false">
+ <text
+ follows="all"
+ font="SansSerif"
+ halign="center"
+ height="18"
+ layout="topleft"
+ left="1"
+ length="1"
+ name="succeeded_lbl"
+ right="-1"
+ text_color="0.2 0.5 0.2 1"
+ top="4"
+ translate="false"
+ type="string">
+ Succeeded
+ </text>
+ </panel>
+ <panel
+ background_visible="true"
+ bg_alpha_color="1 0.9 0.9 1"
+ bottom="-10"
+ follows="left|bottom|right"
+ font="SansSerifLarge"
+ halign="center"
+ height="20"
+ layout="topleft"
+ left_delta="0"
+ length="1"
+ name="failed_panel"
+ right="-10"
+ type="string"
+ visible="false">
+ <text
+ follows="all"
+ font="SansSerif"
+ halign="center"
+ height="18"
+ layout="topleft"
+ left="1"
+ length="1"
+ name="failed_lbl"
+ right="-1"
+ text_color="0.5 0.2 0.2 1"
+ top="4"
+ translate="false"
+ type="string">
+ Failed
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_postcard.xml b/indra/newview/skins/default/xui/en/panel_snapshot_postcard.xml
new file mode 100644
index 0000000000..d8ff043444
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_postcard.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="380"
+ layout="topleft"
+ name="panel_snapshot_postcard"
+ width="490">
+ <string
+ name="default_subject">
+ Postcard from [SECOND_LIFE].
+ </string>
+ <string
+ name="default_message">
+ Check this out!
+ </string>
+ <string
+ name="upload_message">
+ Sending...
+ </string>
+ <string
+ name="default_subject">
+ Postcard from [SECOND_LIFE].
+ </string>
+ <string
+ name="default_message">
+ Check this out!
+ </string>
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Snapshot_Email"
+ layout="topleft"
+ left="12"
+ mouse_opaque="true"
+ name="title_icon"
+ top="5"
+ width="18" />
+ <text
+ follows="top|left|right"
+ font="SansSerifBold"
+ height="20"
+ layout="topleft"
+ left_pad="12"
+ length="1"
+ name="title"
+ right="-10"
+ text_color="white"
+ type="string"
+ top_delta="3">
+ Email
+ </text>
+ <button
+ follows="right|top"
+ height="23"
+ is_toggle="true"
+ label="Message"
+ layout="topleft"
+ name="message_btn"
+ right="-82"
+ top_delta="-7"
+ width="70">
+ <button.commit_callback
+ function="Postcard.Message" />
+ </button>
+ <button
+ follows="right|top"
+ height="23"
+ is_toggle="true"
+ label="Settings"
+ layout="topleft"
+ name="settings_btn"
+ top_delta="0"
+ right="-10"
+ width="70">
+ <button.commit_callback
+ function="Postcard.Settings" />
+ </button>
+ <view_border
+ bevel_style="in"
+ follows="left|top|right"
+ height="1"
+ left="10"
+ layout="topleft"
+ name="hr"
+ right="-10"
+ top_pad="5"
+ />
+ <panel_container
+ follows="all"
+ height="340"
+ layout="topleft"
+ left="0"
+ name="postcard_panel_container"
+ default_panel_name="panel_postcard_message"
+ top_pad="10"
+ width="490">
+ <panel
+ follows="all"
+ layout="topleft"
+ name="panel_postcard_message"
+ filename="panel_postcard_message.xml" />
+ <panel
+ follows="all"
+ layout="topleft"
+ name="panel_postcard_settings"
+ filename="panel_postcard_settings.xml" />
+ </panel_container>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml b/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml
new file mode 100644
index 0000000000..0760a33f82
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="380"
+ layout="topleft"
+ name="panel_snapshot_profile"
+ width="490">
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Snapshot_Profile"
+ layout="topleft"
+ left="12"
+ mouse_opaque="true"
+ name="title_icon"
+ top="5"
+ width="18" />
+ <text
+ follows="top|left|right"
+ font="SansSerifBold"
+ height="20"
+ layout="topleft"
+ left_pad="12"
+ length="1"
+ name="title"
+ right="-10"
+ text_color="white"
+ type="string"
+ top_delta="4">
+ Post to My Profile Feed
+ </text>
+ <view_border
+ bevel_style="in"
+ follows="left|top|right"
+ height="1"
+ left="10"
+ layout="topleft"
+ name="hr"
+ right="-10"
+ top_pad="5"
+ />
+ <combo_box
+ follows="left|top"
+ height="23"
+ label="Resolution"
+ layout="topleft"
+ left_delta="0"
+ name="profile_size_combo"
+ top_pad="10"
+ width="250">
+ <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.item
+ label="Custom"
+ name="Custom"
+ value="[i-1,i-1]" />
+ </combo_box>
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Width"
+ label_width="40"
+ layout="topleft"
+ left="10"
+ max_val="6016"
+ min_val="32"
+ name="profile_snapshot_width"
+ top_pad="10"
+ width="95" />
+ <spinner
+ allow_text_entry="false"
+ decimal_digits="0"
+ follows="left|top"
+ height="20"
+ increment="32"
+ label="Height"
+ label_width="40"
+ layout="topleft"
+ left_pad="5"
+ max_val="6016"
+ min_val="32"
+ name="profile_snapshot_height"
+ top_delta="0"
+ width="95" />
+ <check_box
+ bottom_delta="20"
+ label="Constrain proportions"
+ layout="topleft"
+ left="10"
+ name="profile_keep_aspect_check" />
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ layout="topleft"
+ left="12"
+ name="caption_label"
+ right="-10"
+ top_pad="10"
+ type="string">
+ Caption:
+ </text>
+ <text_editor
+ follows="all"
+ height="170"
+ layout="topleft"
+ left_delta="0"
+ length="1"
+ max_length="700"
+ name="caption"
+ right="-10"
+ top_pad="5"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|bottom"
+ initial_value="true"
+ label="Include location"
+ layout="topleft"
+ left_delta="0"
+ name="add_location_cb"
+ top_pad="15" />
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-10"
+ top="350"
+ width="100">
+ <button.commit_callback
+ function="PostToProfile.Cancel" />
+ </button>
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Post"
+ layout="topleft"
+ left_delta="-106"
+ name="post_btn"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="PostToProfile.Send" />
+ </button>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index ec230773cc..befcc5dd87 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3724,4 +3724,12 @@ Try enclosing path to the editor with double quotes.
<string name="Wrap">Wrap</string>
<string name="Preview">Preview</string>
<string name="Normal">Normal</string>
+
+ <!-- Snapshot image quality levels -->
+ <string name="snapshot_quality_very_low">Very Low</string>
+ <string name="snapshot_quality_low">Low</string>
+ <string name="snapshot_quality_medium">Medium</string>
+ <string name="snapshot_quality_high">High</string>
+ <string name="snapshot_quality_very_high">Very High</string>
+
</strings>