diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index bef775cdb852056928da2df8ee9800a7822900c0..ff9cf3199edaa7845b8b528e592a53d86c5d9aa1 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 5c0ea2f7744ea28eb2c3e4c2b2ad6710ec52a3c9..55e28cd60eb0cb2f3571e25e6d78554f5222e18e 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 966f5b941e0adfcbbd22b074eb420223dc1b23e8..40a4d665f8e6fff60cab8d4ba618f541eb14463b 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 3bcbb987f750eec897a7aa6145fea62790452e45..0000000000000000000000000000000000000000
--- 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 472592154fe6ea52e8ff6be686f05a810ba17e04..0000000000000000000000000000000000000000
--- 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 8105844b0df391d549e267836d622a2e3714fa93..48e6cca6236f88fe62bee74d4af030e64c0efea7 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 @@ class LLSnapshotLivePreview : public LLView
 	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 @@ class LLSnapshotLivePreview : public LLView
 	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 @@ class LLFloaterSnapshot::Impl
 		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 @@ class LLFloaterSnapshot::Impl
 	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) ;
@@ -1687,20 +1635,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)
 {
@@ -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 c92d9efde507bcaa29662c5c232125c8903905cf..2c79c749d60809311dc114e99d61d7bb3c11fbc7 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 @@ class LLFloaterSnapshot : public LLFloater
 	/*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 0000000000000000000000000000000000000000..fdae521ac52bc9d3388cb646dce2be7af52f81a8
--- /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 0000000000000000000000000000000000000000..a49782a3e0404b2649ad3bc4e5107348403c4aee
--- /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 0000000000000000000000000000000000000000..63ccbc1b029fe1f3196bd716d2af1c073ce93072
--- /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 0000000000000000000000000000000000000000..eaa27b8d41065a872001d79baa36c2ffecf9bd80
--- /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 0000000000000000000000000000000000000000..df904b68365ee6f959d43a2fcdbf8cb1dc6021c5
--- /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 0000000000000000000000000000000000000000..6867c7af4ef7c6f13b9530249e3fccebbffb4106
--- /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 0000000000000000000000000000000000000000..89245fc804e7c25b4e8737dfe56793c34f29c48c
--- /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 0000000000000000000000000000000000000000..4f2d6da7e593be9494fb66b07e9139358e386b0e
--- /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 0000000000000000000000000000000000000000..0eb118b90625e757468f7162d79f4767dd9a7190
--- /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 95a12c7c233e738c4e9167a731eeb8b35495679d..e340333c2c46694eb0ea6f96b59b5df5c3ad9260 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 14269b002be2cb3f0bc43283a26a5ed873e4a92e..93a85ed3745589e360e14392f656b8663d775e98 100644
--- a/indra/newview/llsidetraypanelcontainer.h
+++ b/indra/newview/llsidetraypanelcontainer.h
@@ -56,6 +56,11 @@ class LLSideTrayPanelContainer : public LLTabContainer
 	*/
 	/*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.
 	*/
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c761969fcfaab2b49986ac5e8e1c966f2c07a84c..74c4f6d2dc69f52831936e7505b00fe11e927f69 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 41b4dc01e8dc0f252a35d6d00123d5b57baf769f..5afd481ddabcc0b87eb39a923c9b8f20b7f852c7 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 @@ LOG_CLASS(LLViewerMediaWebProfileResponder);
 		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 b9293b3b3105f8c9a1ebaef7e2752b97df21728a..7e830e14bf2b0d8ea5d4b9ea10003dc58e334478 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 a9ca70fd26f8fe731db1066c25cf82fcca1f7330..7cae19a1d21fae7ad03dcd71a9b2a04cf70df910 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 6fcbc401af1bd4700fedbbd02ffeaa951f269259..c20bc5f02f3437c17570695b11506a1c0814dceb 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 d10b06f12117cb69d16df825c2a9512e4281e5fb..0cb7f82b58572f013b523ecec5e0945f704bc8e9 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -324,7 +324,7 @@ class LLViewerWindow : public LLWindowCallbacks
 	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 0000000000000000000000000000000000000000..641f338f2c6be343f352a010a6d0ca3849310dbd
--- /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 0000000000000000000000000000000000000000..10279bffaccfe3c225c59bf81de225d39f0101c2
--- /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 0000000000000000000000000000000000000000..6aa1abded50dae6e6cedd96c0d4518bb81c2fe98
Binary files /dev/null and b/indra/newview/skins/default/textures/snapshot_download.png 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 0000000000000000000000000000000000000000..dee784a9bfb2de38528f8b9992f13266f6087c98
Binary files /dev/null and b/indra/newview/skins/default/textures/snapshot_email.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index bb91d32c6cb9b59eac89bbe4b5c47fb695947d3f..bcdd73aeb89ad3f4818e7bff3a119cf849889a27 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 91b4ed6954b615d4ba059924c95043ffa48ddbd0..d7a1510c1c0c8cb5db1b1bf681cca1747f9fda30 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
similarity index 55%
rename from indra/newview/skins/default/xui/en/floater_postcard.xml
rename to indra/newview/skins/default/xui/en/panel_postcard_message.xml
index adc2433105c0aeaa9c9e2725adf139f9bb9aced2..e9f322f590f14cf05596c718eb2413cb504fb787 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 0000000000000000000000000000000000000000..84e35937980539b914565a58a0d6c74d43913514
--- /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 0000000000000000000000000000000000000000..7b148fa338f084ad6f3d447f059fae4916935bb5
--- /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 0000000000000000000000000000000000000000..4d6c4bcdfaccc741885eda31d4200a1eff51ea16
--- /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 0000000000000000000000000000000000000000..792f6dbec8f032cd454a481b81cb55bf97ed61af
--- /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 0000000000000000000000000000000000000000..d8ff043444b4caf4e4ea2485ab4aeb2789a6360e
--- /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 0000000000000000000000000000000000000000..0760a33f82e0a49866e556eeb8b3c02fc786273a
--- /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 ec230773cc5a37074553c504dc32bccbd0892321..befcc5dd87a40cb9b411b4ef8151bb2ce448de79 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>